blob: fb99d92eceb8eb989649138e20ecab69470e739f [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 Fujitaeec4c982022-09-16 17:27:20 +090029// - v2.6.2 Fix out-of-bounds access of accessors. PR#379.
Syoyo Fujita4581d372022-09-06 22:02:31 +090030// - v2.6.1 Better GLB validation check when loading.
Syoyo Fujita3bddc092022-08-19 18:21:24 +090031// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
32// Disable expanding file path for security(no use of awkward `wordexp` anymore).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +090033// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +090034// - v2.4.3 Fix null object output when when material has all default
35// parameters.
Syoyo Fujitac4166e42020-01-08 02:38:01 +090036// - v2.4.2 Decode percent-encoded URI.
Syoyo Fujita6e08b172019-10-30 17:25:38 +090037// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
38// `extras` property.
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +090039// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
Syoyo Fujitaee179b22019-08-16 13:11:30 +090040// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
Syoyo Fujita046400b2019-07-24 19:26:48 +090041// - v2.3.0 Modified Material representation according to glTF 2.0 schema
42// (and introduced TextureInfo class)
Syoyo Fujita150f2432019-07-25 19:22:44 +090043// Change the behavior of `Value::IsNumber`. It return true either the
44// value is int or real.
Syoyo Fujitaca56f722019-03-07 21:04:25 +090045// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
46// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090047// - v2.1.0 Add draco compression.
Syoyo Fujita0820d832018-10-04 15:45:13 +090048// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090049// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090050//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090051// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090052//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090053// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090054// - base64: base64 decode/encode library.
55// - stb_image: Image loading library.
56//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090057#ifndef TINY_GLTF_H_
58#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090059
Syoyo Fujitad42767e2018-03-15 21:52:00 -050060#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090061#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090062#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050063#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020064#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090065#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090066#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090067#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090068#include <string>
69#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090070
zbendefy69eeea12022-09-05 23:54:57 +020071//Auto-detect C++14 standard version
72#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
73#define TINYGLTF_USE_CPP14
74#endif
75
jrkoonce51453942019-09-03 09:48:30 -050076#ifndef TINYGLTF_USE_CPP14
77#include <functional>
78#endif
79
Sascha Willems5f9cb242018-12-28 20:53:41 +010080#ifdef __ANDROID__
81#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
82#include <android/asset_manager.h>
83#endif
84#endif
85
Selmar Kok79e3df22019-10-29 16:22:07 +010086#ifdef __GNUC__
87#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010088#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010089#else
90#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010091#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010092#else
93#define TINYGLTF_NOEXCEPT noexcept
94#endif
95
Syoyo Fujita6e08b172019-10-30 17:25:38 +090096#define DEFAULT_METHODS(x) \
97 ~x() = default; \
98 x(const x &) = default; \
99 x(x &&) TINYGLTF_NOEXCEPT = default; \
100 x &operator=(const x &) = default; \
101 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100102
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900103namespace tinygltf {
104
105#define TINYGLTF_MODE_POINTS (0)
106#define TINYGLTF_MODE_LINE (1)
107#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +0100108#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900109#define TINYGLTF_MODE_TRIANGLES (4)
110#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
111#define TINYGLTF_MODE_TRIANGLE_FAN (6)
112
113#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
114#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
115#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
116#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
117#define TINYGLTF_COMPONENT_TYPE_INT (5124)
118#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
119#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900120#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
121 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
122 // support double type even the schema seems allow any value of
123 // integer:
124 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900125
Syoyo Fujitac2615632016-06-19 21:56:06 +0900126#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
127#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
128#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
129#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
130#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
131#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
132
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100133#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900134#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400135#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900136
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400137// Redeclarations of the above for technique.parameters.
138#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
139#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
140#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
141#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
142#define TINYGLTF_PARAMETER_TYPE_INT (5124)
143#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
144#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
145
146#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
147#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
148#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
149
150#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
151#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
152#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
153
154#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
155#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
156#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
157#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
158
159#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
160#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
161#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
162
163#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
164
165// End parameter types
166
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900167#define TINYGLTF_TYPE_VEC2 (2)
168#define TINYGLTF_TYPE_VEC3 (3)
169#define TINYGLTF_TYPE_VEC4 (4)
170#define TINYGLTF_TYPE_MAT2 (32 + 2)
171#define TINYGLTF_TYPE_MAT3 (32 + 3)
172#define TINYGLTF_TYPE_MAT4 (32 + 4)
173#define TINYGLTF_TYPE_SCALAR (64 + 1)
174#define TINYGLTF_TYPE_VECTOR (64 + 4)
175#define TINYGLTF_TYPE_MATRIX (64 + 16)
176
177#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
178#define TINYGLTF_IMAGE_FORMAT_PNG (1)
179#define TINYGLTF_IMAGE_FORMAT_BMP (2)
180#define TINYGLTF_IMAGE_FORMAT_GIF (3)
181
Luke San Antonio6d616f52016-06-23 14:09:23 -0400182#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
183#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900184#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400185#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
186#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
187
Syoyo Fujitabde70212016-02-07 17:38:17 +0900188#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
189#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
190
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900191#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
192#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
193
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400194#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
195#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
196
Selmar Kok31cb7f92018-10-03 15:39:05 +0200197#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900198#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200199
Sascha Willems5f9cb242018-12-28 20:53:41 +0100200#ifdef __ANDROID__
201#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000202AAssetManager *asset_manager = nullptr;
Sascha Willems5f9cb242018-12-28 20:53:41 +0100203#endif
204#endif
205
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900206typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100207 NULL_TYPE,
208 REAL_TYPE,
209 INT_TYPE,
210 BOOL_TYPE,
211 STRING_TYPE,
212 ARRAY_TYPE,
213 BINARY_TYPE,
214 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900215} Type;
216
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500217static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900218 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
219 return 1;
220 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
221 return 1;
222 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
223 return 2;
224 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
225 return 2;
226 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
227 return 4;
228 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
229 return 4;
230 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
231 return 4;
232 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
233 return 8;
234 } else {
235 // Unknown componenty type
236 return -1;
237 }
238}
239
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400240static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900241 if (ty == TINYGLTF_TYPE_SCALAR) {
242 return 1;
243 } else if (ty == TINYGLTF_TYPE_VEC2) {
244 return 2;
245 } else if (ty == TINYGLTF_TYPE_VEC3) {
246 return 3;
247 } else if (ty == TINYGLTF_TYPE_VEC4) {
248 return 4;
249 } else if (ty == TINYGLTF_TYPE_MAT2) {
250 return 4;
251 } else if (ty == TINYGLTF_TYPE_MAT3) {
252 return 9;
253 } else if (ty == TINYGLTF_TYPE_MAT4) {
254 return 16;
255 } else {
256 // Unknown componenty type
257 return -1;
258 }
259}
260
Syoyo Fujita150f2432019-07-25 19:22:44 +0900261// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200262bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900263bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
264 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200265
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900266#ifdef __clang__
267#pragma clang diagnostic push
268// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900269#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900270#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900271#endif
272
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900273// Simple class to represent JSON object
274class Value {
275 public:
276 typedef std::vector<Value> Array;
277 typedef std::map<std::string, Value> Object;
278
Syoyo Fujita046400b2019-07-24 19:26:48 +0900279 Value()
280 : type_(NULL_TYPE),
281 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900282 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900283 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900284
285 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900286 explicit Value(int i) : type_(INT_TYPE) {
287 int_value_ = i;
288 real_value_ = i;
289 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900290 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900291 explicit Value(const std::string &s) : type_(STRING_TYPE) {
292 string_value_ = s;
293 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900294 explicit Value(std::string &&s)
295 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900296 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900297 binary_value_.resize(n);
298 memcpy(binary_value_.data(), p, n);
299 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900300 explicit Value(std::vector<unsigned char> &&v) noexcept
301 : type_(BINARY_TYPE),
302 binary_value_(std::move(v)) {}
303 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
304 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
305 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500306
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900307 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
308 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
309 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100310
311 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900312
Hill Mad1e32862021-02-20 22:30:44 -0800313 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900314
315 bool IsBool() const { return (type_ == BOOL_TYPE); }
316
317 bool IsInt() const { return (type_ == INT_TYPE); }
318
Syoyo Fujita150f2432019-07-25 19:22:44 +0900319 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900320
Syoyo Fujita150f2432019-07-25 19:22:44 +0900321 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900322
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900323 bool IsString() const { return (type_ == STRING_TYPE); }
324
325 bool IsBinary() const { return (type_ == BINARY_TYPE); }
326
327 bool IsArray() const { return (type_ == ARRAY_TYPE); }
328
329 bool IsObject() const { return (type_ == OBJECT_TYPE); }
330
Syoyo Fujita150f2432019-07-25 19:22:44 +0900331 // Use this function if you want to have number value as double.
332 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900333 if (type_ == INT_TYPE) {
334 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900335 } else {
336 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900337 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900338 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900339
Syoyo Fujita150f2432019-07-25 19:22:44 +0900340 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900341 // TODO(syoyo): Support int value larger than 32 bits
342 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900343 if (type_ == REAL_TYPE) {
344 return int(real_value_);
345 } else {
346 return int_value_;
347 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900348 }
349
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900350 // Accessor
351 template <typename T>
352 const T &Get() const;
353 template <typename T>
354 T &Get();
355
356 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900357 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900358 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900359 assert(IsArray());
360 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900361 return (static_cast<size_t>(idx) < array_value_.size())
362 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900363 : null_value;
364 }
365
366 // Lookup value from a key-value pair
367 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900368 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900369 assert(IsObject());
370 Object::const_iterator it = object_value_.find(key);
371 return (it != object_value_.end()) ? it->second : null_value;
372 }
373
374 size_t ArrayLen() const {
375 if (!IsArray()) return 0;
376 return array_value_.size();
377 }
378
379 // Valid only for object type.
380 bool Has(const std::string &key) const {
381 if (!IsObject()) return false;
382 Object::const_iterator it = object_value_.find(key);
383 return (it != object_value_.end()) ? true : false;
384 }
385
386 // List keys
387 std::vector<std::string> Keys() const {
388 std::vector<std::string> keys;
389 if (!IsObject()) return keys; // empty
390
391 for (Object::const_iterator it = object_value_.begin();
392 it != object_value_.end(); ++it) {
393 keys.push_back(it->first);
394 }
395
396 return keys;
397 }
398
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900399 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900400
401 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000402
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900403 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900404 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900405
Syoyo Fujita046400b2019-07-24 19:26:48 +0900406 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900407 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900408 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900409 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900410 Array array_value_;
411 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900412 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900413};
414
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900415#ifdef __clang__
416#pragma clang diagnostic pop
417#endif
418
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900419#define TINYGLTF_VALUE_GET(ctype, var) \
420 template <> \
421 inline const ctype &Value::Get<ctype>() const { \
422 return var; \
423 } \
424 template <> \
425 inline ctype &Value::Get<ctype>() { \
426 return var; \
427 }
428TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900429TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900430TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900431TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900432TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900433TINYGLTF_VALUE_GET(Value::Array, array_value_)
434TINYGLTF_VALUE_GET(Value::Object, object_value_)
435#undef TINYGLTF_VALUE_GET
436
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900437#ifdef __clang__
438#pragma clang diagnostic push
439#pragma clang diagnostic ignored "-Wc++98-compat"
440#pragma clang diagnostic ignored "-Wpadded"
441#endif
442
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500443/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100444using ColorValue = std::array<double, 4>;
445
Syoyo Fujita046400b2019-07-24 19:26:48 +0900446// === legacy interface ====
447// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500448struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200449 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700450 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900451 std::string string_value;
452 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000453 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200454 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900455
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500456 // context sensitive methods. depending the type of the Parameter you are
457 // accessing, these are either valid or not
458 // If this parameter represent a texture map in a material, will return the
459 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100460
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500461 /// Return the index of a texture if this Parameter is a texture map.
462 /// Returned value is only valid if the parameter represent a texture from a
463 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100464 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100465 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500466 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100467 return int(it->second);
468 }
469 return -1;
470 }
471
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000472 /// Return the index of a texture coordinate set if this Parameter is a
473 /// texture map. Returned value is only valid if the parameter represent a
474 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100475 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000476 const auto it = json_double_value.find("texCoord");
477 if (it != std::end(json_double_value)) {
478 return int(it->second);
479 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200480 // As per the spec, if texCoord is ommited, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000481 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100482 }
483
Christophe820ede82019-07-04 15:21:21 +0900484 /// Return the scale of a texture if this Parameter is a normal texture map.
485 /// Returned value is only valid if the parameter represent a normal texture
486 /// from a material
487 double TextureScale() const {
488 const auto it = json_double_value.find("scale");
489 if (it != std::end(json_double_value)) {
490 return it->second;
491 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200492 // As per the spec, if scale is ommited, this paramter is 1
493 return 1;
494 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200495
Arthur Brainville8a98d982019-07-05 00:26:02 +0200496 /// Return the strength of a texture if this Parameter is a an occlusion map.
497 /// Returned value is only valid if the parameter represent an occlusion map
498 /// from a material
499 double TextureStrength() const {
500 const auto it = json_double_value.find("strength");
501 if (it != std::end(json_double_value)) {
502 return it->second;
503 }
504 // As per the spec, if strenghth is ommited, this parameter is 1
505 return 1;
Christophe820ede82019-07-04 15:21:21 +0900506 }
507
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500508 /// Material factor, like the roughness or metalness of a material
509 /// Returned value is only valid if the parameter represent a texture from a
510 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700511 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100512
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500513 /// Return the color of a material
514 /// Returned value is only valid if the parameter represent a texture from a
515 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100516 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100517 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500518 {// this agregate intialize the std::array object, and uses C++11 RVO.
519 number_array[0], number_array[1], number_array[2],
520 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100521 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200522
Selmar Kokff2b1f92019-10-21 17:58:09 +0200523 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100524 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900525 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100526};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900527
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900528#ifdef __clang__
529#pragma clang diagnostic pop
530#endif
531
532#ifdef __clang__
533#pragma clang diagnostic push
534#pragma clang diagnostic ignored "-Wpadded"
535#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900536
Syoyo Fujitabde70212016-02-07 17:38:17 +0900537typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200538typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900539
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000540struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900541 int sampler; // required
542 int target_node; // required (index of the node to target)
543 std::string target_path; // required in ["translation", "rotation", "scale",
544 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900545 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200546 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100547 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900548
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900549 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
550 std::string extras_json_string;
551 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100552 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900553
Syoyo Fujita5b407452017-06-04 17:42:41 +0900554 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100555 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900556 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000557};
558
559struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900560 int input; // required
561 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200562 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
563 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200564 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900565 ExtensionMap extensions;
566
567 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
568 std::string extras_json_string;
569 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000570
Syoyo Fujita5b407452017-06-04 17:42:41 +0900571 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100572 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900573 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000574};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900575
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900576struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900577 std::string name;
578 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000579 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900580 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200581 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200582
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900583 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
584 std::string extras_json_string;
585 std::string extensions_json_string;
586
Selmar Kokff2b1f92019-10-21 17:58:09 +0200587 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100588 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900589 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900590};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900591
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000592struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900593 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900594 int inverseBindMatrices; // required here but not in the spec
595 int skeleton; // The index of the node used as a skeleton root
596 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000597
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900598 Value extras;
599 ExtensionMap extensions;
600
601 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
602 std::string extras_json_string;
603 std::string extensions_json_string;
604
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900605 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000606 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000607 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000608 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100609 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900610 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000611};
612
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000613struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900614 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900615 // glTF 2.0 spec does not define default value for `minFilter` and
616 // `magFilter`. Set -1 in TinyGLTF(issue #186)
617 int minFilter =
618 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800619 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900620 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
621 int magFilter =
622 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
623 int wrapS =
624 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
625 // "REPEAT"], default "REPEAT"
626 int wrapT =
627 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
628 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900629 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
630 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900631
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900632 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900633 ExtensionMap extensions;
634
635 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
636 std::string extras_json_string;
637 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900638
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000639 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900640 : minFilter(-1),
641 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800642 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900643 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100644 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900645 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000646};
647
Syoyo Fujita5b407452017-06-04 17:42:41 +0900648struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900649 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900650 int width;
651 int height;
652 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000653 int bits; // bit depth per channel. 8(byte), 16 or 32.
654 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
655 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900656 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900657 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500658 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
659 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900660 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
661 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900662 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900663 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900664
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900665 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
666 std::string extras_json_string;
667 std::string extensions_json_string;
668
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900669 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
670 // compressed for "image/jpeg" mime) This feature is good if you use custom
671 // image loader function. (e.g. delayed decoding of images for faster glTF
672 // parsing) Default parser for Image does not provide as-is loading feature at
673 // the moment. (You can manipulate this by providing your own LoadImageData
674 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200675 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900676
677 Image() : as_is(false) {
678 bufferView = -1;
679 width = -1;
680 height = -1;
681 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900682 bits = -1;
683 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900684 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100685 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500686
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900687 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000688};
689
690struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200691 std::string name;
692
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000693 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200694 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900695 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200696 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900697
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900698 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
699 std::string extras_json_string;
700 std::string extensions_json_string;
701
Syoyo Fujita5b407452017-06-04 17:42:41 +0900702 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100703 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500704
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900705 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000706};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900707
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900708struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900709 int index = -1; // required.
710 int texCoord; // The set index of texture's TEXCOORD attribute used for
711 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900712
713 Value extras;
714 ExtensionMap extensions;
715
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900716 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
717 std::string extras_json_string;
718 std::string extensions_json_string;
719
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900720 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100721 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900722 bool operator==(const TextureInfo &) const;
723};
724
725struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900726 int index = -1; // required
727 int texCoord; // The set index of texture's TEXCOORD attribute used for
728 // texture coordinate mapping.
729 double scale; // scaledNormal = normalize((<sampled normal texture value>
730 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900731
732 Value extras;
733 ExtensionMap extensions;
734
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900735 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
736 std::string extras_json_string;
737 std::string extensions_json_string;
738
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900739 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100740 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900741 bool operator==(const NormalTextureInfo &) const;
742};
743
744struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900745 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900746 int texCoord; // The set index of texture's TEXCOORD attribute used for
747 // texture coordinate mapping.
748 double strength; // occludedColor = lerp(color, color * <sampled occlusion
749 // texture value>, <occlusion strength>)
750
751 Value extras;
752 ExtensionMap extensions;
753
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900754 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
755 std::string extras_json_string;
756 std::string extensions_json_string;
757
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900758 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100759 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900760 bool operator==(const OcclusionTextureInfo &) const;
761};
762
763// pbrMetallicRoughness class defined in glTF 2.0 spec.
764struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900765 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900766 TextureInfo baseColorTexture;
767 double metallicFactor; // default 1
768 double roughnessFactor; // default 1
769 TextureInfo metallicRoughnessTexture;
770
771 Value extras;
772 ExtensionMap extensions;
773
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900774 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
775 std::string extras_json_string;
776 std::string extensions_json_string;
777
778 PbrMetallicRoughness()
779 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
780 metallicFactor(1.0),
781 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100782 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900783 bool operator==(const PbrMetallicRoughness &) const;
784};
785
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000786// Each extension should be stored in a ParameterMap.
787// members not in the values could be included in the ParameterMap
788// to keep a single material model
789struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900790 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900791
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900792 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900793 std::string alphaMode; // default "OPAQUE"
794 double alphaCutoff; // default 0.5
795 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900796
797 PbrMetallicRoughness pbrMetallicRoughness;
798
799 NormalTextureInfo normalTexture;
800 OcclusionTextureInfo occlusionTexture;
801 TextureInfo emissiveTexture;
802
Syoyo Fujita046400b2019-07-24 19:26:48 +0900803 // For backward compatibility
804 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
805 ParameterMap values;
806 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100807
808 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900809 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200810
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900811 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
812 std::string extras_json_string;
813 std::string extensions_json_string;
814
Syoyo Fujita046400b2019-07-24 19:26:48 +0900815 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100816 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900817
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900818 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000819};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900820
Syoyo Fujita5b407452017-06-04 17:42:41 +0900821struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900822 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900823 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900824 size_t byteOffset{0}; // minimum 0, default 0
825 size_t byteLength{0}; // required, minimum 1. 0 = invalid
826 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900827 // understood to be tightly packed
828 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
829 // or atttribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900830 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900831 ExtensionMap extensions;
832
833 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
834 std::string extras_json_string;
835 std::string extensions_json_string;
836
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900837 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900838
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900839 BufferView()
840 : buffer(-1),
841 byteOffset(0),
842 byteLength(0),
843 byteStride(0),
844 target(0),
845 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100846 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900847 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000848};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900849
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000850struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900851 int bufferView; // optional in spec but required here since sparse accessor
852 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900853 std::string name;
854 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100855 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000856 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900857 size_t count; // required
858 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900859 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900860 ExtensionMap extensions;
861
862 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
863 std::string extras_json_string;
864 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000865
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900866 std::vector<double>
867 minValues; // optional. integer value is promoted to double
868 std::vector<double>
869 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900870
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100871 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000872 int count;
873 bool isSparse;
874 struct {
875 int byteOffset;
876 int bufferView;
877 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
878 } indices;
879 struct {
880 int bufferView;
881 int byteOffset;
882 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100883 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000884
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900885 ///
886 /// Utility function to compute byteStride for a given bufferView object.
887 /// Returns -1 upon invalid glTF value or parameter configuration.
888 ///
889 int ByteStride(const BufferView &bufferViewObject) const {
890 if (bufferViewObject.byteStride == 0) {
891 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500892 int componentSizeInBytes =
893 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900894 if (componentSizeInBytes <= 0) {
895 return -1;
896 }
897
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900898 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
899 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900900 return -1;
901 }
902
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900903 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900904 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500905 // Check if byteStride is a mulple of the size of the accessor's component
906 // type.
907 int componentSizeInBytes =
908 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900909 if (componentSizeInBytes <= 0) {
910 return -1;
911 }
912
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900913 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900914 return -1;
915 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100916 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900917 }
918
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900919 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900920 }
921
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900922 Accessor()
923 : bufferView(-1),
924 byteOffset(0),
925 normalized(false),
926 componentType(-1),
927 count(0),
928 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000929 sparse.isSparse = false;
930 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100931 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900932 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000933};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900934
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900935struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200936 double aspectRatio; // min > 0
937 double yfov; // required. min > 0
938 double zfar; // min > 0
939 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900940
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900941 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900942 : aspectRatio(0.0),
943 yfov(0.0),
944 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900945 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900946 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100947 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900948 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900949
Selmar09d2ff12018-03-15 17:30:42 +0100950 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900951 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900952
953 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
954 std::string extras_json_string;
955 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900956};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000957
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900958struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200959 double xmag; // required. must not be zero.
960 double ymag; // required. must not be zero.
961 double zfar; // required. `zfar` must be greater than `znear`.
962 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000963
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900964 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100965 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900966 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900967
Selmar09d2ff12018-03-15 17:30:42 +0100968 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900969 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900970
971 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
972 std::string extras_json_string;
973 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900974};
975
976struct Camera {
977 std::string type; // required. "perspective" or "orthographic"
978 std::string name;
979
980 PerspectiveCamera perspective;
981 OrthographicCamera orthographic;
982
983 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100984 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900985 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900986
Selmar09d2ff12018-03-15 17:30:42 +0100987 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000988 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900989
990 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
991 std::string extras_json_string;
992 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900993};
994
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000995struct Primitive {
996 std::map<std::string, int> attributes; // (required) A dictionary object of
997 // integer, where each integer
998 // is the index of the accessor
999 // containing an attribute.
1000 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +09001001 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001002 int indices; // The index of the accessor that contains the indices.
1003 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001004 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +09001005 // where each target is a dict with attribues in ["POSITION, "NORMAL",
1006 // "TANGENT"] pointing
1007 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -05001008 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001009 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001010
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001011 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1012 std::string extras_json_string;
1013 std::string extensions_json_string;
1014
Syoyo Fujita5b407452017-06-04 17:42:41 +09001015 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001016 material = -1;
1017 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001018 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001019 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001020 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001021 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001022};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001023
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001024struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001025 std::string name;
1026 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001027 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001028 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001029 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001030
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001031 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1032 std::string extras_json_string;
1033 std::string extensions_json_string;
1034
jrkoonced1e14722019-08-27 11:51:02 -05001035 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001036 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001037 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001038};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001039
1040class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001041 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001042 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001043
Selmar Kokb74fade2019-10-29 16:09:32 +01001044 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001045
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001046 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001047
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001048 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001049
1050 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001051 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001052 int mesh;
1053 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001054 std::vector<double> rotation; // length must be 0 or 4
1055 std::vector<double> scale; // length must be 0 or 3
1056 std::vector<double> translation; // length must be 0 or 3
1057 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001058 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001059
Selmar09d2ff12018-03-15 17:30:42 +01001060 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001061 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001062
1063 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1064 std::string extras_json_string;
1065 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001066};
1067
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001068struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001069 std::string name;
1070 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001071 std::string
1072 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001073 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001074 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001075 ExtensionMap extensions;
1076
1077 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1078 std::string extras_json_string;
1079 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001080
Selmar Kokb74fade2019-10-29 16:09:32 +01001081 Buffer() = default;
1082 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001083 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001084};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001085
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001086struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001087 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001088 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001089 std::string minVersion;
1090 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001091 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001092 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001093
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001094 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1095 std::string extras_json_string;
1096 std::string extensions_json_string;
1097
jrkoonced1e14722019-08-27 11:51:02 -05001098 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001099 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001100 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001101};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001102
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001103struct Scene {
1104 std::string name;
1105 std::vector<int> nodes;
1106
Selmar09d2ff12018-03-15 17:30:42 +01001107 ExtensionMap extensions;
1108 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001109
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001110 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1111 std::string extras_json_string;
1112 std::string extensions_json_string;
1113
jrkoonced1e14722019-08-27 11:51:02 -05001114 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001115 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001116 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001117};
1118
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001119struct SpotLight {
1120 double innerConeAngle;
1121 double outerConeAngle;
1122
Johan Bowald52936a02019-07-17 09:06:45 +02001123 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001124 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001125 bool operator==(const SpotLight &) const;
1126
1127 ExtensionMap extensions;
1128 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001129
1130 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1131 std::string extras_json_string;
1132 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001133};
1134
Emanuel Schrade186322b2017-11-06 11:14:41 +01001135struct Light {
1136 std::string name;
1137 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001138 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001139 std::string type;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09001140 double range{0.0}; // 0.0 = inifinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001141 SpotLight spot;
1142
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001143 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001144 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001145
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001146 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001147
1148 ExtensionMap extensions;
1149 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001150
1151 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1152 std::string extras_json_string;
1153 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001154};
1155
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001156class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001157 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001158 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001159 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001160
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001161 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001162
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001163 std::vector<Accessor> accessors;
1164 std::vector<Animation> animations;
1165 std::vector<Buffer> buffers;
1166 std::vector<BufferView> bufferViews;
1167 std::vector<Material> materials;
1168 std::vector<Mesh> meshes;
1169 std::vector<Node> nodes;
1170 std::vector<Texture> textures;
1171 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001172 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001173 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001174 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001175 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001176 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001177
sammyKhana0a62bd2020-01-17 13:41:16 +01001178 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001179 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001180 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001181
1182 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001183
1184 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001185 ExtensionMap extensions;
1186
1187 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1188 std::string extras_json_string;
1189 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001190};
1191
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001192enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001193 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001194 REQUIRE_VERSION = 0x01,
1195 REQUIRE_SCENE = 0x02,
1196 REQUIRE_SCENES = 0x04,
1197 REQUIRE_NODES = 0x08,
1198 REQUIRE_ACCESSORS = 0x10,
1199 REQUIRE_BUFFERS = 0x20,
1200 REQUIRE_BUFFER_VIEWS = 0x40,
1201 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001202};
1203
Squareysff644d82018-03-13 22:36:18 +01001204///
1205/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1206///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001207typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1208 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001209 const unsigned char *, int,
1210 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001211
johan bowald642a3432018-04-01 12:37:18 +02001212///
1213/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1214///
1215typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1216 Image *, bool, void *);
1217
Squareys2d3594d2018-03-13 22:40:53 +01001218#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001219// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001220bool LoadImageData(Image *image, const int image_idx, std::string *err,
1221 std::string *warn, int req_width, int req_height,
1222 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001223#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001224
johan bowald642a3432018-04-01 12:37:18 +02001225#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1226// Declaration of default image writer callback
1227bool WriteImageData(const std::string *basepath, const std::string *filename,
1228 Image *image, bool embedImages, void *);
1229#endif
1230
Paolo Jovone6601bf2018-07-07 20:43:33 +02001231///
1232/// FilExistsFunction type. Signature for custom filesystem callbacks.
1233///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001234typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001235
1236///
1237/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1238///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001239typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001240
1241///
1242/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1243///
1244typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001245 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001246 void *);
1247
1248///
1249/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1250///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001251typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001252 const std::vector<unsigned char> &,
1253 void *);
1254
1255///
1256/// A structure containing all required filesystem callbacks and a pointer to
1257/// their user data.
1258///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001259struct FsCallbacks {
1260 FileExistsFunction FileExists;
1261 ExpandFilePathFunction ExpandFilePath;
1262 ReadWholeFileFunction ReadWholeFile;
1263 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001264
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001265 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001266};
1267
1268#ifndef TINYGLTF_NO_FS
1269// Declaration of default filesystem callbacks
1270
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001271bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001272
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001273///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001274/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001275/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001276///
1277/// @param[in] filepath File path string. Assume UTF-8
1278/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1279///
1280std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001281
1282bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001283 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001284
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001285bool WriteWholeFile(std::string *err, const std::string &filepath,
1286 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001287#endif
1288
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001289///
1290/// glTF Parser/Serialier context.
1291///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001292class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001293 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001294#ifdef __clang__
1295#pragma clang diagnostic push
1296#pragma clang diagnostic ignored "-Wc++98-compat"
1297#endif
1298
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001299 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001300
1301#ifdef __clang__
1302#pragma clang diagnostic pop
1303#endif
1304
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001305 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001306
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001307 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001308 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001309 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001310 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001311 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001312 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001313 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001314 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001315
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001316 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001317 /// Loads glTF ASCII asset from string(memory).
1318 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001319 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1320 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1321 /// message to `warn` for example it fails to load asserts. Returns false and
1322 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001323 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001324 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1325 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001326 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001327 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001328
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001329 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001330 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001331 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001332 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001333 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001334 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001335 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001336 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001337
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001338 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001339 /// Loads glTF binary asset from memory.
1340 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001341 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1342 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001343 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001344 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001345 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001346 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001347 const unsigned char *bytes,
1348 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001349 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001350 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001351
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001352 ///
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001353 /// Write glTF to stream, buffers and images will be embeded
1354 ///
1355 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1356 bool prettyPrint, bool writeBinary);
1357
1358 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001359 /// Write glTF to file.
1360 ///
johan bowald642a3432018-04-01 12:37:18 +02001361 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001362 bool embedImages, bool embedBuffers,
1363 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001364
Squareysff644d82018-03-13 22:36:18 +01001365 ///
1366 /// Set callback to use for loading image data
1367 ///
1368 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1369
johan bowald642a3432018-04-01 12:37:18 +02001370 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001371 /// Unset(remove) callback of loading image data
1372 ///
1373 void RemoveImageLoader();
1374
1375 ///
johan bowald642a3432018-04-01 12:37:18 +02001376 /// Set callback to use for writing image data
1377 ///
1378 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1379
Paolo Jovone6601bf2018-07-07 20:43:33 +02001380 ///
1381 /// Set callbacks to use for filesystem (fs) access and their user data
1382 ///
1383 void SetFsCallbacks(FsCallbacks callbacks);
1384
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001385 ///
1386 /// Set serializing default values(default = false).
1387 /// When true, default values are force serialized to .glTF.
Syoyo Fujitaff515702019-08-24 16:29:14 +09001388 /// This may be helpfull if you want to serialize a full description of glTF
1389 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001390 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001391 /// TODO(LTE): Supply parsing option as function arguments to
1392 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001393 ///
1394 void SetSerializeDefaultValues(const bool enabled) {
1395 serialize_default_values_ = enabled;
1396 }
1397
Syoyo Fujitaff515702019-08-24 16:29:14 +09001398 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001399
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001400 ///
1401 /// Store original JSON string for `extras` and `extensions`.
1402 /// This feature will be useful when the user want to reconstruct custom data
1403 /// structure from JSON string.
1404 ///
1405 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1406 store_original_json_for_extras_and_extensions_ = enabled;
1407 }
1408
1409 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1410 return store_original_json_for_extras_and_extensions_;
1411 }
1412
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001413 ///
1414 /// Specify whether preserve image channales when loading images or not.
1415 /// (Not effective when the user suppy their own LoadImageData callbacks)
1416 ///
1417 void SetPreserveImageChannels(bool onoff) {
1418 preserve_image_channels_ = onoff;
1419 }
1420
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001421 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001422
Syoyo Fujitabeded612016-05-01 20:03:43 +09001423 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001424 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001425 /// Loads glTF asset from string(memory).
1426 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001427 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001428 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001429 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001430 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1431 const char *str, const unsigned int length,
1432 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001433
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001434 const unsigned char *bin_data_ = nullptr;
1435 size_t bin_size_ = 0;
1436 bool is_binary_ = false;
1437
Syoyo Fujitaff515702019-08-24 16:29:14 +09001438 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001439
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001440 bool store_original_json_for_extras_and_extensions_ = false;
1441
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001442 bool preserve_image_channels_ = false; /// Default false(expand channels to
1443 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001444
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001445 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001446 std::string warn_;
1447 std::string err_;
1448
Paolo Jovone6601bf2018-07-07 20:43:33 +02001449 FsCallbacks fs = {
1450#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001451 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1452 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001453
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001454 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001455#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001456 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001457
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001458 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001459#endif
1460 };
1461
Squareysff644d82018-03-13 22:36:18 +01001462 LoadImageDataFunction LoadImageData =
1463#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001464 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001465#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001466 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001467#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001468 void *load_image_user_data_{nullptr};
1469 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001470
1471 WriteImageDataFunction WriteImageData =
1472#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001473 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001474#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001475 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001476#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001477 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001478};
1479
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001480#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001481#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001482#endif
1483
Syoyo Fujita7c877972016-03-08 01:31:49 +09001484} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001485
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001486#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001487
Selmar Kok31cb7f92018-10-03 15:39:05 +02001488#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001489#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001490//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001491#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001492#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001493#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001494#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001495#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001496
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001497#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001498// Disable some warnings for external files.
1499#pragma clang diagnostic push
1500#pragma clang diagnostic ignored "-Wfloat-equal"
1501#pragma clang diagnostic ignored "-Wexit-time-destructors"
1502#pragma clang diagnostic ignored "-Wconversion"
1503#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001504#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001505#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001506#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001507#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001508#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1509#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001510#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001511#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001512#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1513#pragma clang diagnostic ignored "-Wswitch-enum"
1514#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001515#pragma clang diagnostic ignored "-Wweak-vtables"
1516#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001517#if __has_warning("-Wdouble-promotion")
1518#pragma clang diagnostic ignored "-Wdouble-promotion"
1519#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001520#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001521#pragma clang diagnostic ignored "-Wcomma"
1522#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001523#if __has_warning("-Wzero-as-null-pointer-constant")
1524#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1525#endif
1526#if __has_warning("-Wcast-qual")
1527#pragma clang diagnostic ignored "-Wcast-qual"
1528#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001529#if __has_warning("-Wmissing-variable-declarations")
1530#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1531#endif
1532#if __has_warning("-Wmissing-prototypes")
1533#pragma clang diagnostic ignored "-Wmissing-prototypes"
1534#endif
1535#if __has_warning("-Wcast-align")
1536#pragma clang diagnostic ignored "-Wcast-align"
1537#endif
1538#if __has_warning("-Wnewline-eof")
1539#pragma clang diagnostic ignored "-Wnewline-eof"
1540#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001541#if __has_warning("-Wunused-parameter")
1542#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001543#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001544#if __has_warning("-Wmismatched-tags")
1545#pragma clang diagnostic ignored "-Wmismatched-tags"
1546#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001547#if __has_warning("-Wextra-semi-stmt")
1548#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1549#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001550#endif
1551
1552// Disable GCC warnigs
1553#ifdef __GNUC__
1554#pragma GCC diagnostic push
1555#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001556#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001557
krokofc0116b2019-03-03 08:28:49 +02001558#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001559#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001560#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001561#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001562#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001563#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001564#include "prettywriter.h"
1565#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001566#include "stringbuffer.h"
1567#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001568#endif
krokof4b6d112019-03-03 01:11:31 +02001569#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001570#endif
Alex Wood7319db72019-01-24 15:38:16 -05001571
1572#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001573#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001574#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001575#endif
Squareys2d3594d2018-03-13 22:40:53 +01001576
1577#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001578#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001579#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001580#endif
krokof4b6d112019-03-03 01:11:31 +02001581#endif
Squareys2d3594d2018-03-13 22:40:53 +01001582
johan bowald642a3432018-04-01 12:37:18 +02001583#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001584#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001585#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001586#endif
krokof4b6d112019-03-03 01:11:31 +02001587#endif
johan bowald642a3432018-04-01 12:37:18 +02001588
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001589#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001590#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001591#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001592
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001593#ifdef __GNUC__
1594#pragma GCC diagnostic pop
1595#endif
1596
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001597#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001598
1599// issue 143.
1600// Define NOMINMAX to avoid min/max defines,
1601// but undef it after included windows.h
1602#ifndef NOMINMAX
1603#define TINYGLTF_INTERNAL_NOMINMAX
1604#define NOMINMAX
1605#endif
1606
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001607#ifndef WIN32_LEAN_AND_MEAN
1608#define WIN32_LEAN_AND_MEAN
1609#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1610#endif
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001611#include <windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001612
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001613#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1614#undef WIN32_LEAN_AND_MEAN
1615#endif
1616
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001617#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1618#undef NOMINMAX
1619#endif
1620
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001621#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001622
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001623#include <fcntl.h> // _O_RDONLY
1624
1625#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001626
1627#endif
1628
Julian Smith0598a202021-08-25 12:06:08 +01001629#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001630//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001631#endif
1632
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001633#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001634// Big endian
1635#else
1636#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1637#define TINYGLTF_LITTLE_ENDIAN 1
1638#endif
1639#endif
1640
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001641namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001642#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001643
1644#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001645// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1646// documents may be active at once.
1647using json =
1648 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1649using json_const_iterator = json::ConstMemberIterator;
1650using json_const_array_iterator = json const *;
1651using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001652 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001653rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1654rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001655#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001656// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1657// not thread safe. Only a single JsonDocument may be active at any one time,
1658// meaning only a single gltf load/save can be active any one time.
1659using json = rapidjson::Value;
1660using json_const_iterator = json::ConstMemberIterator;
1661using json_const_array_iterator = json const *;
1662rapidjson::Document *s_pActiveDocument = nullptr;
1663rapidjson::Document::AllocatorType &GetAllocator() {
1664 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1665 return s_pActiveDocument->GetAllocator();
1666}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001667
1668#ifdef __clang__
1669#pragma clang diagnostic push
1670// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1671#pragma clang diagnostic ignored "-Wunused-member-function"
1672#endif
1673
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001674struct JsonDocument : public rapidjson::Document {
1675 JsonDocument() {
1676 assert(s_pActiveDocument ==
1677 nullptr); // When using default allocator, only one document can be
1678 // active at a time, if you need multiple active at once,
1679 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1680 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001681 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001682 JsonDocument(const JsonDocument &) = delete;
1683 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001684 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001685 s_pActiveDocument = this;
1686 rhs.isNil = true;
1687 }
1688 ~JsonDocument() {
1689 if (!isNil) {
1690 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001691 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001692 }
jrkooncece7fa742019-09-04 13:31:44 -05001693
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001694 private:
1695 bool isNil = false;
1696};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001697
1698#ifdef __clang__
1699#pragma clang diagnostic pop
1700#endif
1701
jrkooncece7fa742019-09-04 13:31:44 -05001702#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001703
jrkooncecba5d6c2019-08-29 11:26:22 -05001704#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001705using nlohmann::json;
1706using json_const_iterator = json::const_iterator;
1707using json_const_array_iterator = json_const_iterator;
1708using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001709#endif
1710
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001711void JsonParse(JsonDocument &doc, const char *str, size_t length,
1712 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001713#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001714 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001715 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001716#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001717 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001718#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001719}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001720} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001721
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001722#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001723#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001724#endif
1725
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001726#ifdef __clang__
1727#pragma clang diagnostic push
1728#pragma clang diagnostic ignored "-Wc++98-compat"
1729#endif
1730
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001731namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001732
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001733///
1734/// Internal LoadImageDataOption struct.
1735/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001736/// The struct is not passed when the user supply their own LoadImageData
1737/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001738///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001739struct LoadImageDataOption {
1740 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1741 // channels) default `false`(channels are expanded to RGBA for backward
1742 // compatiblity).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001743 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001744};
1745
Selmar Kok31cb7f92018-10-03 15:39:05 +02001746// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001747static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1748 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001749
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001750 switch (one.Type()) {
1751 case NULL_TYPE:
1752 return true;
1753 case BOOL_TYPE:
1754 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001755 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001756 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1757 case INT_TYPE:
1758 return one.Get<int>() == other.Get<int>();
1759 case OBJECT_TYPE: {
1760 auto oneObj = one.Get<tinygltf::Value::Object>();
1761 auto otherObj = other.Get<tinygltf::Value::Object>();
1762 if (oneObj.size() != otherObj.size()) return false;
1763 for (auto &it : oneObj) {
1764 auto otherIt = otherObj.find(it.first);
1765 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001766
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001767 if (!Equals(it.second, otherIt->second)) return false;
1768 }
1769 return true;
1770 }
1771 case ARRAY_TYPE: {
1772 if (one.Size() != other.Size()) return false;
1773 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001774 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001775 return true;
1776 }
1777 case STRING_TYPE:
1778 return one.Get<std::string>() == other.Get<std::string>();
1779 case BINARY_TYPE:
1780 return one.Get<std::vector<unsigned char> >() ==
1781 other.Get<std::vector<unsigned char> >();
1782 default: {
1783 // unhandled type
1784 return false;
1785 }
1786 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001787}
1788
1789// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001790static bool Equals(const std::vector<double> &one,
1791 const std::vector<double> &other) {
1792 if (one.size() != other.size()) return false;
1793 for (int i = 0; i < int(one.size()); ++i) {
1794 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1795 }
1796 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001797}
1798
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001799bool Accessor::operator==(const Accessor &other) const {
1800 return this->bufferView == other.bufferView &&
1801 this->byteOffset == other.byteOffset &&
1802 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001803 this->count == other.count && this->extensions == other.extensions &&
1804 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001805 Equals(this->maxValues, other.maxValues) &&
1806 Equals(this->minValues, other.minValues) && this->name == other.name &&
1807 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001808}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001809bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001810 return this->channels == other.channels &&
1811 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001812 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001813}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001814bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001815 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001816 this->target_node == other.target_node &&
1817 this->target_path == other.target_path &&
1818 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001819}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001820bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001821 return this->extras == other.extras && this->extensions == other.extensions &&
1822 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001823 this->interpolation == other.interpolation &&
1824 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001825}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001826bool Asset::operator==(const Asset &other) const {
1827 return this->copyright == other.copyright &&
1828 this->extensions == other.extensions && this->extras == other.extras &&
1829 this->generator == other.generator &&
1830 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001831}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001832bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001833 return this->data == other.data && this->extensions == other.extensions &&
1834 this->extras == other.extras && this->name == other.name &&
1835 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001836}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001837bool BufferView::operator==(const BufferView &other) const {
1838 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1839 this->byteOffset == other.byteOffset &&
1840 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001841 this->target == other.target && this->extensions == other.extensions &&
1842 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001843 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001844}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001845bool Camera::operator==(const Camera &other) const {
1846 return this->name == other.name && this->extensions == other.extensions &&
1847 this->extras == other.extras &&
1848 this->orthographic == other.orthographic &&
1849 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001850}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001851bool Image::operator==(const Image &other) const {
1852 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001853 this->component == other.component &&
1854 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001855 this->height == other.height && this->image == other.image &&
1856 this->mimeType == other.mimeType && this->name == other.name &&
1857 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001858}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001859bool Light::operator==(const Light &other) const {
1860 return Equals(this->color, other.color) && this->name == other.name &&
1861 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001862}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001863bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001864 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1865 (this->normalTexture == other.normalTexture) &&
1866 (this->occlusionTexture == other.occlusionTexture) &&
1867 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001868 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001869 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001870 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001871 (this->doubleSided == other.doubleSided) &&
1872 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001873 (this->extras == other.extras) && (this->values == other.values) &&
1874 (this->additionalValues == other.additionalValues) &&
1875 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001876}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001877bool Mesh::operator==(const Mesh &other) const {
1878 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001879 this->name == other.name && Equals(this->weights, other.weights) &&
1880 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001881}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001882bool Model::operator==(const Model &other) const {
1883 return this->accessors == other.accessors &&
1884 this->animations == other.animations && this->asset == other.asset &&
1885 this->buffers == other.buffers &&
1886 this->bufferViews == other.bufferViews &&
1887 this->cameras == other.cameras &&
1888 this->defaultScene == other.defaultScene &&
1889 this->extensions == other.extensions &&
1890 this->extensionsRequired == other.extensionsRequired &&
1891 this->extensionsUsed == other.extensionsUsed &&
1892 this->extras == other.extras && this->images == other.images &&
1893 this->lights == other.lights && this->materials == other.materials &&
1894 this->meshes == other.meshes && this->nodes == other.nodes &&
1895 this->samplers == other.samplers && this->scenes == other.scenes &&
1896 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001897}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001898bool Node::operator==(const Node &other) const {
1899 return this->camera == other.camera && this->children == other.children &&
1900 this->extensions == other.extensions && this->extras == other.extras &&
1901 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1902 this->name == other.name && Equals(this->rotation, other.rotation) &&
1903 Equals(this->scale, other.scale) && this->skin == other.skin &&
1904 Equals(this->translation, other.translation) &&
1905 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001906}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001907bool SpotLight::operator==(const SpotLight &other) const {
1908 return this->extensions == other.extensions && this->extras == other.extras &&
1909 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1910 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1911}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001912bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1913 return this->extensions == other.extensions && this->extras == other.extras &&
1914 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1915 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1916 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1917 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001918}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001919bool Parameter::operator==(const Parameter &other) const {
1920 if (this->bool_value != other.bool_value ||
1921 this->has_number_value != other.has_number_value)
1922 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001923
Selmar Kok2bda71c2018-10-05 14:36:05 +02001924 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1925 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001926
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001927 if (this->json_double_value.size() != other.json_double_value.size())
1928 return false;
1929 for (auto &it : this->json_double_value) {
1930 auto otherIt = other.json_double_value.find(it.first);
1931 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001932
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001933 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1934 }
1935
1936 if (!Equals(this->number_array, other.number_array)) return false;
1937
1938 if (this->string_value != other.string_value) return false;
1939
1940 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001941}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001942bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1943 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1944 this->extensions == other.extensions && this->extras == other.extras &&
1945 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1946 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1947 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001948}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001949bool Primitive::operator==(const Primitive &other) const {
1950 return this->attributes == other.attributes && this->extras == other.extras &&
1951 this->indices == other.indices && this->material == other.material &&
1952 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001953}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001954bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001955 return this->extensions == other.extensions && this->extras == other.extras &&
1956 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001957 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001958 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001959
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001960 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02001961}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001962bool Scene::operator==(const Scene &other) const {
1963 return this->extensions == other.extensions && this->extras == other.extras &&
1964 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001965}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001966bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001967 return this->extensions == other.extensions && this->extras == other.extras &&
1968 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001969 this->joints == other.joints && this->name == other.name &&
1970 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001971}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001972bool Texture::operator==(const Texture &other) const {
1973 return this->extensions == other.extensions && this->extras == other.extras &&
1974 this->name == other.name && this->sampler == other.sampler &&
1975 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001976}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001977bool TextureInfo::operator==(const TextureInfo &other) const {
1978 return this->extensions == other.extensions && this->extras == other.extras &&
1979 this->index == other.index && this->texCoord == other.texCoord;
1980}
1981bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1982 return this->extensions == other.extensions && this->extras == other.extras &&
1983 this->index == other.index && this->texCoord == other.texCoord &&
1984 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
1985}
1986bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
1987 return this->extensions == other.extensions && this->extras == other.extras &&
1988 this->index == other.index && this->texCoord == other.texCoord &&
1989 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
1990}
1991bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
1992 return this->extensions == other.extensions && this->extras == other.extras &&
1993 (this->baseColorTexture == other.baseColorTexture) &&
1994 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001995 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001996 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
1997 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
1998}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001999bool Value::operator==(const Value &other) const {
2000 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002001}
2002
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002003static void swap4(unsigned int *val) {
2004#ifdef TINYGLTF_LITTLE_ENDIAN
2005 (void)val;
2006#else
2007 unsigned int tmp = *val;
2008 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2009 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2010
2011 dst[0] = src[3];
2012 dst[1] = src[2];
2013 dst[2] = src[1];
2014 dst[3] = src[0];
2015#endif
2016}
2017
Syoyo Fujitabeded612016-05-01 20:03:43 +09002018static std::string JoinPath(const std::string &path0,
2019 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002020 if (path0.empty()) {
2021 return path1;
2022 } else {
2023 // check '/'
2024 char lastChar = *path0.rbegin();
2025 if (lastChar != '/') {
2026 return path0 + std::string("/") + path1;
2027 } else {
2028 return path0 + path1;
2029 }
2030 }
2031}
2032
Syoyo Fujita643ce102016-05-01 17:19:37 +09002033static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002034 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002035 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2036 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002037 // Error, fs callback[s] missing
2038 return std::string();
2039 }
2040
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002041 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002042 std::string absPath =
2043 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002044 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002045 return absPath;
2046 }
2047 }
2048
2049 return std::string();
2050}
2051
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002052static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002053 if (FileName.find_last_of(".") != std::string::npos)
2054 return FileName.substr(FileName.find_last_of(".") + 1);
2055 return "";
2056}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002057
Syoyo Fujita643ce102016-05-01 17:19:37 +09002058static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002059 if (filepath.find_last_of("/\\") != std::string::npos)
2060 return filepath.substr(0, filepath.find_last_of("/\\"));
2061 return "";
2062}
2063
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002064static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002065 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002066 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002067 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002068}
2069
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002070std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002071std::string base64_decode(std::string const &s);
2072
2073/*
2074 base64.cpp and base64.h
2075
2076 Copyright (C) 2004-2008 René Nyffenegger
2077
2078 This source code is provided 'as-is', without any express or implied
2079 warranty. In no event will the author be held liable for any damages
2080 arising from the use of this software.
2081
2082 Permission is granted to anyone to use this software for any purpose,
2083 including commercial applications, and to alter it and redistribute it
2084 freely, subject to the following restrictions:
2085
2086 1. The origin of this source code must not be misrepresented; you must not
2087 claim that you wrote the original source code. If you use this source code
2088 in a product, an acknowledgment in the product documentation would be
2089 appreciated but is not required.
2090
2091 2. Altered source versions must be plainly marked as such, and must not be
2092 misrepresented as being the original source code.
2093
2094 3. This notice may not be removed or altered from any source distribution.
2095
2096 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2097
2098*/
2099
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002100#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002101#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002102#pragma clang diagnostic ignored "-Wsign-conversion"
2103#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002104#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002105
2106static inline bool is_base64(unsigned char c) {
2107 return (isalnum(c) || (c == '+') || (c == '/'));
2108}
2109
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002110std::string base64_encode(unsigned char const *bytes_to_encode,
2111 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002112 std::string ret;
2113 int i = 0;
2114 int j = 0;
2115 unsigned char char_array_3[3];
2116 unsigned char char_array_4[4];
2117
Syoyo Fujitaff515702019-08-24 16:29:14 +09002118 const char *base64_chars =
2119 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2120 "abcdefghijklmnopqrstuvwxyz"
2121 "0123456789+/";
2122
johan bowald30c53472018-03-30 11:49:36 +02002123 while (in_len--) {
2124 char_array_3[i++] = *(bytes_to_encode++);
2125 if (i == 3) {
2126 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002127 char_array_4[1] =
2128 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2129 char_array_4[2] =
2130 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002131 char_array_4[3] = char_array_3[2] & 0x3f;
2132
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002133 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002134 i = 0;
2135 }
2136 }
2137
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002138 if (i) {
2139 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002140
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002141 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2142 char_array_4[1] =
2143 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2144 char_array_4[2] =
2145 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002146
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002147 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002148
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002149 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002150 }
2151
2152 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002153}
2154
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002155std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002156 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002157 int i = 0;
2158 int j = 0;
2159 int in_ = 0;
2160 unsigned char char_array_4[4], char_array_3[3];
2161 std::string ret;
2162
Syoyo Fujitaff515702019-08-24 16:29:14 +09002163 const std::string base64_chars =
2164 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2165 "abcdefghijklmnopqrstuvwxyz"
2166 "0123456789+/";
2167
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002168 while (in_len-- && (encoded_string[in_] != '=') &&
2169 is_base64(encoded_string[in_])) {
2170 char_array_4[i++] = encoded_string[in_];
2171 in_++;
2172 if (i == 4) {
2173 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002174 char_array_4[i] =
2175 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002176
2177 char_array_3[0] =
2178 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2179 char_array_3[1] =
2180 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2181 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2182
Syoyo Fujita7c877972016-03-08 01:31:49 +09002183 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002184 i = 0;
2185 }
2186 }
2187
2188 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002189 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002190
2191 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002192 char_array_4[j] =
2193 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002194
2195 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2196 char_array_3[1] =
2197 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2198 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2199
Syoyo Fujita7c877972016-03-08 01:31:49 +09002200 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002201 }
2202
2203 return ret;
2204}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002205#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002206#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002207#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002208
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002209// https://github.com/syoyo/tinygltf/issues/228
2210// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2211// decoding?
2212//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002213// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002214// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002215// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002216// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002217// License: Boost Software License
2218// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002219
Alexander Woode4bc6c72021-10-14 08:54:59 -04002220// Permission is hereby granted, free of charge, to any person or organization
2221// obtaining a copy of the software and accompanying documentation covered by
2222// this license (the "Software") to use, reproduce, display, distribute,
2223// execute, and transmit the Software, and to prepare derivative works of the
2224// Software, and to permit third-parties to whom the Software is furnished to
2225// do so, all subject to the following:
2226// The copyright notices in the Software and this entire statement, including
2227// the above license grant, this restriction and the following disclaimer,
2228// must be included in all copies of the Software, in whole or in part, and
2229// all derivative works of the Software, unless such copies or derivative
2230// works are solely in the form of machine-executable object code generated by
2231// a source language processor.
2232// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2233// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2234// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2235// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2236// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2237// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2238// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002239//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002240namespace dlib {
2241
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002242inline unsigned char from_hex(unsigned char ch) {
2243 if (ch <= '9' && ch >= '0')
2244 ch -= '0';
2245 else if (ch <= 'f' && ch >= 'a')
2246 ch -= 'a' - 10;
2247 else if (ch <= 'F' && ch >= 'A')
2248 ch -= 'A' - 10;
2249 else
2250 ch = 0;
2251 return ch;
2252}
2253
2254static const std::string urldecode(const std::string &str) {
2255 using namespace std;
2256 string result;
2257 string::size_type i;
2258 for (i = 0; i < str.size(); ++i) {
2259 if (str[i] == '+') {
2260 result += ' ';
2261 } else if (str[i] == '%' && str.size() > i + 2) {
2262 const unsigned char ch1 =
2263 from_hex(static_cast<unsigned char>(str[i + 1]));
2264 const unsigned char ch2 =
2265 from_hex(static_cast<unsigned char>(str[i + 2]));
2266 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2267 result += static_cast<char>(ch);
2268 i += 2;
2269 } else {
2270 result += str[i];
2271 }
2272 }
2273 return result;
2274}
2275
2276} // namespace dlib
2277// --- dlib end --------------------------------------------------------------
2278
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002279static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002280 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002281 const std::string &basedir, bool required,
2282 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002283 if (fs == nullptr || fs->FileExists == nullptr ||
2284 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002285 // This is a developer error, assert() ?
2286 if (err) {
2287 (*err) += "FS callback[s] not set\n";
2288 }
2289 return false;
2290 }
2291
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002292 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002293
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002294 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002295
2296 std::vector<std::string> paths;
2297 paths.push_back(basedir);
2298 paths.push_back(".");
2299
Paolo Jovone6601bf2018-07-07 20:43:33 +02002300 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002301 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002302 if (failMsgOut) {
2303 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002304 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002305 return false;
2306 }
2307
Paolo Jovone6601bf2018-07-07 20:43:33 +02002308 std::vector<unsigned char> buf;
2309 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002310 bool fileRead =
2311 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002312 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002313 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002314 (*failMsgOut) +=
2315 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002316 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002317 return false;
2318 }
2319
Paolo Jovone6601bf2018-07-07 20:43:33 +02002320 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002321 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002322 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002323 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002324 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002325 return false;
2326 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002327
2328 if (checkSize) {
2329 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002330 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002331 return true;
2332 } else {
2333 std::stringstream ss;
2334 ss << "File size mismatch : " << filepath << ", requestedBytes "
2335 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002336 if (failMsgOut) {
2337 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002338 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002339 return false;
2340 }
2341 }
2342
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002343 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002344 return true;
2345}
2346
Squareysff644d82018-03-13 22:36:18 +01002347void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002348 LoadImageData = func;
2349 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002350 user_image_loader_ = true;
2351}
2352
2353void TinyGLTF::RemoveImageLoader() {
2354 LoadImageData =
2355#ifndef TINYGLTF_NO_STB_IMAGE
2356 &tinygltf::LoadImageData;
2357#else
2358 nullptr;
2359#endif
2360
2361 load_image_user_data_ = nullptr;
2362 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002363}
2364
Squareys2d3594d2018-03-13 22:40:53 +01002365#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002366bool LoadImageData(Image *image, const int image_idx, std::string *err,
2367 std::string *warn, int req_width, int req_height,
2368 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002369 (void)warn;
2370
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002371 LoadImageDataOption option;
2372 if (user_data) {
2373 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2374 }
2375
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002376 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002377
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002378 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002379
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002380 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002381 // false: force 32-bit textures for common Vulkan compatibility. It appears
2382 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002383 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002384 int bits = 8;
2385 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002386
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002387 // It is possible that the image we want to load is a 16bit per channel image
2388 // We are going to attempt to load it as 16bit per channel, and if it worked,
2389 // set the image data accodingly. We are casting the returned pointer into
2390 // unsigned char, because we are representing "bytes". But we are updating
2391 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2392 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002393 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002394 data = reinterpret_cast<unsigned char *>(
2395 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002396 if (data) {
2397 bits = 16;
2398 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2399 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002400 }
2401
2402 // at this point, if data is still NULL, it means that the image wasn't
2403 // 16bit per channel, we are going to load it as a normal 8bit per channel
2404 // mage as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002405 // if image cannot be decoded, ignore parsing and keep it by its path
2406 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002407 // FIXME we should only enter this function if the image is embedded. If
2408 // image->uri references
2409 // an image file, it should be left as it is. Image loading should not be
2410 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002411 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002412 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002413 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002414 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002415 (*err) +=
2416 "Unknown image format. STB cannot decode image data for image[" +
2417 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002418 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002419 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002420 }
2421
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002422 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002423 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002424 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002425 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2426 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002427 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002428 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002429 }
2430
2431 if (req_width > 0) {
2432 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002433 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002434 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002435 (*err) += "Image width mismatch for image[" +
2436 std::to_string(image_idx) + "] name = \"" + image->name +
2437 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002438 }
2439 return false;
2440 }
2441 }
2442
2443 if (req_height > 0) {
2444 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002445 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002446 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002447 (*err) += "Image height mismatch. for image[" +
2448 std::to_string(image_idx) + "] name = \"" + image->name +
2449 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002450 }
2451 return false;
2452 }
2453 }
2454
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002455 if (req_comp != 0) {
2456 // loaded data has `req_comp` channels(components)
2457 comp = req_comp;
2458 }
2459
Syoyo Fujitabeded612016-05-01 20:03:43 +09002460 image->width = w;
2461 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002462 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002463 image->bits = bits;
2464 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002465 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2466 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002467 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002468
Syoyo Fujitabeded612016-05-01 20:03:43 +09002469 return true;
2470}
Squareys2d3594d2018-03-13 22:40:53 +01002471#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002472
johan bowald642a3432018-04-01 12:37:18 +02002473void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2474 WriteImageData = func;
2475 write_image_user_data_ = user_data;
2476}
2477
2478#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2479static void WriteToMemory_stbi(void *context, void *data, int size) {
2480 std::vector<unsigned char> *buffer =
2481 reinterpret_cast<std::vector<unsigned char> *>(context);
2482
2483 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2484
2485 buffer->insert(buffer->end(), pData, pData + size);
2486}
2487
2488bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002489 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002490 const std::string ext = GetFilePathExtension(*filename);
2491
Paolo Jovone6601bf2018-07-07 20:43:33 +02002492 // Write image to temporary buffer
2493 std::string header;
2494 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002495
Paolo Jovone6601bf2018-07-07 20:43:33 +02002496 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002497 if ((image->bits != 8) ||
2498 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002499 // Unsupported pixel format
2500 return false;
2501 }
2502
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002503 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002504 image->height, image->component,
2505 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002506 return false;
2507 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002508 header = "data:image/png;base64,";
2509 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002510 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002511 image->height, image->component,
2512 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002513 return false;
2514 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002515 header = "data:image/jpeg;base64,";
2516 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002517 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002518 image->height, image->component,
2519 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002520 return false;
2521 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002522 header = "data:image/bmp;base64,";
2523 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002524 // Error: can't output requested format to file
2525 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002526 }
johan bowald642a3432018-04-01 12:37:18 +02002527
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002528 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002529 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002530 if (data.size()) {
2531 image->uri =
2532 header +
2533 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
2534 } else {
2535 // Throw error?
2536 }
2537 } else {
2538 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002539 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002540 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002541 const std::string imagefilepath = JoinPath(*basepath, *filename);
2542 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002543 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2544 fs->user_data)) {
2545 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002546 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002547 }
johan bowald642a3432018-04-01 12:37:18 +02002548 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002549 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002550 }
2551 image->uri = *filename;
2552 }
2553
2554 return true;
2555}
2556#endif
2557
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002558void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002559
Harokyangfb256602019-10-30 16:13:52 +08002560#ifdef _WIN32
2561static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002562 int wstr_size =
2563 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
Harokyangfb256602019-10-30 16:13:52 +08002564 std::wstring wstr(wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002565 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2566 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002567 return wstr;
2568}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002569
2570static inline std::string WcharToUTF8(const std::wstring &wstr) {
2571 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002572 nullptr, 0, NULL, NULL);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002573 std::string str(str_size, 0);
2574 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
2575 (int)str.size(), NULL, NULL);
2576 return str;
2577}
Harokyangfb256602019-10-30 16:13:52 +08002578#endif
2579
Paolo Jovone6601bf2018-07-07 20:43:33 +02002580#ifndef TINYGLTF_NO_FS
2581// Default implementations of filesystem functions
2582
2583bool FileExists(const std::string &abs_filename, void *) {
2584 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002585#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002586 if (asset_manager) {
2587 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2588 AASSET_MODE_STREAMING);
2589 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002590 return false;
2591 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002592 AAsset_close(asset);
2593 ret = true;
2594 } else {
2595 return false;
2596 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002597#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002598#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09002599#if defined(_MSC_VER) || defined(__GLIBCXX__)
2600 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002601 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002602 if (err != 0) {
2603 return false;
2604 }
2605#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002606 FILE *fp = nullptr;
2607 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2608 if (err != 0) {
2609 return false;
2610 }
2611#endif
2612
2613#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002614 FILE *fp = fopen(abs_filename.c_str(), "rb");
2615#endif
2616 if (fp) {
2617 ret = true;
2618 fclose(fp);
2619 } else {
2620 ret = false;
2621 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002622#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002623
2624 return ret;
2625}
2626
2627std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002628 // https://github.com/syoyo/tinygltf/issues/368
2629 //
2630 // No file path expansion in built-in FS function anymore, since glTF URI
2631 // should not contain tilde('~') and environment variables, and for security
2632 // reason(`wordexp`).
2633 //
2634 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2635 // `LoadBinaryFromMemory`) in expanded absolute path.
2636
2637 return filepath;
2638
2639#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002640#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002641 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002642 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002643 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002644 wchar_t *wstr = new wchar_t[wlen];
2645 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002646
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002647 std::wstring ws(wstr);
2648 delete[] wstr;
2649 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002650
Paolo Jovone6601bf2018-07-07 20:43:33 +02002651#else
2652
2653#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002654 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002655 // no expansion
2656 std::string s = filepath;
2657#else
2658 std::string s;
2659 wordexp_t p;
2660
2661 if (filepath.empty()) {
2662 return "";
2663 }
2664
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002665 // Quote the string to keep any spaces in filepath intact.
2666 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002667 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002668 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002669 if (ret) {
2670 // err
2671 s = filepath;
2672 return s;
2673 }
2674
2675 // Use first element only.
2676 if (p.we_wordv) {
2677 s = std::string(p.we_wordv[0]);
2678 wordfree(&p);
2679 } else {
2680 s = filepath;
2681 }
2682
2683#endif
2684
2685 return s;
2686#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002687#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002688}
2689
2690bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2691 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002692#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2693 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002694 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2695 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002696 if (!asset) {
2697 if (err) {
2698 (*err) += "File open error : " + filepath + "\n";
2699 }
2700 return false;
2701 }
2702 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002703 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002704 if (err) {
2705 (*err) += "Invalid file size : " + filepath +
2706 " (does the path point to a directory?)";
2707 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002708 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002709 }
2710 out->resize(size);
2711 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2712 AAsset_close(asset);
2713 return true;
2714 } else {
2715 if (err) {
2716 (*err) += "No asset manager specified : " + filepath + "\n";
2717 }
2718 return false;
2719 }
2720#else
Harokyang5cecef22019-10-30 15:16:46 +08002721#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002722#if defined(__GLIBCXX__) // mingw
2723 int file_descriptor =
2724 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002725 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2726 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002727#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002728 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2729 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002730 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002731#else
2732 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002733 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2734#endif
Harokyang5cecef22019-10-30 15:16:46 +08002735#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002736 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002737#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002738 if (!f) {
2739 if (err) {
2740 (*err) += "File open error : " + filepath + "\n";
2741 }
2742 return false;
2743 }
2744
2745 f.seekg(0, f.end);
2746 size_t sz = static_cast<size_t>(f.tellg());
2747 f.seekg(0, f.beg);
2748
Syoyo Fujitae8862472019-10-20 17:47:50 +09002749 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002750 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002751 (*err) += "Invalid file size : " + filepath +
2752 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002753 }
2754 return false;
2755 } else if (sz == 0) {
2756 if (err) {
2757 (*err) += "File is empty : " + filepath + "\n";
2758 }
2759 return false;
2760 }
2761
2762 out->resize(sz);
2763 f.read(reinterpret_cast<char *>(&out->at(0)),
2764 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002765
2766 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002767#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002768}
2769
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002770bool WriteWholeFile(std::string *err, const std::string &filepath,
2771 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002772#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002773#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002774 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2775 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2776 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2777 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002778 std::ostream f(&wfile_buf);
2779#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002780 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002781#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002782 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2783#endif
Harokyangfb256602019-10-30 16:13:52 +08002784#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002785 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002786#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002787 if (!f) {
2788 if (err) {
2789 (*err) += "File open error for writing : " + filepath + "\n";
2790 }
2791 return false;
2792 }
2793
2794 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2795 static_cast<std::streamsize>(contents.size()));
2796 if (!f) {
2797 if (err) {
2798 (*err) += "File write error: " + filepath + "\n";
2799 }
2800 return false;
2801 }
2802
Paolo Jovone6601bf2018-07-07 20:43:33 +02002803 return true;
2804}
2805
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002806#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002807
johan bowald642a3432018-04-01 12:37:18 +02002808static std::string MimeToExt(const std::string &mimeType) {
2809 if (mimeType == "image/jpeg") {
2810 return "jpg";
2811 } else if (mimeType == "image/png") {
2812 return "png";
2813 } else if (mimeType == "image/bmp") {
2814 return "bmp";
2815 } else if (mimeType == "image/gif") {
2816 return "gif";
2817 }
2818
2819 return "";
2820}
2821
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002822static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002823 bool embedImages,
2824 WriteImageDataFunction *WriteImageData = nullptr,
2825 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002826 std::string filename;
2827 std::string ext;
FsiGuy00015623db855c62020-03-09 16:57:21 -05002828 // If image has uri, use it it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002829 if (image.uri.size()) {
2830 filename = GetBaseFilename(image.uri);
2831 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002832 } else if (image.bufferView != -1) {
2833 // If there's no URI and the data exists in a buffer,
2834 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002835 } else if (image.name.size()) {
2836 ext = MimeToExt(image.mimeType);
2837 // Otherwise use name as filename
2838 filename = image.name + "." + ext;
2839 } else {
2840 ext = MimeToExt(image.mimeType);
2841 // Fallback to index of image as filename
2842 filename = std::to_string(index) + "." + ext;
2843 }
2844
2845 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002846 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002847 std::string uri;
2848 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2849 }
2850}
2851
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002852bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002853 std::string header = "data:application/octet-stream;base64,";
2854 if (in.find(header) == 0) {
2855 return true;
2856 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002857
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002858 header = "data:image/jpeg;base64,";
2859 if (in.find(header) == 0) {
2860 return true;
2861 }
Squareys43374632018-03-13 22:20:48 +01002862
Syoyo Fujita620eed12016-01-02 23:37:12 +09002863 header = "data:image/png;base64,";
2864 if (in.find(header) == 0) {
2865 return true;
2866 }
Squareys43374632018-03-13 22:20:48 +01002867
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002868 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002869 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002870 return true;
2871 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002872
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002873 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002874 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002875 return true;
2876 }
2877
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002878 header = "data:text/plain;base64,";
2879 if (in.find(header) == 0) {
2880 return true;
2881 }
2882
Syoyo Fujita20244e12018-03-15 11:01:05 -05002883 header = "data:application/gltf-buffer;base64,";
2884 if (in.find(header) == 0) {
2885 return true;
2886 }
2887
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002888 return false;
2889}
2890
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002891bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2892 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002893 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002894 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002895 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002896 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002897 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002898
2899 if (data.empty()) {
2900 header = "data:image/jpeg;base64,";
2901 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002902 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002903 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002904 }
2905 }
2906
2907 if (data.empty()) {
2908 header = "data:image/png;base64,";
2909 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002910 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002911 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002912 }
2913 }
Squareys43374632018-03-13 22:20:48 +01002914
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002915 if (data.empty()) {
2916 header = "data:image/bmp;base64,";
2917 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002918 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002919 data = base64_decode(in.substr(header.size())); // cut mime string.
2920 }
2921 }
2922
2923 if (data.empty()) {
2924 header = "data:image/gif;base64,";
2925 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002926 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002927 data = base64_decode(in.substr(header.size())); // cut mime string.
2928 }
2929 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002930
2931 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002932 header = "data:text/plain;base64,";
2933 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002934 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002935 data = base64_decode(in.substr(header.size()));
2936 }
2937 }
2938
2939 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002940 header = "data:application/gltf-buffer;base64,";
2941 if (in.find(header) == 0) {
2942 data = base64_decode(in.substr(header.size()));
2943 }
2944 }
2945
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002946 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002947 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002948 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002949 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002950
2951 if (checkSize) {
2952 if (data.size() != reqBytes) {
2953 return false;
2954 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002955 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002956 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002957 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002958 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002959 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002960 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002961}
2962
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002963namespace {
2964bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002965#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002966 if (!o.IsDouble()) {
2967 if (o.IsInt()) {
2968 val = o.GetInt();
2969 return true;
2970 } else if (o.IsUint()) {
2971 val = static_cast<int>(o.GetUint());
2972 return true;
2973 } else if (o.IsInt64()) {
2974 val = static_cast<int>(o.GetInt64());
2975 return true;
2976 } else if (o.IsUint64()) {
2977 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002978 return true;
2979 }
jrkoonce5cecc412019-08-29 11:45:04 -05002980 }
2981
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002982 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002983#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002984 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002985
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002986 if ((type == json::value_t::number_integer) ||
2987 (type == json::value_t::number_unsigned)) {
2988 val = static_cast<int>(o.get<int64_t>());
2989 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002990 }
2991
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002992 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002993#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05002994}
2995
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002996#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002997bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002998 if (o.IsDouble()) {
2999 val = o.GetDouble();
3000 return true;
3001 }
3002
3003 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003004}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003005#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003006
3007bool GetNumber(const json &o, double &val) {
3008#ifdef TINYGLTF_USE_RAPIDJSON
3009 if (o.IsNumber()) {
3010 val = o.GetDouble();
3011 return true;
3012 }
3013
3014 return false;
3015#else
3016 if (o.is_number()) {
3017 val = o.get<double>();
3018 return true;
3019 }
3020
3021 return false;
3022#endif
3023}
3024
3025bool GetString(const json &o, std::string &val) {
3026#ifdef TINYGLTF_USE_RAPIDJSON
3027 if (o.IsString()) {
3028 val = o.GetString();
3029 return true;
3030 }
3031
3032 return false;
3033#else
3034 if (o.type() == json::value_t::string) {
3035 val = o.get<std::string>();
3036 return true;
3037 }
3038
3039 return false;
3040#endif
3041}
3042
3043bool IsArray(const json &o) {
3044#ifdef TINYGLTF_USE_RAPIDJSON
3045 return o.IsArray();
3046#else
3047 return o.is_array();
3048#endif
3049}
3050
3051json_const_array_iterator ArrayBegin(const json &o) {
3052#ifdef TINYGLTF_USE_RAPIDJSON
3053 return o.Begin();
3054#else
3055 return o.begin();
3056#endif
3057}
3058
3059json_const_array_iterator ArrayEnd(const json &o) {
3060#ifdef TINYGLTF_USE_RAPIDJSON
3061 return o.End();
3062#else
3063 return o.end();
3064#endif
3065}
3066
3067bool IsObject(const json &o) {
3068#ifdef TINYGLTF_USE_RAPIDJSON
3069 return o.IsObject();
3070#else
3071 return o.is_object();
3072#endif
3073}
3074
3075json_const_iterator ObjectBegin(const json &o) {
3076#ifdef TINYGLTF_USE_RAPIDJSON
3077 return o.MemberBegin();
3078#else
3079 return o.begin();
3080#endif
3081}
3082
3083json_const_iterator ObjectEnd(const json &o) {
3084#ifdef TINYGLTF_USE_RAPIDJSON
3085 return o.MemberEnd();
3086#else
3087 return o.end();
3088#endif
3089}
3090
Rahul Sheth01d54382020-07-10 14:27:37 -04003091// Making this a const char* results in a pointer to a temporary when
3092// TINYGLTF_USE_RAPIDJSON is off.
3093std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003094#ifdef TINYGLTF_USE_RAPIDJSON
3095 return it->name.GetString();
3096#else
3097 return it.key().c_str();
3098#endif
3099}
3100
3101bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3102#ifdef TINYGLTF_USE_RAPIDJSON
3103 if (!o.IsObject()) {
3104 return false;
3105 }
3106 it = o.FindMember(member);
3107 return it != o.MemberEnd();
3108#else
3109 it = o.find(member);
3110 return it != o.end();
3111#endif
3112}
3113
3114const json &GetValue(json_const_iterator &it) {
3115#ifdef TINYGLTF_USE_RAPIDJSON
3116 return it->value;
3117#else
3118 return it.value();
3119#endif
3120}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003121
3122std::string JsonToString(const json &o, int spacing = -1) {
3123#ifdef TINYGLTF_USE_RAPIDJSON
3124 using namespace rapidjson;
3125 StringBuffer buffer;
3126 if (spacing == -1) {
3127 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003128 // TODO: Better error handling.
3129 // https://github.com/syoyo/tinygltf/issues/332
3130 if (!o.Accept(writer)) {
3131 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3132 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003133 } else {
3134 PrettyWriter<StringBuffer> writer(buffer);
3135 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003136 if (!o.Accept(writer)) {
3137 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3138 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003139 }
3140 return buffer.GetString();
3141#else
3142 return o.dump(spacing);
3143#endif
3144}
3145
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003146} // namespace
3147
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003148static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003149 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003150#ifdef TINYGLTF_USE_RAPIDJSON
3151 using rapidjson::Type;
3152 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003153 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003154 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003155 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003156 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003157 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003158 if (entry.Type() != NULL_TYPE)
3159 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003160 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003161 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003162 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003163 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003164 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003165 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003166 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003167 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003168 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003169 if (entry.Type() != NULL_TYPE)
3170 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003171 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003172 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003173 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003174 case Type::kStringType:
3175 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003176 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003177 case Type::kFalseType:
3178 case Type::kTrueType:
3179 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003180 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003181 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003182 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003183 int i = 0;
3184 GetInt(o, i);
3185 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003186 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003187 double d = 0.0;
3188 GetDouble(o, d);
3189 val = Value(d);
3190 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003191 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003192 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003193 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003194 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003195 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003196#else
3197 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003198 case json::value_t::object: {
3199 Value::Object value_object;
3200 for (auto it = o.begin(); it != o.end(); it++) {
3201 Value entry;
3202 ParseJsonAsValue(&entry, it.value());
3203 if (entry.Type() != NULL_TYPE)
3204 value_object.emplace(it.key(), std::move(entry));
3205 }
3206 if (value_object.size() > 0) val = Value(std::move(value_object));
3207 } break;
3208 case json::value_t::array: {
3209 Value::Array value_array;
3210 value_array.reserve(o.size());
3211 for (auto it = o.begin(); it != o.end(); it++) {
3212 Value entry;
3213 ParseJsonAsValue(&entry, it.value());
3214 if (entry.Type() != NULL_TYPE)
3215 value_array.emplace_back(std::move(entry));
3216 }
3217 if (value_array.size() > 0) val = Value(std::move(value_array));
3218 } break;
3219 case json::value_t::string:
3220 val = Value(o.get<std::string>());
3221 break;
3222 case json::value_t::boolean:
3223 val = Value(o.get<bool>());
3224 break;
3225 case json::value_t::number_integer:
3226 case json::value_t::number_unsigned:
3227 val = Value(static_cast<int>(o.get<int64_t>()));
3228 break;
3229 case json::value_t::number_float:
3230 val = Value(o.get<double>());
3231 break;
3232 case json::value_t::null:
3233 case json::value_t::discarded:
Christopher Sean Morrison0bfcb4f2022-02-23 10:02:49 -05003234 case json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003235 // default:
3236 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003237 }
3238#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003239 const bool isNotNull = val.Type() != NULL_TYPE;
3240
jrkooncecba5d6c2019-08-29 11:26:22 -05003241 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003242
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003243 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003244}
3245
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003246static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003247 json_const_iterator it;
3248 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003249 return false;
3250 }
3251
jrkooncecba5d6c2019-08-29 11:26:22 -05003252 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003253}
3254
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003255static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003256 const std::string &property,
3257 const bool required,
3258 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003259 json_const_iterator it;
3260 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003261 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003262 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003263 (*err) += "'" + property + "' property is missing";
3264 if (!parent_node.empty()) {
3265 (*err) += " in " + parent_node;
3266 }
3267 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003268 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003269 }
3270 return false;
3271 }
3272
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003273 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003274
3275 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003276 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003277#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003278 isBoolean = value.IsBool();
3279 if (isBoolean) {
3280 boolValue = value.GetBool();
3281 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003282#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003283 isBoolean = value.is_boolean();
3284 if (isBoolean) {
3285 boolValue = value.get<bool>();
3286 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003287#endif
3288 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003289 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003290 if (err) {
3291 (*err) += "'" + property + "' property is not a bool type.\n";
3292 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003293 }
3294 return false;
3295 }
3296
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003297 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003298 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003299 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003300
3301 return true;
3302}
3303
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003304static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3305 const std::string &property,
3306 const bool required,
3307 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003308 json_const_iterator it;
3309 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003310 if (required) {
3311 if (err) {
3312 (*err) += "'" + property + "' property is missing";
3313 if (!parent_node.empty()) {
3314 (*err) += " in " + parent_node;
3315 }
3316 (*err) += ".\n";
3317 }
3318 }
3319 return false;
3320 }
3321
jrkooncecba5d6c2019-08-29 11:26:22 -05003322 int intValue;
3323 bool isInt = GetInt(GetValue(it), intValue);
3324 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003325 if (required) {
3326 if (err) {
3327 (*err) += "'" + property + "' property is not an integer type.\n";
3328 }
3329 }
3330 return false;
3331 }
3332
3333 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003334 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003335 }
3336
3337 return true;
3338}
3339
3340static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3341 const std::string &property,
3342 const bool required,
3343 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003344 json_const_iterator it;
3345 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003346 if (required) {
3347 if (err) {
3348 (*err) += "'" + property + "' property is missing";
3349 if (!parent_node.empty()) {
3350 (*err) += " in " + parent_node;
3351 }
3352 (*err) += ".\n";
3353 }
3354 }
3355 return false;
3356 }
3357
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003358 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003359
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003360 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003361 bool isUValue;
3362#ifdef TINYGLTF_USE_RAPIDJSON
3363 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003364 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003365 uValue = value.GetUint();
3366 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003367 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003368 uValue = value.GetUint64();
3369 isUValue = true;
3370 }
3371#else
3372 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003373 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003374 uValue = value.get<size_t>();
3375 }
3376#endif
3377 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003378 if (required) {
3379 if (err) {
3380 (*err) += "'" + property + "' property is not a positive integer.\n";
3381 }
3382 }
3383 return false;
3384 }
3385
3386 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003387 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003388 }
3389
3390 return true;
3391}
3392
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003393static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003394 const std::string &property,
3395 const bool required,
3396 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003397 json_const_iterator it;
3398
3399 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003400 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003401 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003402 (*err) += "'" + property + "' property is missing";
3403 if (!parent_node.empty()) {
3404 (*err) += " in " + parent_node;
3405 }
3406 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003407 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003408 }
3409 return false;
3410 }
3411
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003412 double numberValue;
3413 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003414
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003415 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003416 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003417 if (err) {
3418 (*err) += "'" + property + "' property is not a number type.\n";
3419 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003420 }
3421 return false;
3422 }
3423
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003424 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003425 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003426 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003427
3428 return true;
3429}
3430
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003431static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003432 const json &o, const std::string &property,
3433 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003434 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003435 json_const_iterator it;
3436 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003437 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003438 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003439 (*err) += "'" + property + "' property is missing";
3440 if (!parent_node.empty()) {
3441 (*err) += " in " + parent_node;
3442 }
3443 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003444 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003445 }
3446 return false;
3447 }
3448
jrkooncecba5d6c2019-08-29 11:26:22 -05003449 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003450 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003451 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003452 (*err) += "'" + property + "' property is not an array";
3453 if (!parent_node.empty()) {
3454 (*err) += " in " + parent_node;
3455 }
3456 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003457 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003458 }
3459 return false;
3460 }
3461
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003462 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003463 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003464 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003465 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003466 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003467 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003468 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003469 if (err) {
3470 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003471 if (!parent_node.empty()) {
3472 (*err) += " in " + parent_node;
3473 }
3474 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003475 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003476 }
3477 return false;
3478 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003479 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003480 }
3481
3482 return true;
3483}
3484
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003485static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3486 const json &o,
3487 const std::string &property,
3488 bool required,
3489 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003490 json_const_iterator it;
3491 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003492 if (required) {
3493 if (err) {
3494 (*err) += "'" + property + "' property is missing";
3495 if (!parent_node.empty()) {
3496 (*err) += " in " + parent_node;
3497 }
3498 (*err) += ".\n";
3499 }
3500 }
3501 return false;
3502 }
3503
jrkooncecba5d6c2019-08-29 11:26:22 -05003504 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003505 if (required) {
3506 if (err) {
3507 (*err) += "'" + property + "' property is not an array";
3508 if (!parent_node.empty()) {
3509 (*err) += " in " + parent_node;
3510 }
3511 (*err) += ".\n";
3512 }
3513 }
3514 return false;
3515 }
3516
3517 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003518 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003519 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003520 int numberValue;
3521 bool isNumber = GetInt(*i, numberValue);
3522 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003523 if (required) {
3524 if (err) {
3525 (*err) += "'" + property + "' property is not an integer type.\n";
3526 if (!parent_node.empty()) {
3527 (*err) += " in " + parent_node;
3528 }
3529 (*err) += ".\n";
3530 }
3531 }
3532 return false;
3533 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003534 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003535 }
3536
3537 return true;
3538}
3539
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003540static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003541 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003542 const std::string &property, bool required,
3543 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003544 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003545 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003546 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003547 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003548 (*err) += "'" + property + "' property is missing";
3549 if (parent_node.empty()) {
3550 (*err) += ".\n";
3551 } else {
3552 (*err) += " in `" + parent_node + "'.\n";
3553 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003554 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003555 }
3556 return false;
3557 }
3558
jrkooncecba5d6c2019-08-29 11:26:22 -05003559 std::string strValue;
3560 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003561 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003562 if (err) {
3563 (*err) += "'" + property + "' property is not a string type.\n";
3564 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003565 }
3566 return false;
3567 }
3568
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003569 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003570 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003571 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003572
3573 return true;
3574}
3575
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003576static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3577 std::string *err, const json &o,
3578 const std::string &property,
3579 bool required,
3580 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003581 json_const_iterator it;
3582 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003583 if (required) {
3584 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003585 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003586 (*err) +=
3587 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003588 } else {
3589 (*err) += "'" + property + "' property is missing.\n";
3590 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003591 }
3592 }
3593 return false;
3594 }
3595
jrkooncecba5d6c2019-08-29 11:26:22 -05003596 const json &dict = GetValue(it);
3597
Luke San Antonio19894c72016-06-14 21:19:51 -04003598 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003599 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003600 if (required) {
3601 if (err) {
3602 (*err) += "'" + property + "' property is not an object.\n";
3603 }
3604 }
3605 return false;
3606 }
3607
3608 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003609
jrkooncecba5d6c2019-08-29 11:26:22 -05003610 json_const_iterator dictIt(ObjectBegin(dict));
3611 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003612
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003613 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003614 int intVal;
3615 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003616 if (required) {
3617 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003618 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003619 }
3620 }
3621 return false;
3622 }
3623
3624 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003625 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003626 }
3627 return true;
3628}
3629
Syoyo Fujita5b407452017-06-04 17:42:41 +09003630static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003631 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003632 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003633 json_const_iterator it;
3634 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003635 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003636 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003637 (*err) += "'" + property + "' property is missing. \n'";
3638 }
3639 }
3640 return false;
3641 }
3642
jrkooncecba5d6c2019-08-29 11:26:22 -05003643 const json &obj = GetValue(it);
3644
3645 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003646 if (required) {
3647 if (err) {
3648 (*err) += "'" + property + "' property is not a JSON object.\n";
3649 }
3650 }
3651 return false;
3652 }
3653
3654 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003655
jrkooncecba5d6c2019-08-29 11:26:22 -05003656 json_const_iterator it2(ObjectBegin(obj));
3657 json_const_iterator itEnd(ObjectEnd(obj));
3658 for (; it2 != itEnd; ++it2) {
3659 double numVal;
3660 if (GetNumber(GetValue(it2), numVal))
3661 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003662 }
3663
3664 return true;
3665}
3666
Selmar09d2ff12018-03-15 17:30:42 +01003667static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003668 const json &o, const std::string &prop,
3669 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003670 // A parameter value can either be a string or an array of either a boolean or
3671 // a number. Booleans of any kind aren't supported here. Granted, it
3672 // complicates the Parameter structure and breaks it semantically in the sense
3673 // that the client probably works off the assumption that if the string is
3674 // empty the vector is used, etc. Would a tagged union work?
3675 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3676 // Found string property.
3677 return true;
3678 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3679 false)) {
3680 // Found a number array.
3681 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003682 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02003683 param->has_number_value = true;
3684 return true;
Selmar09d2ff12018-03-15 17:30:42 +01003685 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3686 false)) {
3687 return true;
3688 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3689 return true;
3690 } else {
3691 if (required) {
3692 if (err) {
3693 (*err) += "parameter must be a string or number / number array.\n";
3694 }
3695 }
3696 return false;
3697 }
3698}
3699
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003700static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3701 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003702 (void)err;
3703
jrkooncecba5d6c2019-08-29 11:26:22 -05003704 json_const_iterator it;
3705 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003706 return false;
3707 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003708
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003709 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003710 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003711 return false;
3712 }
3713 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003714 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003715 json_const_iterator extEnd = ObjectEnd(obj);
3716 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003717 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003718 if (!IsObject(itObj)) continue;
3719 std::string key(GetKey(extIt));
3720 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003721 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003722 // create empty object so that an extension object is still of type
3723 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003724 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003725 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003726 }
Selmar09d2ff12018-03-15 17:30:42 +01003727 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003728 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003729 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003730 }
3731 return true;
3732}
3733
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003734static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3735 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003736 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3737 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3738 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003739 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003740
Selmar09d2ff12018-03-15 17:30:42 +01003741 ParseExtensionsProperty(&asset->extensions, err, o);
3742
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003743 // Unity exporter version is added as extra here
3744 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003745
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003746 if (store_original_json_for_extras_and_extensions) {
3747 {
3748 json_const_iterator it;
3749 if (FindMember(o, "extensions", it)) {
3750 asset->extensions_json_string = JsonToString(GetValue(it));
3751 }
3752 }
3753 {
3754 json_const_iterator it;
3755 if (FindMember(o, "extras", it)) {
3756 asset->extras_json_string = JsonToString(GetValue(it));
3757 }
3758 }
3759 }
3760
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003761 return true;
3762}
3763
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003764static bool ParseImage(Image *image, const int image_idx, std::string *err,
3765 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003766 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003767 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003768 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003769 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003770 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003771
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003772 // schema says oneOf [`bufferView`, `uri`]
3773 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003774 json_const_iterator it;
3775 bool hasBufferView = FindMember(o, "bufferView", it);
3776 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003777
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003778 ParseStringProperty(&image->name, err, o, "name", false);
3779
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003780 if (hasBufferView && hasURI) {
3781 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003782 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003783 (*err) +=
3784 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003785 "defined for image[" +
3786 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003787 }
3788 return false;
3789 }
3790
3791 if (!hasBufferView && !hasURI) {
3792 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003793 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3794 std::to_string(image_idx) + "] name = \"" + image->name +
3795 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003796 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003797 return false;
3798 }
3799
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003800 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003801 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003802
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003803 if (store_original_json_for_extras_and_extensions) {
3804 {
3805 json_const_iterator eit;
3806 if (FindMember(o, "extensions", eit)) {
3807 image->extensions_json_string = JsonToString(GetValue(eit));
3808 }
3809 }
3810 {
3811 json_const_iterator eit;
3812 if (FindMember(o, "extras", eit)) {
3813 image->extras_json_string = JsonToString(GetValue(eit));
3814 }
3815 }
3816 }
3817
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003818 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003819 int bufferView = -1;
3820 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003821 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003822 (*err) += "Failed to parse `bufferView` for image[" +
3823 std::to_string(image_idx) + "] name = \"" + image->name +
3824 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003825 }
3826 return false;
3827 }
3828
3829 std::string mime_type;
3830 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3831
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003832 int width = 0;
3833 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003834
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003835 int height = 0;
3836 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003837
3838 // Just only save some information here. Loading actual image data from
3839 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003840 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003841 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003842 image->width = width;
3843 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003844
3845 return true;
3846 }
3847
Syoyo Fujita246654a2018-03-21 20:32:22 +09003848 // Parse URI & Load image data.
3849
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003850 std::string uri;
3851 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003852 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3853 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003854 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3855 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003856 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003857 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003858 }
3859
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003860 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003861
Syoyo Fujita246654a2018-03-21 20:32:22 +09003862 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003863 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003864 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003865 (*err) += "Failed to decode 'uri' for image[" +
3866 std::to_string(image_idx) + "] name = [" + image->name +
3867 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003868 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003869 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003870 }
3871 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003872 // Assume external file
3873 // Keep texture path (for textures that cannot be decoded)
3874 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003875#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003876 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01003877#else
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003878 std::string decoded_uri = dlib::urldecode(uri);
3879 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3880 /* required */ false, /* required bytes */ 0,
3881 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003882 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003883 (*warn) += "Failed to load external 'uri' for image[" +
3884 std::to_string(image_idx) + "] name = [" + image->name +
3885 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003886 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003887 // If the image cannot be loaded, keep uri as image->uri.
3888 return true;
3889 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003890
Syoyo Fujita246654a2018-03-21 20:32:22 +09003891 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003892 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003893 (*warn) += "Image data is empty for image[" +
3894 std::to_string(image_idx) + "] name = [" + image->name +
3895 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003896 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003897 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003898 }
AlvaroBarua43172232022-09-11 00:41:43 +01003899#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003900 }
3901
Squareysff644d82018-03-13 22:36:18 +01003902 if (*LoadImageData == nullptr) {
3903 if (err) {
3904 (*err) += "No LoadImageData callback specified.\n";
3905 }
3906 return false;
3907 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003908 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003909 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003910}
3911
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003912static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003913 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003914 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003915 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003916 int sampler = -1;
3917 int source = -1;
3918 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003919
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003920 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003921
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003922 texture->sampler = sampler;
3923 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003924
Selmar Kokfa7022f2018-04-04 18:10:20 +02003925 ParseExtensionsProperty(&texture->extensions, err, o);
3926 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003927
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003928 if (store_original_json_for_extras_and_extensions) {
3929 {
3930 json_const_iterator it;
3931 if (FindMember(o, "extensions", it)) {
3932 texture->extensions_json_string = JsonToString(GetValue(it));
3933 }
3934 }
3935 {
3936 json_const_iterator it;
3937 if (FindMember(o, "extras", it)) {
3938 texture->extras_json_string = JsonToString(GetValue(it));
3939 }
3940 }
3941 }
3942
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003943 ParseStringProperty(&texture->name, err, o, "name", false);
3944
Syoyo Fujitabde70212016-02-07 17:38:17 +09003945 return true;
3946}
3947
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003948static bool ParseTextureInfo(
3949 TextureInfo *texinfo, std::string *err, const json &o,
3950 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003951 if (texinfo == nullptr) {
3952 return false;
3953 }
3954
3955 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3956 /* required */ true, "TextureInfo")) {
3957 return false;
3958 }
3959
3960 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3961
3962 ParseExtensionsProperty(&texinfo->extensions, err, o);
3963 ParseExtrasProperty(&texinfo->extras, o);
3964
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003965 if (store_original_json_for_extras_and_extensions) {
3966 {
3967 json_const_iterator it;
3968 if (FindMember(o, "extensions", it)) {
3969 texinfo->extensions_json_string = JsonToString(GetValue(it));
3970 }
3971 }
3972 {
3973 json_const_iterator it;
3974 if (FindMember(o, "extras", it)) {
3975 texinfo->extras_json_string = JsonToString(GetValue(it));
3976 }
3977 }
3978 }
3979
Syoyo Fujita046400b2019-07-24 19:26:48 +09003980 return true;
3981}
3982
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003983static bool ParseNormalTextureInfo(
3984 NormalTextureInfo *texinfo, std::string *err, const json &o,
3985 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003986 if (texinfo == nullptr) {
3987 return false;
3988 }
3989
3990 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3991 /* required */ true, "NormalTextureInfo")) {
3992 return false;
3993 }
3994
3995 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3996 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
3997
3998 ParseExtensionsProperty(&texinfo->extensions, err, o);
3999 ParseExtrasProperty(&texinfo->extras, o);
4000
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004001 if (store_original_json_for_extras_and_extensions) {
4002 {
4003 json_const_iterator it;
4004 if (FindMember(o, "extensions", it)) {
4005 texinfo->extensions_json_string = JsonToString(GetValue(it));
4006 }
4007 }
4008 {
4009 json_const_iterator it;
4010 if (FindMember(o, "extras", it)) {
4011 texinfo->extras_json_string = JsonToString(GetValue(it));
4012 }
4013 }
4014 }
4015
Syoyo Fujita046400b2019-07-24 19:26:48 +09004016 return true;
4017}
4018
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004019static bool ParseOcclusionTextureInfo(
4020 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
4021 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004022 if (texinfo == nullptr) {
4023 return false;
4024 }
4025
4026 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4027 /* required */ true, "NormalTextureInfo")) {
4028 return false;
4029 }
4030
4031 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4032 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4033
4034 ParseExtensionsProperty(&texinfo->extensions, err, o);
4035 ParseExtrasProperty(&texinfo->extras, o);
4036
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004037 if (store_original_json_for_extras_and_extensions) {
4038 {
4039 json_const_iterator it;
4040 if (FindMember(o, "extensions", it)) {
4041 texinfo->extensions_json_string = JsonToString(GetValue(it));
4042 }
4043 }
4044 {
4045 json_const_iterator it;
4046 if (FindMember(o, "extras", it)) {
4047 texinfo->extras_json_string = JsonToString(GetValue(it));
4048 }
4049 }
4050 }
4051
Syoyo Fujita046400b2019-07-24 19:26:48 +09004052 return true;
4053}
4054
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004055static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004056 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004057 FsCallbacks *fs, const std::string &basedir,
4058 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004059 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004060 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004061 size_t byteLength;
4062 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4063 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004064 return false;
4065 }
4066
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004067 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004068 buffer->uri.clear();
4069 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004070
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004071 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004072 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004073 if (err) {
4074 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4075 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004076 }
4077
jrkooncecba5d6c2019-08-29 11:26:22 -05004078 json_const_iterator type;
4079 if (FindMember(o, "type", type)) {
4080 std::string typeStr;
4081 if (GetString(GetValue(type), typeStr)) {
4082 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004083 // buffer.type = "arraybuffer";
4084 }
4085 }
4086 }
4087
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004088 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004089 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004090 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004091 // First try embedded data URI.
4092 if (IsDataURI(buffer->uri)) {
4093 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004094 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004095 true)) {
4096 if (err) {
4097 (*err) +=
4098 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4099 }
4100 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004101 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004102 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004103 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004104 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004105 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004106 decoded_uri, basedir, /* required */ true,
4107 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004108 return false;
4109 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004110 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004111 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004112 // load data from (embedded) binary data
4113
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004114 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004115 if (err) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09004116 (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004117 }
4118 return false;
4119 }
4120
4121 if (byteLength > bin_size) {
4122 if (err) {
4123 std::stringstream ss;
4124 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004125 "`byteLength' = "
4126 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004127 (*err) += ss.str();
4128 }
4129 return false;
4130 }
4131
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004132 // Read buffer data
4133 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004134 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004135 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004136
4137 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004138 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004139 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004140 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4141 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004142 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004143 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004144 }
4145 return false;
4146 }
4147 } else {
4148 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004149 std::string decoded_uri = dlib::urldecode(buffer->uri);
4150 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4151 basedir, /* required */ true, byteLength,
4152 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004153 return false;
4154 }
4155 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004156 }
4157
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004158 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004159
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004160 ParseExtensionsProperty(&buffer->extensions, err, o);
4161 ParseExtrasProperty(&buffer->extras, o);
4162
4163 if (store_original_json_for_extras_and_extensions) {
4164 {
4165 json_const_iterator it;
4166 if (FindMember(o, "extensions", it)) {
4167 buffer->extensions_json_string = JsonToString(GetValue(it));
4168 }
4169 }
4170 {
4171 json_const_iterator it;
4172 if (FindMember(o, "extras", it)) {
4173 buffer->extras_json_string = JsonToString(GetValue(it));
4174 }
4175 }
4176 }
4177
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004178 return true;
4179}
4180
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004181static bool ParseBufferView(
4182 BufferView *bufferView, std::string *err, const json &o,
4183 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004184 int buffer = -1;
4185 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004186 return false;
4187 }
4188
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004189 size_t byteOffset = 0;
4190 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004191
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004192 size_t byteLength = 1;
4193 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4194 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004195 return false;
4196 }
4197
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004198 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004199 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004200 // Spec says: When byteStride of referenced bufferView is not defined, it
4201 // means that accessor elements are tightly packed, i.e., effective stride
4202 // equals the size of the element.
4203 // We cannot determine the actual byteStride until Accessor are parsed, thus
4204 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4205 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004206 }
4207
4208 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4209 if (err) {
4210 std::stringstream ss;
4211 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4212 "4 : "
4213 << byteStride << std::endl;
4214
4215 (*err) += ss.str();
4216 }
4217 return false;
4218 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004219
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004220 int target = 0;
4221 ParseIntegerProperty(&target, err, o, "target", false);
4222 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4223 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004224 // OK
4225 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004226 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004227 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004228 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004229
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004230 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004231
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004232 ParseExtensionsProperty(&bufferView->extensions, err, o);
4233 ParseExtrasProperty(&bufferView->extras, o);
4234
4235 if (store_original_json_for_extras_and_extensions) {
4236 {
4237 json_const_iterator it;
4238 if (FindMember(o, "extensions", it)) {
4239 bufferView->extensions_json_string = JsonToString(GetValue(it));
4240 }
4241 }
4242 {
4243 json_const_iterator it;
4244 if (FindMember(o, "extras", it)) {
4245 bufferView->extras_json_string = JsonToString(GetValue(it));
4246 }
4247 }
4248 }
4249
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004250 bufferView->buffer = buffer;
4251 bufferView->byteOffset = byteOffset;
4252 bufferView->byteLength = byteLength;
4253 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004254 return true;
4255}
4256
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004257static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4258 const json &o) {
4259 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004260
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004261 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004262 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4263 return false;
4264 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004265
jrkooncecba5d6c2019-08-29 11:26:22 -05004266 json_const_iterator indices_iterator;
4267 json_const_iterator values_iterator;
4268 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004269 (*err) = "the sparse object of this accessor doesn't have indices";
4270 return false;
4271 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004272
jrkooncecba5d6c2019-08-29 11:26:22 -05004273 if (!FindMember(o, "values", values_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004274 (*err) = "the sparse object ob ths accessor doesn't have values";
4275 return false;
4276 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004277
jrkooncecba5d6c2019-08-29 11:26:22 -05004278 const json &indices_obj = GetValue(indices_iterator);
4279 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004280
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004281 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004282 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4283 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004284 return false;
4285 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004286 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004287 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004288 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004289 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004290 return false;
4291 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004292
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004293 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004294 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004295 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004296 return false;
4297 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004298 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004299 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004300
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004301 accessor->sparse.count = count;
4302 accessor->sparse.indices.bufferView = indices_buffer_view;
4303 accessor->sparse.indices.byteOffset = indices_byte_offset;
4304 accessor->sparse.indices.componentType = component_type;
4305 accessor->sparse.values.bufferView = values_buffer_view;
4306 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004307
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004308 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004309}
4310
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004311static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4312 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004313 int bufferView = -1;
4314 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004315
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004316 size_t byteOffset = 0;
4317 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004318
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004319 bool normalized = false;
4320 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4321
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004322 size_t componentType = 0;
4323 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4324 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004325 return false;
4326 }
4327
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004328 size_t count = 0;
4329 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004330 return false;
4331 }
4332
4333 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004334 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004335 return false;
4336 }
4337
4338 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004339 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004340 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004341 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004342 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004343 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004344 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004345 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004346 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004347 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004348 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004349 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004350 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004351 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004352 } else {
4353 std::stringstream ss;
4354 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004355 if (err) {
4356 (*err) += ss.str();
4357 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004358 return false;
4359 }
4360
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004361 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004362
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004363 accessor->minValues.clear();
4364 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004365 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4366 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004367
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004368 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4369 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004370
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004371 accessor->count = count;
4372 accessor->bufferView = bufferView;
4373 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004374 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004375 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004376 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4377 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004378 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004379 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004380 } else {
4381 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004382 ss << "Invalid `componentType` in accessor. Got " << componentType
4383 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004384 if (err) {
4385 (*err) += ss.str();
4386 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004387 return false;
4388 }
4389 }
4390
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004391 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004392 ParseExtrasProperty(&(accessor->extras), o);
4393
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004394 if (store_original_json_for_extras_and_extensions) {
4395 {
4396 json_const_iterator it;
4397 if (FindMember(o, "extensions", it)) {
4398 accessor->extensions_json_string = JsonToString(GetValue(it));
4399 }
4400 }
4401 {
4402 json_const_iterator it;
4403 if (FindMember(o, "extras", it)) {
4404 accessor->extras_json_string = JsonToString(GetValue(it));
4405 }
4406 }
4407 }
4408
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004409 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004410 json_const_iterator iterator;
4411 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004412 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004413 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004414 }
4415
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004416 return true;
4417}
4418
Alex Wood7319db72019-01-24 15:38:16 -05004419#ifdef TINYGLTF_ENABLE_DRACO
4420
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004421static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4422 std::vector<uint8_t> &outBuffer) {
4423 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004424 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004425 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4426 outBuffer.size());
4427 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004428 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004429 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4430 const draco::Mesh::Face &face = mesh->face(f);
4431 if (componentSize == 2) {
4432 uint16_t indices[3] = {(uint16_t)face[0].value(),
4433 (uint16_t)face[1].value(),
4434 (uint16_t)face[2].value()};
4435 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4436 faceStride);
4437 } else {
4438 uint8_t indices[3] = {(uint8_t)face[0].value(),
4439 (uint8_t)face[1].value(),
4440 (uint8_t)face[2].value()};
4441 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4442 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004443 }
4444 }
4445 }
4446}
4447
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004448template <typename T>
4449static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4450 const draco::PointAttribute *pAttribute,
4451 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004452 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004453 T values[4] = {0, 0, 0, 0};
4454 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004455 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004456 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4457 values))
Alex Wood7319db72019-01-24 15:38:16 -05004458 return false;
4459
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004460 memcpy(outBuffer.data() + byteOffset, &values[0],
4461 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004462 byteOffset += sizeof(T) * pAttribute->num_components();
4463 }
4464
4465 return true;
4466}
4467
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004468static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4469 const draco::PointAttribute *pAttribute,
4470 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004471 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004472 switch (componentType) {
4473 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4474 decodeResult =
4475 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4476 break;
4477 case TINYGLTF_COMPONENT_TYPE_BYTE:
4478 decodeResult =
4479 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4480 break;
4481 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4482 decodeResult =
4483 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4484 break;
4485 case TINYGLTF_COMPONENT_TYPE_SHORT:
4486 decodeResult =
4487 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4488 break;
4489 case TINYGLTF_COMPONENT_TYPE_INT:
4490 decodeResult =
4491 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4492 break;
4493 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4494 decodeResult =
4495 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4496 break;
4497 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4498 decodeResult =
4499 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4500 break;
4501 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4502 decodeResult =
4503 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4504 break;
4505 default:
4506 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004507 }
4508
4509 return decodeResult;
4510}
4511
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004512static bool ParseDracoExtension(Primitive *primitive, Model *model,
4513 std::string *err,
4514 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004515 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004516 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004517 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004518 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004519 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004520
4521 auto attributesObject = attributesValue.Get<Value::Object>();
4522 int bufferView = bufferViewValue.Get<int>();
4523
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004524 BufferView &view = model->bufferViews[bufferView];
4525 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004526 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004527 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004528 view.dracoDecoded = true;
4529
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004530 const char *bufferViewData =
4531 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004532 size_t bufferViewSize = view.byteLength;
4533
4534 // decode draco
4535 draco::DecoderBuffer decoderBuffer;
4536 decoderBuffer.Init(bufferViewData, bufferViewSize);
4537 draco::Decoder decoder;
4538 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4539 if (!decodeResult.ok()) {
4540 return false;
4541 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004542 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004543
4544 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004545 if (primitive->indices >= 0) {
4546 int32_t componentSize = GetComponentSizeInBytes(
4547 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004548 Buffer decodedIndexBuffer;
4549 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4550
4551 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4552
4553 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4554
4555 BufferView decodedIndexBufferView;
4556 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004557 decodedIndexBufferView.byteLength =
4558 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004559 decodedIndexBufferView.byteOffset = 0;
4560 decodedIndexBufferView.byteStride = 0;
4561 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4562 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4563
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004564 model->accessors[primitive->indices].bufferView =
4565 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004566 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004567 }
4568
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004569 for (const auto &attribute : attributesObject) {
4570 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004571 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004572 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004573
4574 int dracoAttributeIndex = attribute.second.Get<int>();
4575 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004576 const auto componentType =
4577 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004578
4579 // Create a new buffer for this decoded buffer
4580 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004581 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4582 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004583 decodedBuffer.data.resize(bufferSize);
4584
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004585 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4586 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004587 return false;
4588
4589 model->buffers.emplace_back(std::move(decodedBuffer));
4590
4591 BufferView decodedBufferView;
4592 decodedBufferView.buffer = int(model->buffers.size() - 1);
4593 decodedBufferView.byteLength = bufferSize;
4594 decodedBufferView.byteOffset = pAttribute->byte_offset();
4595 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004596 decodedBufferView.target = primitive->indices >= 0
4597 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4598 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004599 model->bufferViews.emplace_back(std::move(decodedBufferView));
4600
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004601 model->accessors[primitiveAttribute->second].bufferView =
4602 int(model->bufferViews.size() - 1);
4603 model->accessors[primitiveAttribute->second].count =
4604 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004605 }
4606
4607 return true;
4608}
4609#endif
4610
4611static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004612 const json &o,
4613 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004614 int material = -1;
4615 ParseIntegerProperty(&material, err, o, "material", false);
4616 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004617
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004618 int mode = TINYGLTF_MODE_TRIANGLES;
4619 ParseIntegerProperty(&mode, err, o, "mode", false);
4620 primitive->mode = mode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004621
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004622 int indices = -1;
4623 ParseIntegerProperty(&indices, err, o, "indices", false);
4624 primitive->indices = indices;
4625 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4626 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004627 return false;
4628 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004629
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004630 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004631 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004632 if (FindMember(o, "targets", targetsObject) &&
4633 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004634 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4635 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4636 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004637 std::map<std::string, int> targetAttribues;
4638
jrkooncecba5d6c2019-08-29 11:26:22 -05004639 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004640 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004641 json_const_iterator dictIt(ObjectBegin(dict));
4642 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004643
jrkooncecba5d6c2019-08-29 11:26:22 -05004644 for (; dictIt != dictItEnd; ++dictIt) {
4645 int iVal;
4646 if (GetInt(GetValue(dictIt), iVal))
4647 targetAttribues[GetKey(dictIt)] = iVal;
4648 }
4649 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004650 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004651 }
4652 }
4653
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004654 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004655 ParseExtensionsProperty(&primitive->extensions, err, o);
4656
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004657 if (store_original_json_for_extras_and_extensions) {
4658 {
4659 json_const_iterator it;
4660 if (FindMember(o, "extensions", it)) {
4661 primitive->extensions_json_string = JsonToString(GetValue(it));
4662 }
4663 }
4664 {
4665 json_const_iterator it;
4666 if (FindMember(o, "extras", it)) {
4667 primitive->extras_json_string = JsonToString(GetValue(it));
4668 }
4669 }
4670 }
4671
Alex Wood7319db72019-01-24 15:38:16 -05004672#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004673 auto dracoExtension =
4674 primitive->extensions.find("KHR_draco_mesh_compression");
4675 if (dracoExtension != primitive->extensions.end()) {
4676 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004677 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004678#else
4679 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004680#endif
4681
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004682 return true;
4683}
4684
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004685static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4686 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004687 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004688
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004689 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004690 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004691 if (FindMember(o, "primitives", primObject) &&
4692 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004693 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4694 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4695 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004696 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004697 if (ParsePrimitive(&primitive, model, err, *i,
4698 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004699 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004700 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004701 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004702 }
4703 }
4704
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004705 // Should probably check if has targets and if dimensions fit
4706 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4707
Selmar09d2ff12018-03-15 17:30:42 +01004708 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004709 ParseExtrasProperty(&(mesh->extras), o);
4710
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004711 if (store_original_json_for_extras_and_extensions) {
4712 {
4713 json_const_iterator it;
4714 if (FindMember(o, "extensions", it)) {
4715 mesh->extensions_json_string = JsonToString(GetValue(it));
4716 }
4717 }
4718 {
4719 json_const_iterator it;
4720 if (FindMember(o, "extras", it)) {
4721 mesh->extras_json_string = JsonToString(GetValue(it));
4722 }
4723 }
4724 }
4725
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004726 return true;
4727}
4728
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004729static bool ParseNode(Node *node, std::string *err, const json &o,
4730 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004731 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004732
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004733 int skin = -1;
4734 ParseIntegerProperty(&skin, err, o, "skin", false);
4735 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004736
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004737 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004738 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004739 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4740 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4741 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4742 }
4743
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004744 int camera = -1;
4745 ParseIntegerProperty(&camera, err, o, "camera", false);
4746 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004747
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004748 int mesh = -1;
4749 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4750 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004751
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004752 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004753 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004754
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004755 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4756
Selmar09d2ff12018-03-15 17:30:42 +01004757 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004758 ParseExtrasProperty(&(node->extras), o);
4759
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004760 if (store_original_json_for_extras_and_extensions) {
4761 {
4762 json_const_iterator it;
4763 if (FindMember(o, "extensions", it)) {
4764 node->extensions_json_string = JsonToString(GetValue(it));
4765 }
4766 }
4767 {
4768 json_const_iterator it;
4769 if (FindMember(o, "extras", it)) {
4770 node->extras_json_string = JsonToString(GetValue(it));
4771 }
4772 }
4773 }
4774
Emanuel Schrade186322b2017-11-06 11:14:41 +01004775 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004776}
4777
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004778static bool ParsePbrMetallicRoughness(
4779 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4780 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004781 if (pbr == nullptr) {
4782 return false;
4783 }
4784
4785 std::vector<double> baseColorFactor;
4786 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4787 /* required */ false)) {
4788 if (baseColorFactor.size() != 4) {
4789 if (err) {
4790 (*err) +=
4791 "Array length of `baseColorFactor` parameter in "
4792 "pbrMetallicRoughness must be 4, but got " +
4793 std::to_string(baseColorFactor.size()) + "\n";
4794 }
4795 return false;
4796 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004797 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004798 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004799
4800 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004801 json_const_iterator it;
4802 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004803 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4804 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004805 }
4806 }
4807
4808 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004809 json_const_iterator it;
4810 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004811 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4812 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004813 }
4814 }
4815
4816 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4817 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4818
4819 ParseExtensionsProperty(&pbr->extensions, err, o);
4820 ParseExtrasProperty(&pbr->extras, o);
4821
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004822 if (store_original_json_for_extras_and_extensions) {
4823 {
4824 json_const_iterator it;
4825 if (FindMember(o, "extensions", it)) {
4826 pbr->extensions_json_string = JsonToString(GetValue(it));
4827 }
4828 }
4829 {
4830 json_const_iterator it;
4831 if (FindMember(o, "extras", it)) {
4832 pbr->extras_json_string = JsonToString(GetValue(it));
4833 }
4834 }
4835 }
4836
Syoyo Fujita046400b2019-07-24 19:26:48 +09004837 return true;
4838}
4839
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004840static bool ParseMaterial(Material *material, std::string *err, const json &o,
4841 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004842 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4843
Syoyo Fujitaff515702019-08-24 16:29:14 +09004844 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4845 "emissiveFactor",
4846 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004847 if (material->emissiveFactor.size() != 3) {
4848 if (err) {
4849 (*err) +=
4850 "Array length of `emissiveFactor` parameter in "
4851 "material must be 3, but got " +
4852 std::to_string(material->emissiveFactor.size()) + "\n";
4853 }
4854 return false;
4855 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004856 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004857 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004858 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004859 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004860
4861 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4862 /* required */ false);
4863 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4864 /* required */ false);
4865 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4866 /* required */ false);
4867
4868 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004869 json_const_iterator it;
4870 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004871 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004872 GetValue(it),
4873 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004874 }
4875 }
4876
4877 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004878 json_const_iterator it;
4879 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004880 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4881 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004882 }
4883 }
4884
4885 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004886 json_const_iterator it;
4887 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004888 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4889 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004890 }
4891 }
4892
4893 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004894 json_const_iterator it;
4895 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004896 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4897 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004898 }
4899 }
4900
4901 // Old code path. For backward compatibility, we still store material values
4902 // as Parameter. This will create duplicated information for
4903 // example(pbrMetallicRoughness), but should be neglible in terms of memory
4904 // consumption.
4905 // TODO(syoyo): Remove in the next major release.
4906 material->values.clear();
4907 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004908
jrkooncecba5d6c2019-08-29 11:26:22 -05004909 json_const_iterator it(ObjectBegin(o));
4910 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004911
jrkooncecba5d6c2019-08-29 11:26:22 -05004912 for (; it != itEnd; ++it) {
4913 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004914 if (key == "pbrMetallicRoughness") {
4915 if (IsObject(GetValue(it))) {
4916 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004917
jrkoonce06c30c42019-09-03 15:56:48 -05004918 json_const_iterator itVal(ObjectBegin(values_object));
4919 json_const_iterator itValEnd(ObjectEnd(values_object));
4920
4921 for (; itVal != itValEnd; ++itVal) {
4922 Parameter param;
4923 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004924 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004925 material->values.emplace(GetKey(itVal), std::move(param));
4926 }
4927 }
4928 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004929 } else if (key == "extensions" || key == "extras") {
4930 // done later, skip, otherwise poorly parsed contents will be saved in the
4931 // parametermap and serialized again later
4932 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004933 Parameter param;
4934 if (ParseParameterProperty(&param, err, o, key, false)) {
4935 // names of materials have already been parsed. Putting it in this map
4936 // doesn't correctly reflext the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004937 if (key != "name")
4938 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004939 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004940 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004941 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004942
Syoyo Fujita046400b2019-07-24 19:26:48 +09004943 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004944 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004945 ParseExtrasProperty(&(material->extras), o);
4946
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004947 if (store_original_json_for_extras_and_extensions) {
4948 {
4949 json_const_iterator eit;
4950 if (FindMember(o, "extensions", eit)) {
4951 material->extensions_json_string = JsonToString(GetValue(eit));
4952 }
4953 }
4954 {
4955 json_const_iterator eit;
4956 if (FindMember(o, "extras", eit)) {
4957 material->extras_json_string = JsonToString(GetValue(eit));
4958 }
4959 }
4960 }
4961
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004962 return true;
4963}
4964
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004965static bool ParseAnimationChannel(
4966 AnimationChannel *channel, std::string *err, const json &o,
4967 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004968 int samplerIndex = -1;
4969 int targetIndex = -1;
4970 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4971 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004972 if (err) {
4973 (*err) += "`sampler` field is missing in animation channels\n";
4974 }
4975 return false;
4976 }
4977
jrkooncecba5d6c2019-08-29 11:26:22 -05004978 json_const_iterator targetIt;
4979 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4980 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004981
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004982 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004983 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004984 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004985 }
4986 return false;
4987 }
4988
4989 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4990 true)) {
4991 if (err) {
4992 (*err) += "`path` field is missing in animation.channels.target\n";
4993 }
4994 return false;
4995 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004996 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4997 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01004998 json_const_iterator it;
4999 if (FindMember(target_object, "extensions", it)) {
5000 channel->target_extensions_json_string = JsonToString(GetValue(it));
5001 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005002 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005003 }
5004
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005005 channel->sampler = samplerIndex;
5006 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005007
Selmar Kok4e2988e2019-08-16 14:08:08 +02005008 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005009 ParseExtrasProperty(&(channel->extras), o);
5010
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005011 if (store_original_json_for_extras_and_extensions) {
5012 {
5013 json_const_iterator it;
5014 if (FindMember(o, "extensions", it)) {
5015 channel->extensions_json_string = JsonToString(GetValue(it));
5016 }
5017 }
5018 {
5019 json_const_iterator it;
5020 if (FindMember(o, "extras", it)) {
5021 channel->extras_json_string = JsonToString(GetValue(it));
5022 }
5023 }
5024 }
5025
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005026 return true;
5027}
5028
5029static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005030 const json &o,
5031 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005032 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005033 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005034 if (FindMember(o, "channels", channelsIt) &&
5035 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005036 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
5037 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
5038 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005039 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005040 if (ParseAnimationChannel(
5041 &channel, err, *i,
5042 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005043 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005044 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005045 }
5046 }
5047 }
5048 }
5049
5050 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005051 json_const_iterator samplerIt;
5052 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5053 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005054
jrkooncecba5d6c2019-08-29 11:26:22 -05005055 json_const_array_iterator it = ArrayBegin(sampler_array);
5056 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005057
jrkooncecba5d6c2019-08-29 11:26:22 -05005058 for (; it != itEnd; ++it) {
5059 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005060
5061 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005062 int inputIndex = -1;
5063 int outputIndex = -1;
5064 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005065 if (err) {
5066 (*err) += "`input` field is missing in animation.sampler\n";
5067 }
5068 return false;
5069 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005070 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5071 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005072 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005073 if (err) {
5074 (*err) += "`output` field is missing in animation.sampler\n";
5075 }
5076 return false;
5077 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005078 sampler.input = inputIndex;
5079 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005080 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005081 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005082
5083 if (store_original_json_for_extras_and_extensions) {
5084 {
5085 json_const_iterator eit;
5086 if (FindMember(o, "extensions", eit)) {
5087 sampler.extensions_json_string = JsonToString(GetValue(eit));
5088 }
5089 }
5090 {
5091 json_const_iterator eit;
5092 if (FindMember(o, "extras", eit)) {
5093 sampler.extras_json_string = JsonToString(GetValue(eit));
5094 }
5095 }
5096 }
5097
jrkooncecba5d6c2019-08-29 11:26:22 -05005098 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005099 }
5100 }
5101 }
5102
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005103 ParseStringProperty(&animation->name, err, o, "name", false);
5104
Selmar Kok4e2988e2019-08-16 14:08:08 +02005105 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005106 ParseExtrasProperty(&(animation->extras), o);
5107
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005108 if (store_original_json_for_extras_and_extensions) {
5109 {
5110 json_const_iterator it;
5111 if (FindMember(o, "extensions", it)) {
5112 animation->extensions_json_string = JsonToString(GetValue(it));
5113 }
5114 }
5115 {
5116 json_const_iterator it;
5117 if (FindMember(o, "extras", it)) {
5118 animation->extras_json_string = JsonToString(GetValue(it));
5119 }
5120 }
5121 }
5122
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005123 return true;
5124}
5125
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005126static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5127 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005128 ParseStringProperty(&sampler->name, err, o, "name", false);
5129
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005130 int minFilter = -1;
5131 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005132 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5133 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005134 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005135 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5136 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5137 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5138 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005139 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5140 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005141
5142 // TODO(syoyo): Check the value is alloed one.
5143 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005144
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005145 sampler->minFilter = minFilter;
5146 sampler->magFilter = magFilter;
5147 sampler->wrapS = wrapS;
5148 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005149 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005150
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005151 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005152 ParseExtrasProperty(&(sampler->extras), o);
5153
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005154 if (store_original_json_for_extras_and_extensions) {
5155 {
5156 json_const_iterator it;
5157 if (FindMember(o, "extensions", it)) {
5158 sampler->extensions_json_string = JsonToString(GetValue(it));
5159 }
5160 }
5161 {
5162 json_const_iterator it;
5163 if (FindMember(o, "extras", it)) {
5164 sampler->extras_json_string = JsonToString(GetValue(it));
5165 }
5166 }
5167 }
5168
Syoyo Fujitac2615632016-06-19 21:56:06 +09005169 return true;
5170}
5171
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005172static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5173 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005174 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005175
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005176 std::vector<int> joints;
5177 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005178 return false;
5179 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005180 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005181
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005182 int skeleton = -1;
5183 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5184 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005185
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005186 int invBind = -1;
5187 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5188 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005189
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005190 ParseExtensionsProperty(&(skin->extensions), err, o);
5191 ParseExtrasProperty(&(skin->extras), o);
5192
5193 if (store_original_json_for_extras_and_extensions) {
5194 {
5195 json_const_iterator it;
5196 if (FindMember(o, "extensions", it)) {
5197 skin->extensions_json_string = JsonToString(GetValue(it));
5198 }
5199 }
5200 {
5201 json_const_iterator it;
5202 if (FindMember(o, "extras", it)) {
5203 skin->extras_json_string = JsonToString(GetValue(it));
5204 }
5205 }
5206 }
5207
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005208 return true;
5209}
5210
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005211static bool ParsePerspectiveCamera(
5212 PerspectiveCamera *camera, std::string *err, const json &o,
5213 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005214 double yfov = 0.0;
5215 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5216 return false;
5217 }
5218
5219 double znear = 0.0;
5220 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5221 "PerspectiveCamera")) {
5222 return false;
5223 }
5224
5225 double aspectRatio = 0.0; // = invalid
5226 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5227 "PerspectiveCamera");
5228
5229 double zfar = 0.0; // = invalid
5230 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5231
Selmar Kok31cb7f92018-10-03 15:39:05 +02005232 camera->aspectRatio = aspectRatio;
5233 camera->zfar = zfar;
5234 camera->yfov = yfov;
5235 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005236
Selmar09d2ff12018-03-15 17:30:42 +01005237 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005238 ParseExtrasProperty(&(camera->extras), o);
5239
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005240 if (store_original_json_for_extras_and_extensions) {
5241 {
5242 json_const_iterator it;
5243 if (FindMember(o, "extensions", it)) {
5244 camera->extensions_json_string = JsonToString(GetValue(it));
5245 }
5246 }
5247 {
5248 json_const_iterator it;
5249 if (FindMember(o, "extras", it)) {
5250 camera->extras_json_string = JsonToString(GetValue(it));
5251 }
5252 }
5253 }
5254
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005255 // TODO(syoyo): Validate parameter values.
5256
5257 return true;
5258}
5259
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005260static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5261 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005262 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5263 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005264
Johan Bowald52936a02019-07-17 09:06:45 +02005265 ParseExtensionsProperty(&light->extensions, err, o);
5266 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005267
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005268 if (store_original_json_for_extras_and_extensions) {
5269 {
5270 json_const_iterator it;
5271 if (FindMember(o, "extensions", it)) {
5272 light->extensions_json_string = JsonToString(GetValue(it));
5273 }
5274 }
5275 {
5276 json_const_iterator it;
5277 if (FindMember(o, "extras", it)) {
5278 light->extras_json_string = JsonToString(GetValue(it));
5279 }
5280 }
5281 }
5282
Johan Bowald52936a02019-07-17 09:06:45 +02005283 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005284
Johan Bowald52936a02019-07-17 09:06:45 +02005285 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005286}
5287
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005288static bool ParseOrthographicCamera(
5289 OrthographicCamera *camera, std::string *err, const json &o,
5290 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005291 double xmag = 0.0;
5292 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5293 return false;
5294 }
5295
5296 double ymag = 0.0;
5297 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5298 return false;
5299 }
5300
5301 double zfar = 0.0;
5302 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5303 return false;
5304 }
5305
5306 double znear = 0.0;
5307 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5308 "OrthographicCamera")) {
5309 return false;
5310 }
5311
Selmar09d2ff12018-03-15 17:30:42 +01005312 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005313 ParseExtrasProperty(&(camera->extras), o);
5314
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005315 if (store_original_json_for_extras_and_extensions) {
5316 {
5317 json_const_iterator it;
5318 if (FindMember(o, "extensions", it)) {
5319 camera->extensions_json_string = JsonToString(GetValue(it));
5320 }
5321 }
5322 {
5323 json_const_iterator it;
5324 if (FindMember(o, "extras", it)) {
5325 camera->extras_json_string = JsonToString(GetValue(it));
5326 }
5327 }
5328 }
5329
Selmar Kok31cb7f92018-10-03 15:39:05 +02005330 camera->xmag = xmag;
5331 camera->ymag = ymag;
5332 camera->zfar = zfar;
5333 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005334
5335 // TODO(syoyo): Validate parameter values.
5336
5337 return true;
5338}
5339
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005340static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5341 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005342 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5343 return false;
5344 }
5345
5346 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005347 json_const_iterator orthoIt;
5348 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005349 if (err) {
5350 std::stringstream ss;
5351 ss << "Orhographic camera description not found." << std::endl;
5352 (*err) += ss.str();
5353 }
5354 return false;
5355 }
5356
jrkooncecba5d6c2019-08-29 11:26:22 -05005357 const json &v = GetValue(orthoIt);
5358 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005359 if (err) {
5360 std::stringstream ss;
5361 ss << "\"orthographic\" is not a JSON object." << std::endl;
5362 (*err) += ss.str();
5363 }
5364 return false;
5365 }
5366
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005367 if (!ParseOrthographicCamera(
5368 &camera->orthographic, err, v,
5369 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005370 return false;
5371 }
5372 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005373 json_const_iterator perspIt;
5374 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005375 if (err) {
5376 std::stringstream ss;
5377 ss << "Perspective camera description not found." << std::endl;
5378 (*err) += ss.str();
5379 }
5380 return false;
5381 }
5382
jrkooncecba5d6c2019-08-29 11:26:22 -05005383 const json &v = GetValue(perspIt);
5384 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005385 if (err) {
5386 std::stringstream ss;
5387 ss << "\"perspective\" is not a JSON object." << std::endl;
5388 (*err) += ss.str();
5389 }
5390 return false;
5391 }
5392
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005393 if (!ParsePerspectiveCamera(
5394 &camera->perspective, err, v,
5395 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005396 return false;
5397 }
5398 } else {
5399 if (err) {
5400 std::stringstream ss;
5401 ss << "Invalid camera type: \"" << camera->type
5402 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5403 (*err) += ss.str();
5404 }
5405 return false;
5406 }
5407
5408 ParseStringProperty(&camera->name, err, o, "name", false);
5409
Selmar09d2ff12018-03-15 17:30:42 +01005410 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005411 ParseExtrasProperty(&(camera->extras), o);
5412
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005413 if (store_original_json_for_extras_and_extensions) {
5414 {
5415 json_const_iterator it;
5416 if (FindMember(o, "extensions", it)) {
5417 camera->extensions_json_string = JsonToString(GetValue(it));
5418 }
5419 }
5420 {
5421 json_const_iterator it;
5422 if (FindMember(o, "extras", it)) {
5423 camera->extras_json_string = JsonToString(GetValue(it));
5424 }
5425 }
5426 }
5427
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005428 return true;
5429}
5430
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005431static bool ParseLight(Light *light, std::string *err, const json &o,
5432 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005433 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5434 return false;
5435 }
5436
5437 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005438 json_const_iterator spotIt;
5439 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005440 if (err) {
5441 std::stringstream ss;
5442 ss << "Spot light description not found." << std::endl;
5443 (*err) += ss.str();
5444 }
5445 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005446 }
5447
jrkooncecba5d6c2019-08-29 11:26:22 -05005448 const json &v = GetValue(spotIt);
5449 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005450 if (err) {
5451 std::stringstream ss;
5452 ss << "\"spot\" is not a JSON object." << std::endl;
5453 (*err) += ss.str();
5454 }
5455 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005456 }
5457
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005458 if (!ParseSpotLight(&light->spot, err, v,
5459 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005460 return false;
5461 }
5462 }
5463
5464 ParseStringProperty(&light->name, err, o, "name", false);
5465 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5466 ParseNumberProperty(&light->range, err, o, "range", false);
5467 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5468 ParseExtensionsProperty(&light->extensions, err, o);
5469 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005470
5471 if (store_original_json_for_extras_and_extensions) {
5472 {
5473 json_const_iterator it;
5474 if (FindMember(o, "extensions", it)) {
5475 light->extensions_json_string = JsonToString(GetValue(it));
5476 }
5477 }
5478 {
5479 json_const_iterator it;
5480 if (FindMember(o, "extras", it)) {
5481 light->extras_json_string = JsonToString(GetValue(it));
5482 }
5483 }
5484 }
5485
Johan Bowald52936a02019-07-17 09:06:45 +02005486 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005487}
5488
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005489bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005490 const char *json_str,
5491 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005492 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005493 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005494 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005495 if (err) {
5496 (*err) = "JSON string too short.\n";
5497 }
5498 return false;
5499 }
5500
jrkooncecba5d6c2019-08-29 11:26:22 -05005501 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005502
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005503#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5504 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005505 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005506 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005507 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005508
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005509 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005510 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005511 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005512 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005513 return false;
5514 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005515#else
5516 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005517 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005518
jrkooncecba5d6c2019-08-29 11:26:22 -05005519 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005520 // Assume parsing was failed.
5521 if (err) {
5522 (*err) = "Failed to parse JSON object\n";
5523 }
5524 return false;
5525 }
5526 }
5527#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005528
jrkooncecba5d6c2019-08-29 11:26:22 -05005529 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005530 // root is not an object.
5531 if (err) {
5532 (*err) = "Root element is not a JSON object\n";
5533 }
5534 return false;
5535 }
5536
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005537 {
5538 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005539 json_const_iterator it;
5540 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005541 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005542 json_const_iterator version_it;
5543 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005544 if (FindMember(itObj, "version", version_it) &&
5545 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005546 version_found = true;
5547 }
5548 }
5549 if (version_found) {
5550 // OK
5551 } else if (check_sections & REQUIRE_VERSION) {
5552 if (err) {
5553 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5554 }
5555 return false;
5556 }
5557 }
5558
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005559 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005560 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005561
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005562 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005563 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005564 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005565 };
5566
Syoyo Fujita83675312017-12-02 21:14:13 +09005567 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005568 if ((check_sections & REQUIRE_SCENES) &&
5569 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005570 if (err) {
5571 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5572 }
5573 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005574 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005575 }
5576
Syoyo Fujita83675312017-12-02 21:14:13 +09005577 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005578 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005579 if (err) {
5580 (*err) += "\"nodes\" object not found in .gltf\n";
5581 }
5582 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005583 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005584 }
5585
Syoyo Fujita83675312017-12-02 21:14:13 +09005586 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005587 if ((check_sections & REQUIRE_ACCESSORS) &&
5588 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005589 if (err) {
5590 (*err) += "\"accessors\" object not found in .gltf\n";
5591 }
5592 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005593 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005594 }
5595
Syoyo Fujita83675312017-12-02 21:14:13 +09005596 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005597 if ((check_sections & REQUIRE_BUFFERS) &&
5598 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005599 if (err) {
5600 (*err) += "\"buffers\" object not found in .gltf\n";
5601 }
5602 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005603 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005604 }
5605
Syoyo Fujita83675312017-12-02 21:14:13 +09005606 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005607 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5608 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005609 if (err) {
5610 (*err) += "\"bufferViews\" object not found in .gltf\n";
5611 }
5612 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005613 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005614 }
5615
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005616 model->buffers.clear();
5617 model->bufferViews.clear();
5618 model->accessors.clear();
5619 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005620 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005621 model->nodes.clear();
5622 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005623 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005624 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005625 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005626
Syoyo Fujita83675312017-12-02 21:14:13 +09005627 // 1. Parse Asset
5628 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005629 json_const_iterator it;
5630 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5631 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005632
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005633 ParseAsset(&model->asset, err, root,
5634 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005635 }
5636 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005637
jrkoonce51453942019-09-03 09:48:30 -05005638#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005639 auto ForEachInArray = [](const json &_v, const char *member,
5640 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005641#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005642 // The std::function<> implementation can be less efficient because it will
5643 // allocate heap when the size of the captured lambda is above 16 bytes with
5644 // clang and gcc, but it does not require C++14.
5645 auto ForEachInArray = [](const json &_v, const char *member,
5646 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005647#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005648 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005649 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005650 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005651 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005652 auto it = ArrayBegin(root);
5653 auto end = ArrayEnd(root);
5654 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005655 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005656 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005657 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005658 return true;
5659 };
5660
jrkooncecba5d6c2019-08-29 11:26:22 -05005661 // 2. Parse extensionUsed
5662 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005663 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005664 std::string str;
5665 GetString(o, str);
5666 model->extensionsUsed.emplace_back(std::move(str));
5667 return true;
5668 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005669 }
5670
Syoyo Fujita83675312017-12-02 21:14:13 +09005671 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005672 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005673 std::string str;
5674 GetString(o, str);
5675 model->extensionsRequired.emplace_back(std::move(str));
5676 return true;
5677 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005678 }
5679
Syoyo Fujita83675312017-12-02 21:14:13 +09005680 // 3. Parse Buffer
5681 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005682 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005683 if (!IsObject(o)) {
5684 if (err) {
5685 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005686 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005687 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005688 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005689 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005690 if (!ParseBuffer(&buffer, err, o,
5691 store_original_json_for_extras_and_extensions_, &fs,
5692 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005693 return false;
5694 }
5695
5696 model->buffers.emplace_back(std::move(buffer));
5697 return true;
5698 });
5699
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005700 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005701 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005702 }
5703 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005704 // 4. Parse BufferView
5705 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005706 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005707 if (!IsObject(o)) {
5708 if (err) {
5709 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005710 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005711 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005712 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005713 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005714 if (!ParseBufferView(&bufferView, err, o,
5715 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005716 return false;
5717 }
5718
5719 model->bufferViews.emplace_back(std::move(bufferView));
5720 return true;
5721 });
5722
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005723 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005724 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005725 }
5726 }
5727
Syoyo Fujita83675312017-12-02 21:14:13 +09005728 // 5. Parse Accessor
5729 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005730 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005731 if (!IsObject(o)) {
5732 if (err) {
5733 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005734 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005735 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005736 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005737 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005738 if (!ParseAccessor(&accessor, err, o,
5739 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005740 return false;
5741 }
5742
5743 model->accessors.emplace_back(std::move(accessor));
5744 return true;
5745 });
5746
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005747 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005748 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005749 }
5750 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005751
Syoyo Fujita83675312017-12-02 21:14:13 +09005752 // 6. Parse Mesh
5753 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005754 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005755 if (!IsObject(o)) {
5756 if (err) {
5757 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005758 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005759 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005760 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005761 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005762 if (!ParseMesh(&mesh, model, err, o,
5763 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005764 return false;
5765 }
5766
5767 model->meshes.emplace_back(std::move(mesh));
5768 return true;
5769 });
5770
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005771 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005772 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005773 }
5774 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005775
viperscape9df05802018-12-05 14:11:01 -05005776 // Assign missing bufferView target types
5777 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005778 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005779 for (auto &mesh : model->meshes) {
5780 for (auto &primitive : mesh.primitives) {
5781 if (primitive.indices >
5782 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005783 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005784 if (size_t(primitive.indices) >= model->accessors.size()) {
5785 if (err) {
5786 (*err) += "primitive indices accessor out of bounds";
5787 }
5788 return false;
5789 }
5790
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005791 auto bufferView =
5792 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005793 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5794 if (err) {
5795 (*err) += "accessor[" + std::to_string(primitive.indices) +
5796 "] invalid bufferView";
5797 }
5798 return false;
5799 }
5800
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005801 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005802 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005803 // we could optionally check if acessors' bufferView type is Scalar, as
5804 // it should be
viperscape9df05802018-12-05 14:11:01 -05005805 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005806
5807 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07005808 const auto accessorsIndex = size_t(attribute.second);
5809 if (accessorsIndex < model->accessors.size()) {
5810 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5811 // bufferView could be null(-1) for sparse morph target
5812 if (bufferView >= 0 && bufferView < model->bufferViews.size()) {
5813 model->bufferViews[size_t(bufferView)].target =
5814 TINYGLTF_TARGET_ARRAY_BUFFER;
5815 }
5816 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005817 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005818
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005819 for (auto &target : primitive.targets) {
5820 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07005821 const auto accessorsIndex = size_t(attribute.second);
5822 if (accessorsIndex < model->accessors.size()) {
5823 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5824 // bufferView could be null(-1) for sparse morph target
5825 if (bufferView >= 0 && bufferView < model->bufferViews.size()) {
5826 model->bufferViews[size_t(bufferView)].target =
5827 TINYGLTF_TARGET_ARRAY_BUFFER;
5828 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04005829 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005830 }
5831 }
viperscape9df05802018-12-05 14:11:01 -05005832 }
5833 }
5834
Syoyo Fujita83675312017-12-02 21:14:13 +09005835 // 7. Parse Node
5836 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005837 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005838 if (!IsObject(o)) {
5839 if (err) {
5840 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005841 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005842 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005843 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005844 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005845 if (!ParseNode(&node, err, o,
5846 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005847 return false;
5848 }
5849
5850 model->nodes.emplace_back(std::move(node));
5851 return true;
5852 });
5853
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005854 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005855 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005856 }
5857 }
5858
5859 // 8. Parse scenes.
5860 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005861 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005862 if (!IsObject(o)) {
5863 if (err) {
5864 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005865 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005866 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005867 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005868 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005869 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005870
5871 Scene scene;
5872 scene.nodes = std::move(nodes);
5873
5874 ParseStringProperty(&scene.name, err, o, "name", false);
5875
5876 ParseExtensionsProperty(&scene.extensions, err, o);
5877 ParseExtrasProperty(&scene.extras, o);
5878
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005879 if (store_original_json_for_extras_and_extensions_) {
5880 {
5881 json_const_iterator it;
5882 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005883 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005884 }
5885 }
5886 {
5887 json_const_iterator it;
5888 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005889 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005890 }
5891 }
5892 }
5893
jrkooncecba5d6c2019-08-29 11:26:22 -05005894 model->scenes.emplace_back(std::move(scene));
5895 return true;
5896 });
5897
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005898 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005899 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005900 }
5901 }
5902
5903 // 9. Parse default scenes.
5904 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005905 json_const_iterator rootIt;
5906 int iVal;
5907 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5908 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005909 }
5910 }
5911
5912 // 10. Parse Material
5913 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005914 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005915 if (!IsObject(o)) {
5916 if (err) {
5917 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005918 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005919 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005920 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005921 Material material;
5922 ParseStringProperty(&material.name, err, o, "name", false);
5923
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005924 if (!ParseMaterial(&material, err, o,
5925 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005926 return false;
5927 }
5928
5929 model->materials.emplace_back(std::move(material));
5930 return true;
5931 });
5932
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005933 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005934 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005935 }
5936 }
5937
5938 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005939 void *load_image_user_data{nullptr};
5940
5941 LoadImageDataOption load_image_option;
5942
5943 if (user_image_loader_) {
5944 // Use user supplied pointer
5945 load_image_user_data = load_image_user_data_;
5946 } else {
5947 load_image_option.preserve_channels = preserve_image_channels_;
5948 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5949 }
5950
Syoyo Fujita83675312017-12-02 21:14:13 +09005951 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005952 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005953 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005954 if (!IsObject(o)) {
5955 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005956 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005957 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005958 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005959 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005960 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005961 if (!ParseImage(&image, idx, err, warn, o,
5962 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005963 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005964 return false;
5965 }
5966
5967 if (image.bufferView != -1) {
5968 // Load image from the buffer view.
5969 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5970 if (err) {
5971 std::stringstream ss;
5972 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005973 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005974 (*err) += ss.str();
5975 }
5976 return false;
5977 }
5978
5979 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005980 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005981 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5982 if (err) {
5983 std::stringstream ss;
5984 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005985 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005986 (*err) += ss.str();
5987 }
5988 return false;
5989 }
5990 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5991
5992 if (*LoadImageData == nullptr) {
5993 if (err) {
5994 (*err) += "No LoadImageData callback specified.\n";
5995 }
5996 return false;
5997 }
5998 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005999 &image, idx, err, warn, image.width, image.height,
6000 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006001 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05006002 if (!ret) {
6003 return false;
6004 }
6005 }
6006
6007 model->images.emplace_back(std::move(image));
6008 ++idx;
6009 return true;
6010 });
6011
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006012 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006013 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006014 }
6015 }
6016
6017 // 12. Parse Texture
6018 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006019 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006020 if (!IsObject(o)) {
6021 if (err) {
6022 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006023 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006024 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006025 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006026 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006027 if (!ParseTexture(&texture, err, o,
6028 store_original_json_for_extras_and_extensions_,
6029 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006030 return false;
6031 }
6032
6033 model->textures.emplace_back(std::move(texture));
6034 return true;
6035 });
6036
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006037 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006038 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006039 }
6040 }
6041
6042 // 13. Parse Animation
6043 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006044 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006045 if (!IsObject(o)) {
6046 if (err) {
6047 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006048 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006049 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006050 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006051 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006052 if (!ParseAnimation(&animation, err, o,
6053 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006054 return false;
6055 }
6056
6057 model->animations.emplace_back(std::move(animation));
6058 return true;
6059 });
6060
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006061 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006062 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006063 }
6064 }
6065
6066 // 14. Parse Skin
6067 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006068 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006069 if (!IsObject(o)) {
6070 if (err) {
6071 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006072 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006073 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006074 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006075 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006076 if (!ParseSkin(&skin, err, o,
6077 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006078 return false;
6079 }
6080
6081 model->skins.emplace_back(std::move(skin));
6082 return true;
6083 });
6084
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006085 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006086 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006087 }
6088 }
6089
6090 // 15. Parse Sampler
6091 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006092 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006093 if (!IsObject(o)) {
6094 if (err) {
6095 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006096 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006097 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006098 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006099 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006100 if (!ParseSampler(&sampler, err, o,
6101 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006102 return false;
6103 }
6104
6105 model->samplers.emplace_back(std::move(sampler));
6106 return true;
6107 });
6108
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006109 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006110 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006111 }
6112 }
6113
6114 // 16. Parse Camera
6115 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006116 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006117 if (!IsObject(o)) {
6118 if (err) {
6119 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006120 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006121 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006122 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006123 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006124 if (!ParseCamera(&camera, err, o,
6125 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006126 return false;
6127 }
6128
6129 model->cameras.emplace_back(std::move(camera));
6130 return true;
6131 });
6132
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006133 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006134 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006135 }
6136 }
6137
6138 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006139 ParseExtensionsProperty(&model->extensions, err, v);
6140
6141 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006142 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006143 json_const_iterator rootIt;
6144 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6145 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006146
jrkooncecba5d6c2019-08-29 11:26:22 -05006147 json_const_iterator it(ObjectBegin(root));
6148 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006149 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006150 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006151 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006152 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006153 const json &object = GetValue(it);
6154 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006155 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006156 const json &lights = GetValue(itLight);
6157 if (!IsArray(lights)) {
6158 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006159 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006160
6161 auto arrayIt(ArrayBegin(lights));
6162 auto arrayItEnd(ArrayEnd(lights));
6163 for (; arrayIt != arrayItEnd; ++arrayIt) {
6164 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006165 if (!ParseLight(&light, err, *arrayIt,
6166 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006167 return false;
6168 }
6169 model->lights.emplace_back(std::move(light));
6170 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006171 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006172 }
6173 }
6174 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006175 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006176
mynzc0d4d1c2018-06-28 23:06:00 +09006177 // 19. Parse Extras
6178 ParseExtrasProperty(&model->extras, v);
6179
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006180 if (store_original_json_for_extras_and_extensions_) {
6181 model->extras_json_string = JsonToString(v["extras"]);
6182 model->extensions_json_string = JsonToString(v["extensions"]);
6183 }
6184
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006185 return true;
6186}
6187
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006188bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006189 std::string *warn, const char *str,
6190 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006191 const std::string &base_dir,
6192 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006193 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006194 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006195 bin_size_ = 0;
6196
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006197 return LoadFromString(model, err, warn, str, length, base_dir,
6198 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006199}
6200
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006201bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006202 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006203 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006204 std::stringstream ss;
6205
Paolo Jovone6601bf2018-07-07 20:43:33 +02006206 if (fs.ReadWholeFile == nullptr) {
6207 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006208 ss << "Failed to read file: " << filename
6209 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006210 if (err) {
6211 (*err) = ss.str();
6212 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006213 return false;
6214 }
6215
Paolo Jovone6601bf2018-07-07 20:43:33 +02006216 std::vector<unsigned char> data;
6217 std::string fileerr;
6218 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006219 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006220 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6221 if (err) {
6222 (*err) = ss.str();
6223 }
6224 return false;
6225 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006226
Paolo Jovone6601bf2018-07-07 20:43:33 +02006227 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006228 if (sz == 0) {
6229 if (err) {
6230 (*err) = "Empty file.";
6231 }
6232 return false;
6233 }
6234
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006235 std::string basedir = GetBaseDir(filename);
6236
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006237 bool ret = LoadASCIIFromString(
6238 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6239 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006240
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006241 return ret;
6242}
6243
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006244bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006245 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006246 const unsigned char *bytes,
6247 unsigned int size,
6248 const std::string &base_dir,
6249 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006250 if (size < 20) {
6251 if (err) {
6252 (*err) = "Too short data size for glTF Binary.";
6253 }
6254 return false;
6255 }
6256
Syoyo Fujitabeded612016-05-01 20:03:43 +09006257 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6258 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006259 // ok
6260 } else {
6261 if (err) {
6262 (*err) = "Invalid magic.";
6263 }
6264 return false;
6265 }
6266
Syoyo Fujitabeded612016-05-01 20:03:43 +09006267 unsigned int version; // 4 bytes
6268 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006269 unsigned int chunk0_length; // 4 bytes
6270 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006271
Syoyo Fujitabeded612016-05-01 20:03:43 +09006272 memcpy(&version, bytes + 4, 4);
6273 swap4(&version);
6274 memcpy(&length, bytes + 8, 4);
6275 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006276 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6277 swap4(&chunk0_length);
6278 memcpy(&chunk0_format, bytes + 16, 4);
6279 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006280
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006281 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6282 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006283 // In case the Bin buffer is not present, the size is exactly 20 + size of
6284 // JSON contents,
6285 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006286 //
6287 // https://github.com/syoyo/tinygltf/issues/372
6288 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006289 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006290
Syoyo Fujitac670f082022-09-17 19:52:25 +09006291 if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006292 // Do not allow 4GB or more GLB data.
6293 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6294 }
6295
Syoyo Fujitac670f082022-09-17 19:52:25 +09006296 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6297 (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006298 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006299 if (err) {
6300 (*err) = "Invalid glTF binary.";
6301 }
6302 return false;
6303 }
6304
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006305 // Padding check
6306 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6307 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006308 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006309 if (err) {
6310 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6311 }
6312 }
6313
Syoyo Fujita612e5782022-09-18 21:01:39 +09006314 //std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6315 //std::cout << "length = " << length << "\n";
6316
Syoyo Fujitac670f082022-09-17 19:52:25 +09006317 // Chunk1(BIN) data
6318 // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
Syoyo Fujita612e5782022-09-18 21:01:39 +09006319 // So when header + JSON data == binary size, Chunk1 is omitted.
6320 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006321
Syoyo Fujitac670f082022-09-17 19:52:25 +09006322 bin_data_ = nullptr;
6323 bin_size_ = 0;
6324 } else {
6325 // Read Chunk1 info(BIN data)
Syoyo Fujita612e5782022-09-18 21:01:39 +09006326 // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aliged to 4 bytes)
6327 if ((header_and_json_size + 12ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006328 if (err) {
Syoyo Fujita612e5782022-09-18 21:01:39 +09006329 (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006330 }
6331 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006332 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006333
Syoyo Fujitac670f082022-09-17 19:52:25 +09006334 unsigned int chunk1_length; // 4 bytes
6335 unsigned int chunk1_format; // 4 bytes;
6336 memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
6337 swap4(&chunk1_length);
6338 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6339 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006340
Syoyo Fujita612e5782022-09-18 21:01:39 +09006341 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6342
Syoyo Fujitac670f082022-09-17 19:52:25 +09006343 if (chunk1_length < 4) {
6344 if (err) {
6345 (*err) = "Insufficient Chunk1(BIN) data size.";
6346 }
6347 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006348 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006349
Syoyo Fujitac670f082022-09-17 19:52:25 +09006350 if ((chunk1_length % 4) != 0) {
6351 if (err) {
6352 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6353 }
6354 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006355 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006356
Syoyo Fujitac670f082022-09-17 19:52:25 +09006357 if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
6358 if (err) {
6359 (*err) = "BIN Chunk data length exceeds the GLB size.";
6360 }
6361 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006362 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006363
Syoyo Fujitac670f082022-09-17 19:52:25 +09006364 if (chunk1_format != 0x004e4942) {
6365 if (err) {
6366 (*err) = "Invlid type for chunk1 data.";
6367 }
6368 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006369 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006370
Syoyo Fujita612e5782022-09-18 21:01:39 +09006371 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6372
Syoyo Fujitac670f082022-09-17 19:52:25 +09006373 bin_data_ = bytes + header_and_json_size +
6374 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6375
6376 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006377 }
6378
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006379 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006380 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006381 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006382
6383 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006384
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006385 bool ret = LoadFromString(model, err, warn,
6386 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006387 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006388 if (!ret) {
6389 return ret;
6390 }
6391
6392 return true;
6393}
6394
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006395bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006396 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006397 const std::string &filename,
6398 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006399 std::stringstream ss;
6400
Paolo Jovone6601bf2018-07-07 20:43:33 +02006401 if (fs.ReadWholeFile == nullptr) {
6402 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006403 ss << "Failed to read file: " << filename
6404 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006405 if (err) {
6406 (*err) = ss.str();
6407 }
6408 return false;
6409 }
6410
Paolo Jovone6601bf2018-07-07 20:43:33 +02006411 std::vector<unsigned char> data;
6412 std::string fileerr;
6413 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006414 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006415 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6416 if (err) {
6417 (*err) = ss.str();
6418 }
6419 return false;
6420 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006421
Syoyo Fujitabeded612016-05-01 20:03:43 +09006422 std::string basedir = GetBaseDir(filename);
6423
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006424 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6425 static_cast<unsigned int>(data.size()),
6426 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006427
6428 return ret;
6429}
6430
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006431///////////////////////
6432// GLTF Serialization
6433///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006434namespace {
6435json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006436#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006437 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006438#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006439 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006440#endif
jrkoonce63419a12019-09-03 17:06:41 -05006441}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006442
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006443void JsonAssign(json &dest, const json &src) {
6444#ifdef TINYGLTF_USE_RAPIDJSON
6445 dest.CopyFrom(src, GetAllocator());
6446#else
6447 dest = src;
6448#endif
6449}
6450
6451void JsonAddMember(json &o, const char *key, json &&value) {
6452#ifdef TINYGLTF_USE_RAPIDJSON
6453 if (!o.IsObject()) {
6454 o.SetObject();
6455 }
6456 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6457#else
6458 o[key] = std::move(value);
6459#endif
6460}
6461
6462void JsonPushBack(json &o, json &&value) {
6463#ifdef TINYGLTF_USE_RAPIDJSON
6464 o.PushBack(std::move(value), GetAllocator());
6465#else
6466 o.push_back(std::move(value));
6467#endif
6468}
6469
6470bool JsonIsNull(const json &o) {
6471#ifdef TINYGLTF_USE_RAPIDJSON
6472 return o.IsNull();
6473#else
6474 return o.is_null();
6475#endif
6476}
6477
6478void JsonSetObject(json &o) {
6479#ifdef TINYGLTF_USE_RAPIDJSON
6480 o.SetObject();
6481#else
6482 o = o.object({});
6483#endif
6484}
6485
6486void JsonReserveArray(json &o, size_t s) {
6487#ifdef TINYGLTF_USE_RAPIDJSON
6488 o.SetArray();
6489 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6490#endif
6491 (void)(o);
6492 (void)(s);
6493}
6494} // namespace
6495
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006496// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006497
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006498template <typename T>
6499static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006500 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006501 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006502 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006503 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006504 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006505}
6506
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006507#ifdef TINYGLTF_USE_RAPIDJSON
6508template <>
6509void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6510 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6511}
6512#endif
6513
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006514template <typename T>
6515static void SerializeNumberArrayProperty(const std::string &key,
6516 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006517 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006518 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006519
jrkooncecba5d6c2019-08-29 11:26:22 -05006520 json ary;
6521 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006522 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006523 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006524 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006525 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006526}
6527
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006528static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006529 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006530 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006531}
6532
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006533static void SerializeStringArrayProperty(const std::string &key,
6534 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006535 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006536 json ary;
6537 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006538 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006539 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006540 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006541 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006542}
6543
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006544static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006545 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006546#ifdef TINYGLTF_USE_RAPIDJSON
6547 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006548 case REAL_TYPE:
6549 obj.SetDouble(value.Get<double>());
6550 break;
6551 case INT_TYPE:
6552 obj.SetInt(value.Get<int>());
6553 break;
6554 case BOOL_TYPE:
6555 obj.SetBool(value.Get<bool>());
6556 break;
6557 case STRING_TYPE:
6558 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6559 break;
6560 case ARRAY_TYPE: {
6561 obj.SetArray();
6562 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6563 GetAllocator());
6564 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6565 Value elementValue = value.Get(int(i));
6566 json elementJson;
6567 if (ValueToJson(value.Get(int(i)), &elementJson))
6568 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006569 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006570 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006571 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006572 case BINARY_TYPE:
6573 // TODO
6574 // obj = json(value.Get<std::vector<unsigned char>>());
6575 return false;
6576 break;
6577 case OBJECT_TYPE: {
6578 obj.SetObject();
6579 Value::Object objMap = value.Get<Value::Object>();
6580 for (auto &it : objMap) {
6581 json elementJson;
6582 if (ValueToJson(it.second, &elementJson)) {
6583 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6584 std::move(elementJson), GetAllocator());
6585 }
6586 }
6587 break;
6588 }
6589 case NULL_TYPE:
6590 default:
6591 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006592 }
6593#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006594 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006595 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006596 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006597 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006598 case INT_TYPE:
6599 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006600 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006601 case BOOL_TYPE:
6602 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006603 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006604 case STRING_TYPE:
6605 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006606 break;
6607 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006608 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6609 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006610 json elementJson;
6611 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006612 obj.push_back(elementJson);
6613 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006614 break;
6615 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006616 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006617 // TODO
6618 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006619 return false;
6620 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006621 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006622 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006623 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006624 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006625 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006626 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006627 break;
6628 }
6629 case NULL_TYPE:
6630 default:
6631 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006632 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006633#endif
6634 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006635 return true;
6636}
6637
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006638static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006639 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006640 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006641 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006642 JsonAddMember(obj, key.c_str(), std::move(ret));
6643 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006644}
6645
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006646static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006647 json &o) {
6648 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006649 if (data.size() > 0) {
6650 std::string encodedData =
6651 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6652 SerializeStringProperty("uri", header + encodedData, o);
6653 } else {
6654 // Issue #229
6655 // size 0 is allowd. Just emit mime header.
6656 SerializeStringProperty("uri", header, o);
6657 }
johan bowald30c53472018-03-30 11:49:36 +02006658}
6659
Selmar Koke4677492018-10-25 16:45:49 +02006660static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006661 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006662#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006663#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006664 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6665 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6666 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6667 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006668 std::ostream output(&wfile_buf);
6669 if (!wfile_buf.is_open()) return false;
6670#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006671 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006672 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006673#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006674 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006675 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006676#endif
6677#else
6678 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6679 if (!output.is_open()) return false;
6680#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006681 if (data.size() > 0) {
6682 output.write(reinterpret_cast<const char *>(&data[0]),
6683 std::streamsize(data.size()));
6684 } else {
6685 // Issue #229
6686 // size 0 will be still valid buffer data.
6687 // write empty file.
6688 }
Selmar Koke4677492018-10-25 16:45:49 +02006689 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006690}
6691
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006692#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006693static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006694 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6695 ++paramIt) {
6696 if (paramIt->second.number_array.size()) {
6697 SerializeNumberArrayProperty<double>(paramIt->first,
6698 paramIt->second.number_array, o);
6699 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006700 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006701 for (std::map<std::string, double>::iterator it =
6702 paramIt->second.json_double_value.begin();
6703 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006704 if (it->first == "index") {
6705 json_double_value[it->first] = paramIt->second.TextureIndex();
6706 } else {
6707 json_double_value[it->first] = it->second;
6708 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006709 }
6710
Syoyo Fujita83675312017-12-02 21:14:13 +09006711 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006712 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006713 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006714 } else if (paramIt->second.has_number_value) {
6715 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006716 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006717 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006718 }
6719 }
6720}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006721#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006722
Selmar Kok81b672b2019-10-18 16:08:44 +02006723static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006724 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006725
6726 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006727 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006728 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006729 // Allow an empty object for extension(#97)
6730 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006731 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006732 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006733 isNull = JsonIsNull(ret);
6734 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006735 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006736 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006737 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6738 // create empty object so that an extension name is still included in
6739 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006740 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006741 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006742 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006743 }
6744 }
Selmar09d2ff12018-03-15 17:30:42 +01006745 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006746 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006747}
6748
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006749static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006750 if (accessor.bufferView >= 0)
6751 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006752
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006753 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006754 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006755
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006756 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6757 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006758
6759 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6760 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6761 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6762 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6763 } else {
6764 // Issue #301. Serialize as integer.
6765 // Assume int value is within [-2**31-1, 2**31-1]
6766 {
6767 std::vector<int> values;
6768 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6769 std::back_inserter(values),
6770 [](double v) { return static_cast<int>(v); });
6771
6772 SerializeNumberArrayProperty<int>("min", values, o);
6773 }
6774
6775 {
6776 std::vector<int> values;
6777 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6778 std::back_inserter(values),
6779 [](double v) { return static_cast<int>(v); });
6780
6781 SerializeNumberArrayProperty<int>("max", values, o);
6782 }
6783 }
6784
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006785 if (accessor.normalized)
6786 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006787 std::string type;
6788 switch (accessor.type) {
6789 case TINYGLTF_TYPE_SCALAR:
6790 type = "SCALAR";
6791 break;
6792 case TINYGLTF_TYPE_VEC2:
6793 type = "VEC2";
6794 break;
6795 case TINYGLTF_TYPE_VEC3:
6796 type = "VEC3";
6797 break;
6798 case TINYGLTF_TYPE_VEC4:
6799 type = "VEC4";
6800 break;
6801 case TINYGLTF_TYPE_MAT2:
6802 type = "MAT2";
6803 break;
6804 case TINYGLTF_TYPE_MAT3:
6805 type = "MAT3";
6806 break;
6807 case TINYGLTF_TYPE_MAT4:
6808 type = "MAT4";
6809 break;
6810 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006811
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006812 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006813 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006814
6815 if (accessor.extras.Type() != NULL_TYPE) {
6816 SerializeValue("extras", accessor.extras, o);
6817 }
feiy0b315432022-08-13 10:08:17 +08006818
6819 // sparse
6820 if (accessor.sparse.isSparse)
6821 {
6822 json sparse;
6823 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
6824 {
6825 json indices;
6826 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
6827 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
6828 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
6829 JsonAddMember(sparse, "indices", std::move(indices));
6830 }
6831 {
6832 json values;
6833 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08006834 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
feiy0b315432022-08-13 10:08:17 +08006835 JsonAddMember(sparse, "values", std::move(values));
6836 }
6837 JsonAddMember(o, "sparse", std::move(sparse));
6838 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006839}
6840
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006841static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006842 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006843 {
6844 json target;
6845 SerializeNumberProperty("node", channel.target_node, target);
6846 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006847
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006848 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006849
jrkooncecba5d6c2019-08-29 11:26:22 -05006850 JsonAddMember(o, "target", std::move(target));
6851 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006852
6853 if (channel.extras.Type() != NULL_TYPE) {
6854 SerializeValue("extras", channel.extras, o);
6855 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006856
Selmar Kok4e2988e2019-08-16 14:08:08 +02006857 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006858}
6859
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006860static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006861 SerializeNumberProperty("input", sampler.input, o);
6862 SerializeNumberProperty("output", sampler.output, o);
6863 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006864
6865 if (sampler.extras.Type() != NULL_TYPE) {
6866 SerializeValue("extras", sampler.extras, o);
6867 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006868}
6869
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006870static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006871 if (!animation.name.empty())
6872 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006873
jrkooncecba5d6c2019-08-29 11:26:22 -05006874 {
6875 json channels;
6876 JsonReserveArray(channels, animation.channels.size());
6877 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6878 json channel;
6879 AnimationChannel gltfChannel = animation.channels[i];
6880 SerializeGltfAnimationChannel(gltfChannel, channel);
6881 JsonPushBack(channels, std::move(channel));
6882 }
6883
6884 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006885 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006886
jrkooncecba5d6c2019-08-29 11:26:22 -05006887 {
6888 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006889 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006890 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6891 json sampler;
6892 AnimationSampler gltfSampler = animation.samplers[i];
6893 SerializeGltfAnimationSampler(gltfSampler, sampler);
6894 JsonPushBack(samplers, std::move(sampler));
6895 }
6896 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006897 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006898
Jens Olssonb3af2f12018-06-04 10:17:49 +02006899 if (animation.extras.Type() != NULL_TYPE) {
6900 SerializeValue("extras", animation.extras, o);
6901 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006902
Selmar Kok4e2988e2019-08-16 14:08:08 +02006903 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006904}
6905
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006906static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006907 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006908 SerializeStringProperty("generator", asset.generator, o);
6909 }
6910
Christophe820ede82019-07-04 15:21:21 +09006911 if (!asset.copyright.empty()) {
6912 SerializeStringProperty("copyright", asset.copyright, o);
6913 }
6914
Syoyo Fujitab702de72021-03-02 19:08:29 +09006915 if (asset.version.empty()) {
6916 // Just in case
6917 // `version` must be defined
6918 asset.version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006919 }
6920
Syoyo Fujitab702de72021-03-02 19:08:29 +09006921 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6922 SerializeStringProperty("version", asset.version, o);
6923
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006924 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006925 SerializeValue("extras", asset.extras, o);
6926 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006927
Selmar09d2ff12018-03-15 17:30:42 +01006928 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006929}
6930
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006931static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6932 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006933 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006934 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006935
6936 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6937
6938 if (buffer.extras.Type() != NULL_TYPE) {
6939 SerializeValue("extras", buffer.extras, o);
6940 }
6941}
6942
johan bowald30c53472018-03-30 11:49:36 +02006943static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6944 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6945 SerializeGltfBufferData(buffer.data, o);
6946
6947 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006948
6949 if (buffer.extras.Type() != NULL_TYPE) {
6950 SerializeValue("extras", buffer.extras, o);
6951 }
johan bowald30c53472018-03-30 11:49:36 +02006952}
6953
Selmar Koke4677492018-10-25 16:45:49 +02006954static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006955 const std::string &binFilename,
6956 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006957 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006958 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006959 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006960
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006961 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006962
6963 if (buffer.extras.Type() != NULL_TYPE) {
6964 SerializeValue("extras", buffer.extras, o);
6965 }
Selmar Koke4677492018-10-25 16:45:49 +02006966 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006967}
6968
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006969static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006970 SerializeNumberProperty("buffer", bufferView.buffer, o);
6971 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006972
Johan Bowaldfaa27222018-03-28 14:44:45 +02006973 // byteStride is optional, minimum allowed is 4
6974 if (bufferView.byteStride >= 4) {
6975 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6976 }
6977 // byteOffset is optional, default is 0
6978 if (bufferView.byteOffset > 0) {
6979 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6980 }
6981 // Target is optional, check if it contains a valid value
6982 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6983 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6984 SerializeNumberProperty("target", bufferView.target, o);
6985 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006986 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006987 SerializeStringProperty("name", bufferView.name, o);
6988 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006989
6990 if (bufferView.extras.Type() != NULL_TYPE) {
6991 SerializeValue("extras", bufferView.extras, o);
6992 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006993}
6994
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006995static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006996 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08006997 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006998 SerializeStringProperty("mimeType", image.mimeType, o);
6999 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7000 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007001 // TODO(syoyo): dlib::urilencode?
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007002 SerializeStringProperty("uri", image.uri, o);
7003 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007004
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007005 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007006 SerializeStringProperty("name", image.name, o);
7007 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007008
7009 if (image.extras.Type() != NULL_TYPE) {
7010 SerializeValue("extras", image.extras, o);
7011 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09007012
7013 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007014}
7015
Syoyo Fujita046400b2019-07-24 19:26:48 +09007016static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
7017 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007018
Syoyo Fujita046400b2019-07-24 19:26:48 +09007019 if (texinfo.texCoord != 0) {
7020 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7021 }
7022
7023 if (texinfo.extras.Type() != NULL_TYPE) {
7024 SerializeValue("extras", texinfo.extras, o);
7025 }
7026
7027 SerializeExtensionMap(texinfo.extensions, o);
7028}
7029
7030static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
7031 json &o) {
7032 SerializeNumberProperty("index", texinfo.index, o);
7033
7034 if (texinfo.texCoord != 0) {
7035 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7036 }
7037
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007038 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007039 SerializeNumberProperty("scale", texinfo.scale, o);
7040 }
7041
7042 if (texinfo.extras.Type() != NULL_TYPE) {
7043 SerializeValue("extras", texinfo.extras, o);
7044 }
7045
7046 SerializeExtensionMap(texinfo.extensions, o);
7047}
7048
7049static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
7050 json &o) {
7051 SerializeNumberProperty("index", texinfo.index, o);
7052
7053 if (texinfo.texCoord != 0) {
7054 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7055 }
7056
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007057 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007058 SerializeNumberProperty("strength", texinfo.strength, o);
7059 }
7060
7061 if (texinfo.extras.Type() != NULL_TYPE) {
7062 SerializeValue("extras", texinfo.extras, o);
7063 }
7064
7065 SerializeExtensionMap(texinfo.extensions, o);
7066}
7067
7068static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
7069 json &o) {
7070 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7071 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7072 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7073 o);
7074 }
7075
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007076 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007077 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7078 }
7079
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007080 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007081 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7082 }
7083
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007084 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007085 json texinfo;
7086 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007087 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007088 }
7089
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007090 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007091 json texinfo;
7092 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007093 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007094 }
7095
7096 SerializeExtensionMap(pbr.extensions, o);
7097
7098 if (pbr.extras.Type() != NULL_TYPE) {
7099 SerializeValue("extras", pbr.extras, o);
7100 }
7101}
7102
7103static void SerializeGltfMaterial(Material &material, json &o) {
7104 if (material.name.size()) {
7105 SerializeStringProperty("name", material.name, o);
7106 }
7107
7108 // QUESTION(syoyo): Write material parameters regardless of its default value?
7109
7110 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7111 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7112 }
7113
Patrick Härtld9a468b2019-08-14 14:14:07 +02007114 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007115 SerializeStringProperty("alphaMode", material.alphaMode, o);
7116 }
7117
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007118 if (material.doubleSided != false)
7119 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007120
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007121 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007122 json texinfo;
7123 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007124 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007125 }
7126
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007127 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007128 json texinfo;
7129 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007130 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007131 }
7132
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007133 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007134 json texinfo;
7135 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007136 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007137 }
7138
7139 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7140 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7141 SerializeNumberArrayProperty<double>("emissiveFactor",
7142 material.emissiveFactor, o);
7143 }
7144
7145 {
7146 json pbrMetallicRoughness;
7147 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7148 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007149 // Issue 204
7150 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7151 // default values(json is null). Otherwise it will serialize to
7152 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
7153 // importers(and validators).
7154 //
7155 if (!JsonIsNull(pbrMetallicRoughness)) {
7156 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7157 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007158 }
7159
7160#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007161 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007162 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007163 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007164 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007165 }
7166
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007167 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007168#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007169
Syoyo Fujita046400b2019-07-24 19:26:48 +09007170#endif
7171
7172 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007173
7174 if (material.extras.Type() != NULL_TYPE) {
7175 SerializeValue("extras", material.extras, o);
7176 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007177}
7178
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007179static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007180 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007181 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007182 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007183 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007184 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007185 {
7186 json attributes;
7187 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007188 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007189 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7190 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007191
jrkooncecba5d6c2019-08-29 11:26:22 -05007192 JsonAddMember(primitive, "attributes", std::move(attributes));
7193 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007194
7195 // Indicies is optional
7196 if (gltfPrimitive.indices > -1) {
7197 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7198 }
7199 // Material is optional
7200 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007201 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7202 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007203 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007204 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007205
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007206 // Morph targets
7207 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007208 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007209 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007210 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007211 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007212 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7213 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7214 attrIt != targetData.end(); ++attrIt) {
7215 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7216 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007217 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007218 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007219 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007220 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007221 }
7222
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007223 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007224
Jens Olssonb3af2f12018-06-04 10:17:49 +02007225 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7226 SerializeValue("extras", gltfPrimitive.extras, primitive);
7227 }
7228
jrkooncecba5d6c2019-08-29 11:26:22 -05007229 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007230 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007231
jrkooncecba5d6c2019-08-29 11:26:22 -05007232 JsonAddMember(o, "primitives", std::move(primitives));
7233
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007234 if (mesh.weights.size()) {
7235 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7236 }
7237
7238 if (mesh.name.size()) {
7239 SerializeStringProperty("name", mesh.name, o);
7240 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007241
Selmar Kok81b672b2019-10-18 16:08:44 +02007242 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007243 if (mesh.extras.Type() != NULL_TYPE) {
7244 SerializeValue("extras", mesh.extras, o);
7245 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007246}
7247
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007248static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007249 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7250 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7251 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007252 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007253 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007254 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007255}
7256
Syoyo Fujita83675312017-12-02 21:14:13 +09007257static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007258 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007259 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007260 if (light.range > 0.0) {
7261 SerializeNumberProperty("range", light.range, o);
7262 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007263 SerializeNumberArrayProperty("color", light.color, o);
7264 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007265 if (light.type == "spot") {
7266 json spot;
7267 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007268 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007269 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007270 SerializeExtensionMap(light.extensions, o);
7271 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007272 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007273 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007274}
7275
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007276static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007277 if (node.translation.size() > 0) {
7278 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7279 }
7280 if (node.rotation.size() > 0) {
7281 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7282 }
7283 if (node.scale.size() > 0) {
7284 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7285 }
7286 if (node.matrix.size() > 0) {
7287 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7288 }
7289 if (node.mesh != -1) {
7290 SerializeNumberProperty<int>("mesh", node.mesh, o);
7291 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007292
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007293 if (node.skin != -1) {
7294 SerializeNumberProperty<int>("skin", node.skin, o);
7295 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007296
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007297 if (node.camera != -1) {
7298 SerializeNumberProperty<int>("camera", node.camera, o);
7299 }
7300
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007301 if (node.weights.size() > 0) {
7302 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7303 }
7304
Jens Olssona9718662018-05-24 15:48:49 +02007305 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007306 SerializeValue("extras", node.extras, o);
7307 }
7308
Selmar09d2ff12018-03-15 17:30:42 +01007309 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007310 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007311 SerializeNumberArrayProperty<int>("children", node.children, o);
7312}
7313
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007314static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007315 if (sampler.magFilter != -1) {
7316 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7317 }
7318 if (sampler.minFilter != -1) {
7319 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7320 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007321 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007322 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7323 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007324
7325 if (sampler.extras.Type() != NULL_TYPE) {
7326 SerializeValue("extras", sampler.extras, o);
7327 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007328}
7329
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007330static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007331 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007332 SerializeNumberProperty("zfar", camera.zfar, o);
7333 SerializeNumberProperty("znear", camera.znear, o);
7334 SerializeNumberProperty("xmag", camera.xmag, o);
7335 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007336
7337 if (camera.extras.Type() != NULL_TYPE) {
7338 SerializeValue("extras", camera.extras, o);
7339 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007340}
7341
7342static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007343 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007344 SerializeNumberProperty("zfar", camera.zfar, o);
7345 SerializeNumberProperty("znear", camera.znear, o);
7346 if (camera.aspectRatio > 0) {
7347 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7348 }
7349
7350 if (camera.yfov > 0) {
7351 SerializeNumberProperty("yfov", camera.yfov, o);
7352 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007353
7354 if (camera.extras.Type() != NULL_TYPE) {
7355 SerializeValue("extras", camera.extras, o);
7356 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007357}
7358
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007359static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007360 SerializeStringProperty("type", camera.type, o);
7361 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007362 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007363 }
7364
7365 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007366 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007367 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007368 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007369 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007370 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007371 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007372 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007373 } else {
7374 // ???
7375 }
Syoyofe77cc52020-05-09 02:41:07 +09007376
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007377 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007378 SerializeValue("extras", camera.extras, o);
7379 }
7380 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007381}
7382
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007383static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007384 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7385
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007386 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007387 SerializeStringProperty("name", scene.name, o);
7388 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007389 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007390 SerializeValue("extras", scene.extras, o);
7391 }
7392 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007393}
7394
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007395static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007396 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007397 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007398
7399 if (skin.inverseBindMatrices >= 0) {
7400 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7401 }
7402
7403 if (skin.skeleton >= 0) {
7404 SerializeNumberProperty("skeleton", skin.skeleton, o);
7405 }
7406
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007407 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007408 SerializeStringProperty("name", skin.name, o);
7409 }
7410}
7411
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007412static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007413 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007414 SerializeNumberProperty("sampler", texture.sampler, o);
7415 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007416 if (texture.source > -1) {
7417 SerializeNumberProperty("source", texture.source, o);
7418 }
Christophe820ede82019-07-04 15:21:21 +09007419 if (texture.name.size()) {
7420 SerializeStringProperty("name", texture.name, o);
7421 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007422 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007423 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007424 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007425 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007426}
7427
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007428///
7429/// Serialize all properties except buffers and images.
7430///
7431static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007432 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007433 if (model->accessors.size()) {
7434 json accessors;
7435 JsonReserveArray(accessors, model->accessors.size());
7436 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7437 json accessor;
7438 SerializeGltfAccessor(model->accessors[i], accessor);
7439 JsonPushBack(accessors, std::move(accessor));
7440 }
7441 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007442 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007443
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007444 // ANIMATIONS
7445 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007446 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007447 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007448 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7449 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007450 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007451 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007452 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007453 }
7454 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007455
jrkooncecba5d6c2019-08-29 11:26:22 -05007456 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007457 }
7458
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007459 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007460 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007461 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007462 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007463
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007464 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007465 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007466 json bufferViews;
7467 JsonReserveArray(bufferViews, model->bufferViews.size());
7468 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7469 json bufferView;
7470 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7471 JsonPushBack(bufferViews, std::move(bufferView));
7472 }
7473 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007474 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007475
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007476 // Extensions required
7477 if (model->extensionsRequired.size()) {
7478 SerializeStringArrayProperty("extensionsRequired",
7479 model->extensionsRequired, o);
7480 }
7481
7482 // MATERIALS
7483 if (model->materials.size()) {
7484 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007485 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007486 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7487 json material;
7488 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007489
7490 if (JsonIsNull(material)) {
7491 // Issue 294.
7492 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007493 // so the result may be null(unmodified) when all material parameters
7494 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007495 //
7496 // null is not allowed thus we create an empty JSON object.
7497 JsonSetObject(material);
7498 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007499 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007500 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007501 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007502 }
7503
7504 // MESHES
7505 if (model->meshes.size()) {
7506 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007507 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007508 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7509 json mesh;
7510 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007511 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007512 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007513 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007514 }
7515
7516 // NODES
7517 if (model->nodes.size()) {
7518 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007519 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007520 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7521 json node;
7522 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007523 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007524 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007525 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007526 }
7527
7528 // SCENE
7529 if (model->defaultScene > -1) {
7530 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7531 }
7532
7533 // SCENES
7534 if (model->scenes.size()) {
7535 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007536 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007537 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7538 json currentScene;
7539 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007540 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007541 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007542 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007543 }
7544
7545 // SKINS
7546 if (model->skins.size()) {
7547 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007548 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007549 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7550 json skin;
7551 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007552 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007553 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007554 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007555 }
7556
7557 // TEXTURES
7558 if (model->textures.size()) {
7559 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007560 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007561 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7562 json texture;
7563 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007564 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007565 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007566 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007567 }
7568
7569 // SAMPLERS
7570 if (model->samplers.size()) {
7571 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007572 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007573 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7574 json sampler;
7575 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007576 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007577 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007578 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007579 }
7580
7581 // CAMERAS
7582 if (model->cameras.size()) {
7583 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007584 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007585 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7586 json camera;
7587 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007588 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007589 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007590 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007591 }
7592
7593 // EXTENSIONS
7594 SerializeExtensionMap(model->extensions, o);
7595
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007596 auto extensionsUsed = model->extensionsUsed;
7597
7598 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007599 if (model->lights.size()) {
7600 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007601 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007602 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7603 json light;
7604 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007605 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007606 }
7607 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007608 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007609 json ext_j;
7610
jrkooncecba5d6c2019-08-29 11:26:22 -05007611 {
7612 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007613 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007614 JsonAssign(ext_j, GetValue(it));
7615 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007616 }
7617
jrkooncecba5d6c2019-08-29 11:26:22 -05007618 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007619
jrkooncecba5d6c2019-08-29 11:26:22 -05007620 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007621
7622 // Also add "KHR_lights_punctual" to `extensionsUsed`
7623 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007624 auto has_khr_lights_punctual =
7625 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7626 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007627 return (s.compare("KHR_lights_punctual") == 0);
7628 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007629
7630 if (has_khr_lights_punctual == extensionsUsed.end()) {
7631 extensionsUsed.push_back("KHR_lights_punctual");
7632 }
7633 }
7634 }
7635
7636 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007637 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007638 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007639 }
7640
7641 // EXTRAS
7642 if (model->extras.Type() != NULL_TYPE) {
7643 SerializeValue("extras", model->extras, o);
7644 }
7645}
7646
Johan Bowald52936a02019-07-17 09:06:45 +02007647static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007648 stream << content << std::endl;
7649 return true;
7650}
7651
7652static bool WriteGltfFile(const std::string &output,
7653 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007654#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007655#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007656 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007657#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007658 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7659 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7660 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7661 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007662 std::ostream gltfFile(&wfile_buf);
7663 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007664#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007665 std::ofstream gltfFile(output.c_str());
7666 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007667#endif
7668#else
7669 std::ofstream gltfFile(output.c_str());
7670 if (!gltfFile.is_open()) return false;
7671#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007672 return WriteGltfStream(gltfFile, content);
7673}
7674
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007675static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007676 const std::string &content,
7677 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007678 const std::string header = "glTF";
7679 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007680
Alexander Wood190382a2021-10-08 12:19:13 -04007681 const uint32_t content_size = uint32_t(content.size());
7682 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7683 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007684 const uint32_t content_padding_size =
7685 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7686 const uint32_t bin_padding_size =
7687 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007688
7689 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007690 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007691 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007692 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007693 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007694
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007695 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007696 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7697 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7698
7699 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007700 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007701 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007702 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007703 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007704 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007705 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007706 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007707
7708 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007709 if (content_padding_size > 0) {
7710 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007711 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007712 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007713 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007714 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007715 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7716 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007717 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007718 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007719 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007720 sizeof(bin_format));
7721 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7722 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007723 // Chunksize must be multiplies of 4, so pad with zeroes
7724 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007725 const std::vector<unsigned char> padding =
7726 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7727 stream.write(reinterpret_cast<const char *>(padding.data()),
7728 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007729 }
7730 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007731
7732 // TODO: Check error on stream.write
7733 return true;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007734}
7735
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007736static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007737 const std::string &content,
7738 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007739#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007740#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007741 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007742#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007743 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7744 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7745 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7746 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007747 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007748#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007749 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007750#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007751#else
7752 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007753#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007754 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007755}
7756
7757bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7758 bool prettyPrint = true,
7759 bool writeBinary = false) {
7760 JsonDocument output;
7761
7762 /// Serialize all properties except buffers and images.
7763 SerializeGltfModel(model, output);
7764
7765 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007766 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007767 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007768 json buffers;
7769 JsonReserveArray(buffers, model->buffers.size());
7770 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7771 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007772 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7773 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007774 } else {
7775 SerializeGltfBuffer(model->buffers[i], buffer);
7776 }
7777 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007778 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007779 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007780 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007781
7782 // IMAGES
7783 if (model->images.size()) {
7784 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007785 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007786 for (unsigned int i = 0; i < model->images.size(); ++i) {
7787 json image;
7788
7789 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007790 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7791 // enabled, since we won't write separate images when writing to a stream
7792 // we
Hendrik Schwanekamp41e11022022-06-29 12:22:40 +02007793 UpdateImageObject(model->images[i], dummystring, int(i), true,
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007794 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007795 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007796 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007797 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007798 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007799 }
7800
7801 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007802 return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007803 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007804 return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007805 }
7806
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007807}
7808
7809bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7810 bool embedImages = false,
7811 bool embedBuffers = false,
7812 bool prettyPrint = true,
7813 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007814 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007815 std::string defaultBinFilename = GetBaseFilename(filename);
7816 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007817 std::string::size_type pos =
7818 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007819
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007820 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007821 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007822 }
johan bowald642a3432018-04-01 12:37:18 +02007823 std::string baseDir = GetBaseDir(filename);
7824 if (baseDir.empty()) {
7825 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007826 }
Johan Bowald52936a02019-07-17 09:06:45 +02007827 /// Serialize all properties except buffers and images.
7828 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007829
Selmar Kok7cb31e42018-10-05 16:02:29 +02007830 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007831 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007832 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007833 if (model->buffers.size()) {
7834 json buffers;
7835 JsonReserveArray(buffers, model->buffers.size());
7836 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7837 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007838 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7839 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007840 } else if (embedBuffers) {
7841 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007842 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007843 std::string binSavePath;
7844 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007845 if (!model->buffers[i].uri.empty() &&
7846 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007847 binUri = model->buffers[i].uri;
7848 } else {
7849 binUri = defaultBinFilename + defaultBinFileExt;
7850 bool inUse = true;
7851 int numUsed = 0;
7852 while (inUse) {
7853 inUse = false;
7854 for (const std::string &usedName : usedUris) {
7855 if (binUri.compare(usedName) != 0) continue;
7856 inUse = true;
7857 binUri = defaultBinFilename + std::to_string(numUsed++) +
7858 defaultBinFileExt;
7859 break;
7860 }
Selmar Kokc884e582018-10-05 16:25:54 +02007861 }
7862 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007863 usedUris.push_back(binUri);
7864 binSavePath = JoinPath(baseDir, binUri);
7865 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7866 binUri)) {
7867 return false;
7868 }
Selmar Kokc884e582018-10-05 16:25:54 +02007869 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007870 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007871 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007872 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007873 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007874
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007875 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007876 if (model->images.size()) {
7877 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007878 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007879 for (unsigned int i = 0; i < model->images.size(); ++i) {
7880 json image;
johan bowald642a3432018-04-01 12:37:18 +02007881
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007882 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007883 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007884 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007885 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007886 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007887 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007888 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007889
David Harmonda9eac22018-08-30 08:06:05 -04007890 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007891 return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007892 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007893 return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007894 }
7895
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007896}
7897
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007898} // namespace tinygltf
7899
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007900#ifdef __clang__
7901#pragma clang diagnostic pop
7902#endif
7903
Syoyo Fujita612e5782022-09-18 21:01:39 +09007904#endif // TINYGLTF_IMPLEMENTATION