blob: 5ba33f230aa477adec58469a63f5a6e7a30a9abc [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 Fujitae0b62552022-09-19 03:36:58 +090029// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383.
Syoyo Fujitaeec4c982022-09-16 17:27:20 +090030// - v2.6.2 Fix out-of-bounds access of accessors. PR#379.
Syoyo Fujita4581d372022-09-06 22:02:31 +090031// - v2.6.1 Better GLB validation check when loading.
Syoyo Fujita3bddc092022-08-19 18:21:24 +090032// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
33// Disable expanding file path for security(no use of awkward `wordexp` anymore).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +090034// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
imallettd9ce9eb2022-10-07 10:37:09 -070035// - v2.4.3 Fix null object output when material has all default
Syoyo Fujita7905a5b2021-01-21 20:33:11 +090036// parameters.
Syoyo Fujitac4166e42020-01-08 02:38:01 +090037// - v2.4.2 Decode percent-encoded URI.
Syoyo Fujita6e08b172019-10-30 17:25:38 +090038// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
39// `extras` property.
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +090040// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
Syoyo Fujitaee179b22019-08-16 13:11:30 +090041// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
Syoyo Fujita046400b2019-07-24 19:26:48 +090042// - v2.3.0 Modified Material representation according to glTF 2.0 schema
43// (and introduced TextureInfo class)
Syoyo Fujita150f2432019-07-25 19:22:44 +090044// Change the behavior of `Value::IsNumber`. It return true either the
45// value is int or real.
Syoyo Fujitaca56f722019-03-07 21:04:25 +090046// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
47// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090048// - v2.1.0 Add draco compression.
imallettd9ce9eb2022-10-07 10:37:09 -070049// - v2.0.1 Add comparison feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090050// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090051//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090052// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090053//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090054// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090055// - base64: base64 decode/encode library.
56// - stb_image: Image loading library.
57//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090058#ifndef TINY_GLTF_H_
59#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090060
Syoyo Fujitad42767e2018-03-15 21:52:00 -050061#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090062#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090063#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050064#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020065#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090066#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090067#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090068#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090069#include <string>
70#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090071
zbendefy69eeea12022-09-05 23:54:57 +020072//Auto-detect C++14 standard version
73#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
74#define TINYGLTF_USE_CPP14
75#endif
76
jrkoonce51453942019-09-03 09:48:30 -050077#ifndef TINYGLTF_USE_CPP14
78#include <functional>
79#endif
80
Sascha Willems5f9cb242018-12-28 20:53:41 +010081#ifdef __ANDROID__
82#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
83#include <android/asset_manager.h>
84#endif
85#endif
86
Selmar Kok79e3df22019-10-29 16:22:07 +010087#ifdef __GNUC__
88#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010089#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010090#else
91#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010092#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010093#else
94#define TINYGLTF_NOEXCEPT noexcept
95#endif
96
Syoyo Fujita6e08b172019-10-30 17:25:38 +090097#define DEFAULT_METHODS(x) \
98 ~x() = default; \
99 x(const x &) = default; \
100 x(x &&) TINYGLTF_NOEXCEPT = default; \
101 x &operator=(const x &) = default; \
102 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100103
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900104namespace tinygltf {
105
106#define TINYGLTF_MODE_POINTS (0)
107#define TINYGLTF_MODE_LINE (1)
108#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +0100109#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900110#define TINYGLTF_MODE_TRIANGLES (4)
111#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
112#define TINYGLTF_MODE_TRIANGLE_FAN (6)
113
114#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
115#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
116#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
117#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
118#define TINYGLTF_COMPONENT_TYPE_INT (5124)
119#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
120#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900121#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
122 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
123 // support double type even the schema seems allow any value of
124 // integer:
125 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900126
Syoyo Fujitac2615632016-06-19 21:56:06 +0900127#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
128#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
129#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
130#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
131#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
132#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
133
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100134#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900135#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400136#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900137
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400138// Redeclarations of the above for technique.parameters.
139#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
140#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
141#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
142#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
143#define TINYGLTF_PARAMETER_TYPE_INT (5124)
144#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
145#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
146
147#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
148#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
149#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
150
151#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
152#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
153#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
154
155#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
156#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
157#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
158#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
159
160#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
161#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
162#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
163
164#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
165
166// End parameter types
167
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900168#define TINYGLTF_TYPE_VEC2 (2)
169#define TINYGLTF_TYPE_VEC3 (3)
170#define TINYGLTF_TYPE_VEC4 (4)
171#define TINYGLTF_TYPE_MAT2 (32 + 2)
172#define TINYGLTF_TYPE_MAT3 (32 + 3)
173#define TINYGLTF_TYPE_MAT4 (32 + 4)
174#define TINYGLTF_TYPE_SCALAR (64 + 1)
175#define TINYGLTF_TYPE_VECTOR (64 + 4)
176#define TINYGLTF_TYPE_MATRIX (64 + 16)
177
178#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
179#define TINYGLTF_IMAGE_FORMAT_PNG (1)
180#define TINYGLTF_IMAGE_FORMAT_BMP (2)
181#define TINYGLTF_IMAGE_FORMAT_GIF (3)
182
Luke San Antonio6d616f52016-06-23 14:09:23 -0400183#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
184#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900185#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400186#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
187#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
188
Syoyo Fujitabde70212016-02-07 17:38:17 +0900189#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
190#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
191
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900192#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
193#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
194
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400195#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
196#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
197
Selmar Kok31cb7f92018-10-03 15:39:05 +0200198#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900199#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200200
Sascha Willems5f9cb242018-12-28 20:53:41 +0100201#ifdef __ANDROID__
202#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000203AAssetManager *asset_manager = nullptr;
Sascha Willems5f9cb242018-12-28 20:53:41 +0100204#endif
205#endif
206
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900207typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100208 NULL_TYPE,
209 REAL_TYPE,
210 INT_TYPE,
211 BOOL_TYPE,
212 STRING_TYPE,
213 ARRAY_TYPE,
214 BINARY_TYPE,
215 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900216} Type;
217
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500218static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900219 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
220 return 1;
221 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
222 return 1;
223 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
224 return 2;
225 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
226 return 2;
227 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
228 return 4;
229 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
230 return 4;
231 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
232 return 4;
233 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
234 return 8;
235 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700236 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900237 return -1;
238 }
239}
240
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400241static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900242 if (ty == TINYGLTF_TYPE_SCALAR) {
243 return 1;
244 } else if (ty == TINYGLTF_TYPE_VEC2) {
245 return 2;
246 } else if (ty == TINYGLTF_TYPE_VEC3) {
247 return 3;
248 } else if (ty == TINYGLTF_TYPE_VEC4) {
249 return 4;
250 } else if (ty == TINYGLTF_TYPE_MAT2) {
251 return 4;
252 } else if (ty == TINYGLTF_TYPE_MAT3) {
253 return 9;
254 } else if (ty == TINYGLTF_TYPE_MAT4) {
255 return 16;
256 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700257 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900258 return -1;
259 }
260}
261
Syoyo Fujita150f2432019-07-25 19:22:44 +0900262// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200263bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900264bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
265 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200266
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900267#ifdef __clang__
268#pragma clang diagnostic push
269// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900270#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900271#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900272#endif
273
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900274// Simple class to represent JSON object
275class Value {
276 public:
277 typedef std::vector<Value> Array;
278 typedef std::map<std::string, Value> Object;
279
Syoyo Fujita046400b2019-07-24 19:26:48 +0900280 Value()
281 : type_(NULL_TYPE),
282 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900283 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900284 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900285
286 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900287 explicit Value(int i) : type_(INT_TYPE) {
288 int_value_ = i;
289 real_value_ = i;
290 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900291 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900292 explicit Value(const std::string &s) : type_(STRING_TYPE) {
293 string_value_ = s;
294 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900295 explicit Value(std::string &&s)
296 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900297 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900298 binary_value_.resize(n);
299 memcpy(binary_value_.data(), p, n);
300 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900301 explicit Value(std::vector<unsigned char> &&v) noexcept
302 : type_(BINARY_TYPE),
303 binary_value_(std::move(v)) {}
304 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
305 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
306 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500307
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900308 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
309 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
310 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100311
312 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900313
Hill Mad1e32862021-02-20 22:30:44 -0800314 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900315
316 bool IsBool() const { return (type_ == BOOL_TYPE); }
317
318 bool IsInt() const { return (type_ == INT_TYPE); }
319
Syoyo Fujita150f2432019-07-25 19:22:44 +0900320 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900321
Syoyo Fujita150f2432019-07-25 19:22:44 +0900322 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900323
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900324 bool IsString() const { return (type_ == STRING_TYPE); }
325
326 bool IsBinary() const { return (type_ == BINARY_TYPE); }
327
328 bool IsArray() const { return (type_ == ARRAY_TYPE); }
329
330 bool IsObject() const { return (type_ == OBJECT_TYPE); }
331
Syoyo Fujita150f2432019-07-25 19:22:44 +0900332 // Use this function if you want to have number value as double.
333 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900334 if (type_ == INT_TYPE) {
335 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900336 } else {
337 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900338 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900339 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900340
Syoyo Fujita150f2432019-07-25 19:22:44 +0900341 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900342 // TODO(syoyo): Support int value larger than 32 bits
343 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900344 if (type_ == REAL_TYPE) {
345 return int(real_value_);
346 } else {
347 return int_value_;
348 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900349 }
350
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900351 // Accessor
352 template <typename T>
353 const T &Get() const;
354 template <typename T>
355 T &Get();
356
357 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900358 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900359 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900360 assert(IsArray());
361 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900362 return (static_cast<size_t>(idx) < array_value_.size())
363 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900364 : null_value;
365 }
366
367 // Lookup value from a key-value pair
368 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900369 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900370 assert(IsObject());
371 Object::const_iterator it = object_value_.find(key);
372 return (it != object_value_.end()) ? it->second : null_value;
373 }
374
375 size_t ArrayLen() const {
376 if (!IsArray()) return 0;
377 return array_value_.size();
378 }
379
380 // Valid only for object type.
381 bool Has(const std::string &key) const {
382 if (!IsObject()) return false;
383 Object::const_iterator it = object_value_.find(key);
384 return (it != object_value_.end()) ? true : false;
385 }
386
387 // List keys
388 std::vector<std::string> Keys() const {
389 std::vector<std::string> keys;
390 if (!IsObject()) return keys; // empty
391
392 for (Object::const_iterator it = object_value_.begin();
393 it != object_value_.end(); ++it) {
394 keys.push_back(it->first);
395 }
396
397 return keys;
398 }
399
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900400 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900401
402 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000403
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900404 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900405 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900406
Syoyo Fujita046400b2019-07-24 19:26:48 +0900407 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900408 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900409 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900410 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900411 Array array_value_;
412 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900413 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900414};
415
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900416#ifdef __clang__
417#pragma clang diagnostic pop
418#endif
419
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900420#define TINYGLTF_VALUE_GET(ctype, var) \
421 template <> \
422 inline const ctype &Value::Get<ctype>() const { \
423 return var; \
424 } \
425 template <> \
426 inline ctype &Value::Get<ctype>() { \
427 return var; \
428 }
429TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900430TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900431TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900432TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900433TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900434TINYGLTF_VALUE_GET(Value::Array, array_value_)
435TINYGLTF_VALUE_GET(Value::Object, object_value_)
436#undef TINYGLTF_VALUE_GET
437
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900438#ifdef __clang__
439#pragma clang diagnostic push
440#pragma clang diagnostic ignored "-Wc++98-compat"
441#pragma clang diagnostic ignored "-Wpadded"
442#endif
443
imallettd9ce9eb2022-10-07 10:37:09 -0700444/// Aggregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100445using ColorValue = std::array<double, 4>;
446
Syoyo Fujita046400b2019-07-24 19:26:48 +0900447// === legacy interface ====
448// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500449struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200450 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700451 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900452 std::string string_value;
453 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000454 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200455 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900456
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500457 // context sensitive methods. depending the type of the Parameter you are
458 // accessing, these are either valid or not
459 // If this parameter represent a texture map in a material, will return the
460 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100461
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500462 /// Return the index of a texture if this Parameter is a texture map.
463 /// Returned value is only valid if the parameter represent a texture from a
464 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100465 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100466 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500467 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100468 return int(it->second);
469 }
470 return -1;
471 }
472
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000473 /// Return the index of a texture coordinate set if this Parameter is a
474 /// texture map. Returned value is only valid if the parameter represent a
475 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100476 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000477 const auto it = json_double_value.find("texCoord");
478 if (it != std::end(json_double_value)) {
479 return int(it->second);
480 }
imallettd9ce9eb2022-10-07 10:37:09 -0700481 // As per the spec, if texCoord is omitted, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000482 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100483 }
484
Christophe820ede82019-07-04 15:21:21 +0900485 /// Return the scale of a texture if this Parameter is a normal texture map.
486 /// Returned value is only valid if the parameter represent a normal texture
487 /// from a material
488 double TextureScale() const {
489 const auto it = json_double_value.find("scale");
490 if (it != std::end(json_double_value)) {
491 return it->second;
492 }
imallettd9ce9eb2022-10-07 10:37:09 -0700493 // As per the spec, if scale is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200494 return 1;
495 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200496
Arthur Brainville8a98d982019-07-05 00:26:02 +0200497 /// Return the strength of a texture if this Parameter is a an occlusion map.
498 /// Returned value is only valid if the parameter represent an occlusion map
499 /// from a material
500 double TextureStrength() const {
501 const auto it = json_double_value.find("strength");
502 if (it != std::end(json_double_value)) {
503 return it->second;
504 }
imallettd9ce9eb2022-10-07 10:37:09 -0700505 // As per the spec, if strength is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200506 return 1;
Christophe820ede82019-07-04 15:21:21 +0900507 }
508
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500509 /// Material factor, like the roughness or metalness of a material
510 /// Returned value is only valid if the parameter represent a texture from a
511 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700512 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100513
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500514 /// Return the color of a material
515 /// Returned value is only valid if the parameter represent a texture from a
516 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100517 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100518 return {
imallettd9ce9eb2022-10-07 10:37:09 -0700519 {// this aggregate initialize the std::array object, and uses C++11 RVO.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500520 number_array[0], number_array[1], number_array[2],
521 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100522 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200523
Selmar Kokff2b1f92019-10-21 17:58:09 +0200524 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100525 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900526 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100527};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900528
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900529#ifdef __clang__
530#pragma clang diagnostic pop
531#endif
532
533#ifdef __clang__
534#pragma clang diagnostic push
535#pragma clang diagnostic ignored "-Wpadded"
536#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900537
Syoyo Fujitabde70212016-02-07 17:38:17 +0900538typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200539typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900540
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000541struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900542 int sampler; // required
543 int target_node; // required (index of the node to target)
544 std::string target_path; // required in ["translation", "rotation", "scale",
545 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900546 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200547 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100548 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900549
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900550 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
551 std::string extras_json_string;
552 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100553 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900554
Syoyo Fujita5b407452017-06-04 17:42:41 +0900555 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100556 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900557 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000558};
559
560struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900561 int input; // required
562 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200563 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
564 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200565 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900566 ExtensionMap extensions;
567
568 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
569 std::string extras_json_string;
570 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000571
Syoyo Fujita5b407452017-06-04 17:42:41 +0900572 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100573 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900574 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000575};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900576
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900577struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900578 std::string name;
579 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000580 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900581 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200582 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200583
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900584 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
585 std::string extras_json_string;
586 std::string extensions_json_string;
587
Selmar Kokff2b1f92019-10-21 17:58:09 +0200588 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100589 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900590 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900591};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900592
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000593struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900594 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900595 int inverseBindMatrices; // required here but not in the spec
596 int skeleton; // The index of the node used as a skeleton root
597 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000598
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900599 Value extras;
600 ExtensionMap extensions;
601
602 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
603 std::string extras_json_string;
604 std::string extensions_json_string;
605
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900606 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000607 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000608 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000609 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100610 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900611 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000612};
613
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000614struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900615 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900616 // glTF 2.0 spec does not define default value for `minFilter` and
617 // `magFilter`. Set -1 in TinyGLTF(issue #186)
618 int minFilter =
619 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800620 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900621 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
622 int magFilter =
623 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
624 int wrapS =
625 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
626 // "REPEAT"], default "REPEAT"
627 int wrapT =
628 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
629 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900630 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
631 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900632
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900633 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900634 ExtensionMap extensions;
635
636 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
637 std::string extras_json_string;
638 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900639
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000640 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900641 : minFilter(-1),
642 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800643 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900644 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100645 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900646 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000647};
648
Syoyo Fujita5b407452017-06-04 17:42:41 +0900649struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900650 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900651 int width;
652 int height;
653 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000654 int bits; // bit depth per channel. 8(byte), 16 or 32.
655 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
656 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900657 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900658 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500659 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
660 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900661 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
662 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900663 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900664 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900665
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900666 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
667 std::string extras_json_string;
668 std::string extensions_json_string;
669
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900670 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
671 // compressed for "image/jpeg" mime) This feature is good if you use custom
672 // image loader function. (e.g. delayed decoding of images for faster glTF
673 // parsing) Default parser for Image does not provide as-is loading feature at
674 // the moment. (You can manipulate this by providing your own LoadImageData
675 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200676 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900677
678 Image() : as_is(false) {
679 bufferView = -1;
680 width = -1;
681 height = -1;
682 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900683 bits = -1;
684 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900685 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100686 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500687
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900688 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000689};
690
691struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200692 std::string name;
693
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000694 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200695 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900696 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200697 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900698
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900699 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
700 std::string extras_json_string;
701 std::string extensions_json_string;
702
Syoyo Fujita5b407452017-06-04 17:42:41 +0900703 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100704 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500705
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900706 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000707};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900708
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900709struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900710 int index = -1; // required.
711 int texCoord; // The set index of texture's TEXCOORD attribute used for
712 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900713
714 Value extras;
715 ExtensionMap extensions;
716
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900717 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
718 std::string extras_json_string;
719 std::string extensions_json_string;
720
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900721 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100722 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900723 bool operator==(const TextureInfo &) const;
724};
725
726struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900727 int index = -1; // required
728 int texCoord; // The set index of texture's TEXCOORD attribute used for
729 // texture coordinate mapping.
730 double scale; // scaledNormal = normalize((<sampled normal texture value>
731 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900732
733 Value extras;
734 ExtensionMap extensions;
735
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900736 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
737 std::string extras_json_string;
738 std::string extensions_json_string;
739
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900740 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100741 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900742 bool operator==(const NormalTextureInfo &) const;
743};
744
745struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900746 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900747 int texCoord; // The set index of texture's TEXCOORD attribute used for
748 // texture coordinate mapping.
749 double strength; // occludedColor = lerp(color, color * <sampled occlusion
750 // texture value>, <occlusion strength>)
751
752 Value extras;
753 ExtensionMap extensions;
754
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900755 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
756 std::string extras_json_string;
757 std::string extensions_json_string;
758
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900759 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100760 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900761 bool operator==(const OcclusionTextureInfo &) const;
762};
763
764// pbrMetallicRoughness class defined in glTF 2.0 spec.
765struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900766 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900767 TextureInfo baseColorTexture;
768 double metallicFactor; // default 1
769 double roughnessFactor; // default 1
770 TextureInfo metallicRoughnessTexture;
771
772 Value extras;
773 ExtensionMap extensions;
774
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900775 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
776 std::string extras_json_string;
777 std::string extensions_json_string;
778
779 PbrMetallicRoughness()
780 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
781 metallicFactor(1.0),
782 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100783 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900784 bool operator==(const PbrMetallicRoughness &) const;
785};
786
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000787// Each extension should be stored in a ParameterMap.
788// members not in the values could be included in the ParameterMap
789// to keep a single material model
790struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900791 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900792
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900793 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900794 std::string alphaMode; // default "OPAQUE"
795 double alphaCutoff; // default 0.5
796 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900797
798 PbrMetallicRoughness pbrMetallicRoughness;
799
800 NormalTextureInfo normalTexture;
801 OcclusionTextureInfo occlusionTexture;
802 TextureInfo emissiveTexture;
803
Syoyo Fujita046400b2019-07-24 19:26:48 +0900804 // For backward compatibility
805 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
806 ParameterMap values;
807 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100808
809 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900810 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200811
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900812 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
813 std::string extras_json_string;
814 std::string extensions_json_string;
815
Syoyo Fujita046400b2019-07-24 19:26:48 +0900816 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100817 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900818
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900819 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000820};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900821
Syoyo Fujita5b407452017-06-04 17:42:41 +0900822struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900823 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900824 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900825 size_t byteOffset{0}; // minimum 0, default 0
826 size_t byteLength{0}; // required, minimum 1. 0 = invalid
827 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900828 // understood to be tightly packed
829 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
imallettd9ce9eb2022-10-07 10:37:09 -0700830 // or attribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900831 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900832 ExtensionMap extensions;
833
834 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
835 std::string extras_json_string;
836 std::string extensions_json_string;
837
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900838 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900839
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900840 BufferView()
841 : buffer(-1),
842 byteOffset(0),
843 byteLength(0),
844 byteStride(0),
845 target(0),
846 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100847 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900848 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000849};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900850
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000851struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900852 int bufferView; // optional in spec but required here since sparse accessor
853 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900854 std::string name;
855 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100856 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000857 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900858 size_t count; // required
859 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900860 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900861 ExtensionMap extensions;
862
863 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
864 std::string extras_json_string;
865 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000866
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900867 std::vector<double>
868 minValues; // optional. integer value is promoted to double
869 std::vector<double>
870 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900871
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100872 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000873 int count;
874 bool isSparse;
875 struct {
876 int byteOffset;
877 int bufferView;
878 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
879 } indices;
880 struct {
881 int bufferView;
882 int byteOffset;
883 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100884 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000885
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900886 ///
887 /// Utility function to compute byteStride for a given bufferView object.
888 /// Returns -1 upon invalid glTF value or parameter configuration.
889 ///
890 int ByteStride(const BufferView &bufferViewObject) const {
891 if (bufferViewObject.byteStride == 0) {
892 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500893 int componentSizeInBytes =
894 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900895 if (componentSizeInBytes <= 0) {
896 return -1;
897 }
898
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900899 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
900 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900901 return -1;
902 }
903
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900904 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900905 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700906 // Check if byteStride is a multiple of the size of the accessor's component
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500907 // type.
908 int componentSizeInBytes =
909 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900910 if (componentSizeInBytes <= 0) {
911 return -1;
912 }
913
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900914 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900915 return -1;
916 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100917 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900918 }
919
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900920 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900921 }
922
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900923 Accessor()
924 : bufferView(-1),
925 byteOffset(0),
926 normalized(false),
927 componentType(-1),
928 count(0),
929 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000930 sparse.isSparse = false;
931 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100932 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900933 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000934};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900935
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900936struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200937 double aspectRatio; // min > 0
938 double yfov; // required. min > 0
939 double zfar; // min > 0
940 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900941
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900942 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900943 : aspectRatio(0.0),
944 yfov(0.0),
imallettd9ce9eb2022-10-07 10:37:09 -0700945 zfar(0.0) // 0 = use infinite projection matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900946 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900947 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100948 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900949 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900950
Selmar09d2ff12018-03-15 17:30:42 +0100951 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900952 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900953
954 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
955 std::string extras_json_string;
956 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900957};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000958
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900959struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200960 double xmag; // required. must not be zero.
961 double ymag; // required. must not be zero.
962 double zfar; // required. `zfar` must be greater than `znear`.
963 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000964
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900965 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100966 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900967 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900968
Selmar09d2ff12018-03-15 17:30:42 +0100969 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900970 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900971
972 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
973 std::string extras_json_string;
974 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900975};
976
977struct Camera {
978 std::string type; // required. "perspective" or "orthographic"
979 std::string name;
980
981 PerspectiveCamera perspective;
982 OrthographicCamera orthographic;
983
984 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100985 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900986 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900987
Selmar09d2ff12018-03-15 17:30:42 +0100988 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000989 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900990
991 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
992 std::string extras_json_string;
993 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900994};
995
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000996struct Primitive {
997 std::map<std::string, int> attributes; // (required) A dictionary object of
998 // integer, where each integer
999 // is the index of the accessor
1000 // containing an attribute.
1001 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +09001002 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001003 int indices; // The index of the accessor that contains the indices.
1004 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001005 std::vector<std::map<std::string, int> > targets; // array of morph targets,
imallettd9ce9eb2022-10-07 10:37:09 -07001006 // where each target is a dict with attributes in ["POSITION, "NORMAL",
Syoyo Fujita5b407452017-06-04 17:42:41 +09001007 // "TANGENT"] pointing
1008 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -05001009 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001010 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001011
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001012 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1013 std::string extras_json_string;
1014 std::string extensions_json_string;
1015
Syoyo Fujita5b407452017-06-04 17:42:41 +09001016 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001017 material = -1;
1018 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001019 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001020 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001021 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001022 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001023};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001024
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001025struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001026 std::string name;
1027 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001028 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001029 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001030 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001031
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001032 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1033 std::string extras_json_string;
1034 std::string extensions_json_string;
1035
jrkoonced1e14722019-08-27 11:51:02 -05001036 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001037 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001038 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001039};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001040
1041class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001042 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001043 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001044
Selmar Kokb74fade2019-10-29 16:09:32 +01001045 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001046
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001047 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001048
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001049 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001050
1051 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001052 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001053 int mesh;
1054 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001055 std::vector<double> rotation; // length must be 0 or 4
1056 std::vector<double> scale; // length must be 0 or 3
1057 std::vector<double> translation; // length must be 0 or 3
1058 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001059 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001060
Selmar09d2ff12018-03-15 17:30:42 +01001061 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001062 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001063
1064 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1065 std::string extras_json_string;
1066 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001067};
1068
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001069struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001070 std::string name;
1071 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001072 std::string
1073 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001074 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001075 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001076 ExtensionMap extensions;
1077
1078 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1079 std::string extras_json_string;
1080 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001081
Selmar Kokb74fade2019-10-29 16:09:32 +01001082 Buffer() = default;
1083 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001084 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001085};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001086
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001087struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001088 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001089 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001090 std::string minVersion;
1091 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001092 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001093 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001094
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001095 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1096 std::string extras_json_string;
1097 std::string extensions_json_string;
1098
jrkoonced1e14722019-08-27 11:51:02 -05001099 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001100 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001101 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001102};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001103
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001104struct Scene {
1105 std::string name;
1106 std::vector<int> nodes;
1107
Selmar09d2ff12018-03-15 17:30:42 +01001108 ExtensionMap extensions;
1109 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001110
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001111 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1112 std::string extras_json_string;
1113 std::string extensions_json_string;
1114
jrkoonced1e14722019-08-27 11:51:02 -05001115 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001116 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001117 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001118};
1119
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001120struct SpotLight {
1121 double innerConeAngle;
1122 double outerConeAngle;
1123
Johan Bowald52936a02019-07-17 09:06:45 +02001124 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001125 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001126 bool operator==(const SpotLight &) const;
1127
1128 ExtensionMap extensions;
1129 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001130
1131 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1132 std::string extras_json_string;
1133 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001134};
1135
Emanuel Schrade186322b2017-11-06 11:14:41 +01001136struct Light {
1137 std::string name;
1138 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001139 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001140 std::string type;
imallettd9ce9eb2022-10-07 10:37:09 -07001141 double range{0.0}; // 0.0 = infinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001142 SpotLight spot;
1143
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001144 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001145 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001146
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001147 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001148
1149 ExtensionMap extensions;
1150 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001151
1152 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1153 std::string extras_json_string;
1154 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001155};
1156
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001157class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001158 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001159 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001160 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001161
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001162 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001163
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001164 std::vector<Accessor> accessors;
1165 std::vector<Animation> animations;
1166 std::vector<Buffer> buffers;
1167 std::vector<BufferView> bufferViews;
1168 std::vector<Material> materials;
1169 std::vector<Mesh> meshes;
1170 std::vector<Node> nodes;
1171 std::vector<Texture> textures;
1172 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001173 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001174 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001175 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001176 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001177 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001178
sammyKhana0a62bd2020-01-17 13:41:16 +01001179 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001180 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001181 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001182
1183 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001184
1185 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001186 ExtensionMap extensions;
1187
1188 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1189 std::string extras_json_string;
1190 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001191};
1192
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001193enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001194 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001195 REQUIRE_VERSION = 0x01,
1196 REQUIRE_SCENE = 0x02,
1197 REQUIRE_SCENES = 0x04,
1198 REQUIRE_NODES = 0x08,
1199 REQUIRE_ACCESSORS = 0x10,
1200 REQUIRE_BUFFERS = 0x20,
1201 REQUIRE_BUFFER_VIEWS = 0x40,
1202 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001203};
1204
Squareysff644d82018-03-13 22:36:18 +01001205///
1206/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1207///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001208typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1209 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001210 const unsigned char *, int,
1211 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001212
johan bowald642a3432018-04-01 12:37:18 +02001213///
1214/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1215///
1216typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1217 Image *, bool, void *);
1218
Squareys2d3594d2018-03-13 22:40:53 +01001219#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001220// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001221bool LoadImageData(Image *image, const int image_idx, std::string *err,
1222 std::string *warn, int req_width, int req_height,
1223 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001224#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001225
johan bowald642a3432018-04-01 12:37:18 +02001226#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1227// Declaration of default image writer callback
1228bool WriteImageData(const std::string *basepath, const std::string *filename,
1229 Image *image, bool embedImages, void *);
1230#endif
1231
Paolo Jovone6601bf2018-07-07 20:43:33 +02001232///
1233/// FilExistsFunction type. Signature for custom filesystem callbacks.
1234///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001235typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001236
1237///
1238/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1239///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001240typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001241
1242///
1243/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1244///
1245typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001246 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001247 void *);
1248
1249///
1250/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1251///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001252typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001253 const std::vector<unsigned char> &,
1254 void *);
1255
1256///
1257/// A structure containing all required filesystem callbacks and a pointer to
1258/// their user data.
1259///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001260struct FsCallbacks {
1261 FileExistsFunction FileExists;
1262 ExpandFilePathFunction ExpandFilePath;
1263 ReadWholeFileFunction ReadWholeFile;
1264 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001265
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001266 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001267};
1268
1269#ifndef TINYGLTF_NO_FS
1270// Declaration of default filesystem callbacks
1271
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001272bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001273
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001274///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001275/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001276/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001277///
1278/// @param[in] filepath File path string. Assume UTF-8
1279/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1280///
1281std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001282
1283bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001284 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001285
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001286bool WriteWholeFile(std::string *err, const std::string &filepath,
1287 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001288#endif
1289
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001290///
imallettd9ce9eb2022-10-07 10:37:09 -07001291/// glTF Parser/Serializer context.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001292///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001293class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001294 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001295#ifdef __clang__
1296#pragma clang diagnostic push
1297#pragma clang diagnostic ignored "-Wc++98-compat"
1298#endif
1299
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001300 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001301
1302#ifdef __clang__
1303#pragma clang diagnostic pop
1304#endif
1305
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001306 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001307
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001308 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001309 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001310 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001311 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001312 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001313 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001314 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001315 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001316
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001317 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001318 /// Loads glTF ASCII asset from string(memory).
1319 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001320 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1321 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1322 /// message to `warn` for example it fails to load asserts. Returns false and
1323 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001324 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001325 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1326 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001327 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001328 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001329
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001330 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001331 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001332 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001333 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001334 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001335 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001336 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001337 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001338
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001339 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001340 /// Loads glTF binary asset from memory.
1341 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001342 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1343 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001344 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001345 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001346 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001347 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001348 const unsigned char *bytes,
1349 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001350 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001351 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001352
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001353 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001354 /// Write glTF to stream, buffers and images will be embedded
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001355 ///
1356 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1357 bool prettyPrint, bool writeBinary);
1358
1359 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001360 /// Write glTF to file.
1361 ///
johan bowald642a3432018-04-01 12:37:18 +02001362 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001363 bool embedImages, bool embedBuffers,
1364 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001365
Squareysff644d82018-03-13 22:36:18 +01001366 ///
1367 /// Set callback to use for loading image data
1368 ///
1369 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1370
johan bowald642a3432018-04-01 12:37:18 +02001371 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001372 /// Unset(remove) callback of loading image data
1373 ///
1374 void RemoveImageLoader();
1375
1376 ///
johan bowald642a3432018-04-01 12:37:18 +02001377 /// Set callback to use for writing image data
1378 ///
1379 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1380
Paolo Jovone6601bf2018-07-07 20:43:33 +02001381 ///
1382 /// Set callbacks to use for filesystem (fs) access and their user data
1383 ///
1384 void SetFsCallbacks(FsCallbacks callbacks);
1385
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001386 ///
1387 /// Set serializing default values(default = false).
1388 /// When true, default values are force serialized to .glTF.
imallettd9ce9eb2022-10-07 10:37:09 -07001389 /// This may be helpful if you want to serialize a full description of glTF
Syoyo Fujitaff515702019-08-24 16:29:14 +09001390 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001391 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001392 /// TODO(LTE): Supply parsing option as function arguments to
1393 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001394 ///
1395 void SetSerializeDefaultValues(const bool enabled) {
1396 serialize_default_values_ = enabled;
1397 }
1398
Syoyo Fujitaff515702019-08-24 16:29:14 +09001399 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001400
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001401 ///
1402 /// Store original JSON string for `extras` and `extensions`.
1403 /// This feature will be useful when the user want to reconstruct custom data
1404 /// structure from JSON string.
1405 ///
1406 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1407 store_original_json_for_extras_and_extensions_ = enabled;
1408 }
1409
1410 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1411 return store_original_json_for_extras_and_extensions_;
1412 }
1413
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001414 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001415 /// Specify whether preserve image channels when loading images or not.
1416 /// (Not effective when the user supplies their own LoadImageData callbacks)
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001417 ///
1418 void SetPreserveImageChannels(bool onoff) {
1419 preserve_image_channels_ = onoff;
1420 }
1421
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001422 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001423
Syoyo Fujitabeded612016-05-01 20:03:43 +09001424 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001425 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001426 /// Loads glTF asset from string(memory).
1427 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001428 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001429 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001430 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001431 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1432 const char *str, const unsigned int length,
1433 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001434
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001435 const unsigned char *bin_data_ = nullptr;
1436 size_t bin_size_ = 0;
1437 bool is_binary_ = false;
1438
Syoyo Fujitaff515702019-08-24 16:29:14 +09001439 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001440
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001441 bool store_original_json_for_extras_and_extensions_ = false;
1442
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001443 bool preserve_image_channels_ = false; /// Default false(expand channels to
1444 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001445
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001446 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001447 std::string warn_;
1448 std::string err_;
1449
Paolo Jovone6601bf2018-07-07 20:43:33 +02001450 FsCallbacks fs = {
1451#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001452 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1453 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001454
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001455 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001456#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001457 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001458
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001459 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001460#endif
1461 };
1462
Squareysff644d82018-03-13 22:36:18 +01001463 LoadImageDataFunction LoadImageData =
1464#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001465 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001466#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001467 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001468#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001469 void *load_image_user_data_{nullptr};
1470 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001471
1472 WriteImageDataFunction WriteImageData =
1473#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001474 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001475#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001476 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001477#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001478 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001479};
1480
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001481#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001482#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001483#endif
1484
Syoyo Fujita7c877972016-03-08 01:31:49 +09001485} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001486
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001487#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001488
Selmar Kok31cb7f92018-10-03 15:39:05 +02001489#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001490#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001491//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001492#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001493#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001494#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001495#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001496#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001497
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001498#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001499// Disable some warnings for external files.
1500#pragma clang diagnostic push
1501#pragma clang diagnostic ignored "-Wfloat-equal"
1502#pragma clang diagnostic ignored "-Wexit-time-destructors"
1503#pragma clang diagnostic ignored "-Wconversion"
1504#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001505#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001506#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001507#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001508#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001509#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1510#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001511#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001512#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001513#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1514#pragma clang diagnostic ignored "-Wswitch-enum"
1515#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001516#pragma clang diagnostic ignored "-Wweak-vtables"
1517#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001518#if __has_warning("-Wdouble-promotion")
1519#pragma clang diagnostic ignored "-Wdouble-promotion"
1520#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001521#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001522#pragma clang diagnostic ignored "-Wcomma"
1523#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001524#if __has_warning("-Wzero-as-null-pointer-constant")
1525#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1526#endif
1527#if __has_warning("-Wcast-qual")
1528#pragma clang diagnostic ignored "-Wcast-qual"
1529#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001530#if __has_warning("-Wmissing-variable-declarations")
1531#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1532#endif
1533#if __has_warning("-Wmissing-prototypes")
1534#pragma clang diagnostic ignored "-Wmissing-prototypes"
1535#endif
1536#if __has_warning("-Wcast-align")
1537#pragma clang diagnostic ignored "-Wcast-align"
1538#endif
1539#if __has_warning("-Wnewline-eof")
1540#pragma clang diagnostic ignored "-Wnewline-eof"
1541#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001542#if __has_warning("-Wunused-parameter")
1543#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001544#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001545#if __has_warning("-Wmismatched-tags")
1546#pragma clang diagnostic ignored "-Wmismatched-tags"
1547#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001548#if __has_warning("-Wextra-semi-stmt")
1549#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1550#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001551#endif
1552
imallettd9ce9eb2022-10-07 10:37:09 -07001553// Disable GCC warnings
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001554#ifdef __GNUC__
1555#pragma GCC diagnostic push
1556#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001557#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001558
krokofc0116b2019-03-03 08:28:49 +02001559#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001560#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001561#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001562#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001563#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001564#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001565#include "prettywriter.h"
1566#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001567#include "stringbuffer.h"
1568#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001569#endif
krokof4b6d112019-03-03 01:11:31 +02001570#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001571#endif
Alex Wood7319db72019-01-24 15:38:16 -05001572
1573#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001574#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001575#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001576#endif
Squareys2d3594d2018-03-13 22:40:53 +01001577
1578#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001579#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001580#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001581#endif
krokof4b6d112019-03-03 01:11:31 +02001582#endif
Squareys2d3594d2018-03-13 22:40:53 +01001583
johan bowald642a3432018-04-01 12:37:18 +02001584#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001585#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001586#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001587#endif
krokof4b6d112019-03-03 01:11:31 +02001588#endif
johan bowald642a3432018-04-01 12:37:18 +02001589
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001590#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001591#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001592#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001593
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001594#ifdef __GNUC__
1595#pragma GCC diagnostic pop
1596#endif
1597
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001598#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001599
1600// issue 143.
1601// Define NOMINMAX to avoid min/max defines,
1602// but undef it after included windows.h
1603#ifndef NOMINMAX
1604#define TINYGLTF_INTERNAL_NOMINMAX
1605#define NOMINMAX
1606#endif
1607
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001608#ifndef WIN32_LEAN_AND_MEAN
1609#define WIN32_LEAN_AND_MEAN
1610#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1611#endif
imallett56e10982022-10-07 10:35:16 -07001612#include <Windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001613
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001614#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1615#undef WIN32_LEAN_AND_MEAN
1616#endif
1617
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001618#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1619#undef NOMINMAX
1620#endif
1621
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001622#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001623
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001624#include <fcntl.h> // _O_RDONLY
1625
1626#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001627
1628#endif
1629
Julian Smith0598a202021-08-25 12:06:08 +01001630#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001631//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001632#endif
1633
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001634#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001635// Big endian
1636#else
1637#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1638#define TINYGLTF_LITTLE_ENDIAN 1
1639#endif
1640#endif
1641
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001642namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001643#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001644
1645#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001646// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1647// documents may be active at once.
1648using json =
1649 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1650using json_const_iterator = json::ConstMemberIterator;
1651using json_const_array_iterator = json const *;
1652using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001653 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001654rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1655rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001656#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001657// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1658// not thread safe. Only a single JsonDocument may be active at any one time,
1659// meaning only a single gltf load/save can be active any one time.
1660using json = rapidjson::Value;
1661using json_const_iterator = json::ConstMemberIterator;
1662using json_const_array_iterator = json const *;
1663rapidjson::Document *s_pActiveDocument = nullptr;
1664rapidjson::Document::AllocatorType &GetAllocator() {
1665 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1666 return s_pActiveDocument->GetAllocator();
1667}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001668
1669#ifdef __clang__
1670#pragma clang diagnostic push
1671// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1672#pragma clang diagnostic ignored "-Wunused-member-function"
1673#endif
1674
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001675struct JsonDocument : public rapidjson::Document {
1676 JsonDocument() {
1677 assert(s_pActiveDocument ==
1678 nullptr); // When using default allocator, only one document can be
1679 // active at a time, if you need multiple active at once,
1680 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1681 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001682 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001683 JsonDocument(const JsonDocument &) = delete;
1684 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001685 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001686 s_pActiveDocument = this;
1687 rhs.isNil = true;
1688 }
1689 ~JsonDocument() {
1690 if (!isNil) {
1691 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001692 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001693 }
jrkooncece7fa742019-09-04 13:31:44 -05001694
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001695 private:
1696 bool isNil = false;
1697};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001698
1699#ifdef __clang__
1700#pragma clang diagnostic pop
1701#endif
1702
jrkooncece7fa742019-09-04 13:31:44 -05001703#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001704
jrkooncecba5d6c2019-08-29 11:26:22 -05001705#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001706using nlohmann::json;
1707using json_const_iterator = json::const_iterator;
1708using json_const_array_iterator = json_const_iterator;
1709using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001710#endif
1711
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001712void JsonParse(JsonDocument &doc, const char *str, size_t length,
1713 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001714#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001715 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001716 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001717#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001718 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001719#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001720}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001721} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001722
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001723#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001724#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001725#endif
1726
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001727#ifdef __clang__
1728#pragma clang diagnostic push
1729#pragma clang diagnostic ignored "-Wc++98-compat"
1730#endif
1731
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001732namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001733
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001734///
1735/// Internal LoadImageDataOption struct.
1736/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001737/// The struct is not passed when the user supply their own LoadImageData
1738/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001739///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001740struct LoadImageDataOption {
1741 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1742 // channels) default `false`(channels are expanded to RGBA for backward
imallettd9ce9eb2022-10-07 10:37:09 -07001743 // compatibility).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001744 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001745};
1746
Selmar Kok31cb7f92018-10-03 15:39:05 +02001747// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001748static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1749 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001750
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001751 switch (one.Type()) {
1752 case NULL_TYPE:
1753 return true;
1754 case BOOL_TYPE:
1755 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001756 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001757 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1758 case INT_TYPE:
1759 return one.Get<int>() == other.Get<int>();
1760 case OBJECT_TYPE: {
1761 auto oneObj = one.Get<tinygltf::Value::Object>();
1762 auto otherObj = other.Get<tinygltf::Value::Object>();
1763 if (oneObj.size() != otherObj.size()) return false;
1764 for (auto &it : oneObj) {
1765 auto otherIt = otherObj.find(it.first);
1766 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001767
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001768 if (!Equals(it.second, otherIt->second)) return false;
1769 }
1770 return true;
1771 }
1772 case ARRAY_TYPE: {
1773 if (one.Size() != other.Size()) return false;
1774 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001775 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001776 return true;
1777 }
1778 case STRING_TYPE:
1779 return one.Get<std::string>() == other.Get<std::string>();
1780 case BINARY_TYPE:
1781 return one.Get<std::vector<unsigned char> >() ==
1782 other.Get<std::vector<unsigned char> >();
1783 default: {
1784 // unhandled type
1785 return false;
1786 }
1787 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001788}
1789
1790// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001791static bool Equals(const std::vector<double> &one,
1792 const std::vector<double> &other) {
1793 if (one.size() != other.size()) return false;
1794 for (int i = 0; i < int(one.size()); ++i) {
1795 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1796 }
1797 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001798}
1799
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001800bool Accessor::operator==(const Accessor &other) const {
1801 return this->bufferView == other.bufferView &&
1802 this->byteOffset == other.byteOffset &&
1803 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001804 this->count == other.count && this->extensions == other.extensions &&
1805 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001806 Equals(this->maxValues, other.maxValues) &&
1807 Equals(this->minValues, other.minValues) && this->name == other.name &&
1808 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001809}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001810bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001811 return this->channels == other.channels &&
1812 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001813 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001814}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001815bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001816 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001817 this->target_node == other.target_node &&
1818 this->target_path == other.target_path &&
1819 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001820}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001821bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001822 return this->extras == other.extras && this->extensions == other.extensions &&
1823 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001824 this->interpolation == other.interpolation &&
1825 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001826}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001827bool Asset::operator==(const Asset &other) const {
1828 return this->copyright == other.copyright &&
1829 this->extensions == other.extensions && this->extras == other.extras &&
1830 this->generator == other.generator &&
1831 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001832}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001833bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001834 return this->data == other.data && this->extensions == other.extensions &&
1835 this->extras == other.extras && this->name == other.name &&
1836 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001837}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001838bool BufferView::operator==(const BufferView &other) const {
1839 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1840 this->byteOffset == other.byteOffset &&
1841 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001842 this->target == other.target && this->extensions == other.extensions &&
1843 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001844 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001845}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001846bool Camera::operator==(const Camera &other) const {
1847 return this->name == other.name && this->extensions == other.extensions &&
1848 this->extras == other.extras &&
1849 this->orthographic == other.orthographic &&
1850 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001851}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001852bool Image::operator==(const Image &other) const {
1853 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001854 this->component == other.component &&
1855 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001856 this->height == other.height && this->image == other.image &&
1857 this->mimeType == other.mimeType && this->name == other.name &&
1858 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001859}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001860bool Light::operator==(const Light &other) const {
1861 return Equals(this->color, other.color) && this->name == other.name &&
1862 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001863}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001864bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001865 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1866 (this->normalTexture == other.normalTexture) &&
1867 (this->occlusionTexture == other.occlusionTexture) &&
1868 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001869 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001870 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001871 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001872 (this->doubleSided == other.doubleSided) &&
1873 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001874 (this->extras == other.extras) && (this->values == other.values) &&
1875 (this->additionalValues == other.additionalValues) &&
1876 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001877}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001878bool Mesh::operator==(const Mesh &other) const {
1879 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001880 this->name == other.name && Equals(this->weights, other.weights) &&
1881 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001882}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001883bool Model::operator==(const Model &other) const {
1884 return this->accessors == other.accessors &&
1885 this->animations == other.animations && this->asset == other.asset &&
1886 this->buffers == other.buffers &&
1887 this->bufferViews == other.bufferViews &&
1888 this->cameras == other.cameras &&
1889 this->defaultScene == other.defaultScene &&
1890 this->extensions == other.extensions &&
1891 this->extensionsRequired == other.extensionsRequired &&
1892 this->extensionsUsed == other.extensionsUsed &&
1893 this->extras == other.extras && this->images == other.images &&
1894 this->lights == other.lights && this->materials == other.materials &&
1895 this->meshes == other.meshes && this->nodes == other.nodes &&
1896 this->samplers == other.samplers && this->scenes == other.scenes &&
1897 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001898}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001899bool Node::operator==(const Node &other) const {
1900 return this->camera == other.camera && this->children == other.children &&
1901 this->extensions == other.extensions && this->extras == other.extras &&
1902 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1903 this->name == other.name && Equals(this->rotation, other.rotation) &&
1904 Equals(this->scale, other.scale) && this->skin == other.skin &&
1905 Equals(this->translation, other.translation) &&
1906 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001907}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001908bool SpotLight::operator==(const SpotLight &other) const {
1909 return this->extensions == other.extensions && this->extras == other.extras &&
1910 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1911 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1912}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001913bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1914 return this->extensions == other.extensions && this->extras == other.extras &&
1915 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1916 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1917 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1918 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001919}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001920bool Parameter::operator==(const Parameter &other) const {
1921 if (this->bool_value != other.bool_value ||
1922 this->has_number_value != other.has_number_value)
1923 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001924
Selmar Kok2bda71c2018-10-05 14:36:05 +02001925 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1926 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001927
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001928 if (this->json_double_value.size() != other.json_double_value.size())
1929 return false;
1930 for (auto &it : this->json_double_value) {
1931 auto otherIt = other.json_double_value.find(it.first);
1932 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001933
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001934 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1935 }
1936
1937 if (!Equals(this->number_array, other.number_array)) return false;
1938
1939 if (this->string_value != other.string_value) return false;
1940
1941 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001942}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001943bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1944 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1945 this->extensions == other.extensions && this->extras == other.extras &&
1946 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1947 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1948 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001949}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001950bool Primitive::operator==(const Primitive &other) const {
1951 return this->attributes == other.attributes && this->extras == other.extras &&
1952 this->indices == other.indices && this->material == other.material &&
1953 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001954}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001955bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001956 return this->extensions == other.extensions && this->extras == other.extras &&
1957 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001958 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001959 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001960
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001961 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02001962}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001963bool Scene::operator==(const Scene &other) const {
1964 return this->extensions == other.extensions && this->extras == other.extras &&
1965 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001966}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001967bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001968 return this->extensions == other.extensions && this->extras == other.extras &&
1969 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001970 this->joints == other.joints && this->name == other.name &&
1971 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001972}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001973bool Texture::operator==(const Texture &other) const {
1974 return this->extensions == other.extensions && this->extras == other.extras &&
1975 this->name == other.name && this->sampler == other.sampler &&
1976 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001977}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001978bool TextureInfo::operator==(const TextureInfo &other) const {
1979 return this->extensions == other.extensions && this->extras == other.extras &&
1980 this->index == other.index && this->texCoord == other.texCoord;
1981}
1982bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1983 return this->extensions == other.extensions && this->extras == other.extras &&
1984 this->index == other.index && this->texCoord == other.texCoord &&
1985 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
1986}
1987bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
1988 return this->extensions == other.extensions && this->extras == other.extras &&
1989 this->index == other.index && this->texCoord == other.texCoord &&
1990 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
1991}
1992bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
1993 return this->extensions == other.extensions && this->extras == other.extras &&
1994 (this->baseColorTexture == other.baseColorTexture) &&
1995 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001996 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001997 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
1998 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
1999}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002000bool Value::operator==(const Value &other) const {
2001 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002002}
2003
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002004static void swap4(unsigned int *val) {
2005#ifdef TINYGLTF_LITTLE_ENDIAN
2006 (void)val;
2007#else
2008 unsigned int tmp = *val;
2009 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2010 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2011
2012 dst[0] = src[3];
2013 dst[1] = src[2];
2014 dst[2] = src[1];
2015 dst[3] = src[0];
2016#endif
2017}
2018
Syoyo Fujitabeded612016-05-01 20:03:43 +09002019static std::string JoinPath(const std::string &path0,
2020 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002021 if (path0.empty()) {
2022 return path1;
2023 } else {
2024 // check '/'
2025 char lastChar = *path0.rbegin();
2026 if (lastChar != '/') {
2027 return path0 + std::string("/") + path1;
2028 } else {
2029 return path0 + path1;
2030 }
2031 }
2032}
2033
Syoyo Fujita643ce102016-05-01 17:19:37 +09002034static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002035 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002036 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2037 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002038 // Error, fs callback[s] missing
2039 return std::string();
2040 }
2041
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002042 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002043 std::string absPath =
2044 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002045 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002046 return absPath;
2047 }
2048 }
2049
2050 return std::string();
2051}
2052
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002053static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002054 if (FileName.find_last_of(".") != std::string::npos)
2055 return FileName.substr(FileName.find_last_of(".") + 1);
2056 return "";
2057}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002058
Syoyo Fujita643ce102016-05-01 17:19:37 +09002059static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002060 if (filepath.find_last_of("/\\") != std::string::npos)
2061 return filepath.substr(0, filepath.find_last_of("/\\"));
2062 return "";
2063}
2064
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002065static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002066 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002067 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002068 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002069}
2070
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002071std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002072std::string base64_decode(std::string const &s);
2073
2074/*
2075 base64.cpp and base64.h
2076
2077 Copyright (C) 2004-2008 René Nyffenegger
2078
2079 This source code is provided 'as-is', without any express or implied
2080 warranty. In no event will the author be held liable for any damages
2081 arising from the use of this software.
2082
2083 Permission is granted to anyone to use this software for any purpose,
2084 including commercial applications, and to alter it and redistribute it
2085 freely, subject to the following restrictions:
2086
2087 1. The origin of this source code must not be misrepresented; you must not
2088 claim that you wrote the original source code. If you use this source code
2089 in a product, an acknowledgment in the product documentation would be
2090 appreciated but is not required.
2091
2092 2. Altered source versions must be plainly marked as such, and must not be
2093 misrepresented as being the original source code.
2094
2095 3. This notice may not be removed or altered from any source distribution.
2096
2097 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2098
2099*/
2100
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002101#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002102#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002103#pragma clang diagnostic ignored "-Wsign-conversion"
2104#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002105#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002106
2107static inline bool is_base64(unsigned char c) {
2108 return (isalnum(c) || (c == '+') || (c == '/'));
2109}
2110
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002111std::string base64_encode(unsigned char const *bytes_to_encode,
2112 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002113 std::string ret;
2114 int i = 0;
2115 int j = 0;
2116 unsigned char char_array_3[3];
2117 unsigned char char_array_4[4];
2118
Syoyo Fujitaff515702019-08-24 16:29:14 +09002119 const char *base64_chars =
2120 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2121 "abcdefghijklmnopqrstuvwxyz"
2122 "0123456789+/";
2123
johan bowald30c53472018-03-30 11:49:36 +02002124 while (in_len--) {
2125 char_array_3[i++] = *(bytes_to_encode++);
2126 if (i == 3) {
2127 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002128 char_array_4[1] =
2129 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2130 char_array_4[2] =
2131 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002132 char_array_4[3] = char_array_3[2] & 0x3f;
2133
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002134 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002135 i = 0;
2136 }
2137 }
2138
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002139 if (i) {
2140 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002141
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002142 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2143 char_array_4[1] =
2144 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2145 char_array_4[2] =
2146 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002147
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002148 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002149
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002150 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002151 }
2152
2153 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002154}
2155
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002156std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002157 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002158 int i = 0;
2159 int j = 0;
2160 int in_ = 0;
2161 unsigned char char_array_4[4], char_array_3[3];
2162 std::string ret;
2163
Syoyo Fujitaff515702019-08-24 16:29:14 +09002164 const std::string base64_chars =
2165 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2166 "abcdefghijklmnopqrstuvwxyz"
2167 "0123456789+/";
2168
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002169 while (in_len-- && (encoded_string[in_] != '=') &&
2170 is_base64(encoded_string[in_])) {
2171 char_array_4[i++] = encoded_string[in_];
2172 in_++;
2173 if (i == 4) {
2174 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002175 char_array_4[i] =
2176 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002177
2178 char_array_3[0] =
2179 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2180 char_array_3[1] =
2181 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2182 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2183
Syoyo Fujita7c877972016-03-08 01:31:49 +09002184 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002185 i = 0;
2186 }
2187 }
2188
2189 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002190 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002191
2192 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002193 char_array_4[j] =
2194 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002195
2196 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2197 char_array_3[1] =
2198 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2199 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2200
Syoyo Fujita7c877972016-03-08 01:31:49 +09002201 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002202 }
2203
2204 return ret;
2205}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002206#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002207#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002208#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002209
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002210// https://github.com/syoyo/tinygltf/issues/228
2211// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2212// decoding?
2213//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002214// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002215// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002216// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002217// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002218// License: Boost Software License
2219// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002220
Alexander Woode4bc6c72021-10-14 08:54:59 -04002221// Permission is hereby granted, free of charge, to any person or organization
2222// obtaining a copy of the software and accompanying documentation covered by
2223// this license (the "Software") to use, reproduce, display, distribute,
2224// execute, and transmit the Software, and to prepare derivative works of the
2225// Software, and to permit third-parties to whom the Software is furnished to
2226// do so, all subject to the following:
2227// The copyright notices in the Software and this entire statement, including
2228// the above license grant, this restriction and the following disclaimer,
2229// must be included in all copies of the Software, in whole or in part, and
2230// all derivative works of the Software, unless such copies or derivative
2231// works are solely in the form of machine-executable object code generated by
2232// a source language processor.
2233// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2234// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2235// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2236// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2237// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2238// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2239// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002240//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002241namespace dlib {
2242
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002243inline unsigned char from_hex(unsigned char ch) {
2244 if (ch <= '9' && ch >= '0')
2245 ch -= '0';
2246 else if (ch <= 'f' && ch >= 'a')
2247 ch -= 'a' - 10;
2248 else if (ch <= 'F' && ch >= 'A')
2249 ch -= 'A' - 10;
2250 else
2251 ch = 0;
2252 return ch;
2253}
2254
2255static const std::string urldecode(const std::string &str) {
2256 using namespace std;
2257 string result;
2258 string::size_type i;
2259 for (i = 0; i < str.size(); ++i) {
2260 if (str[i] == '+') {
2261 result += ' ';
2262 } else if (str[i] == '%' && str.size() > i + 2) {
2263 const unsigned char ch1 =
2264 from_hex(static_cast<unsigned char>(str[i + 1]));
2265 const unsigned char ch2 =
2266 from_hex(static_cast<unsigned char>(str[i + 2]));
2267 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2268 result += static_cast<char>(ch);
2269 i += 2;
2270 } else {
2271 result += str[i];
2272 }
2273 }
2274 return result;
2275}
2276
2277} // namespace dlib
2278// --- dlib end --------------------------------------------------------------
2279
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002280static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002281 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002282 const std::string &basedir, bool required,
2283 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002284 if (fs == nullptr || fs->FileExists == nullptr ||
2285 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002286 // This is a developer error, assert() ?
2287 if (err) {
2288 (*err) += "FS callback[s] not set\n";
2289 }
2290 return false;
2291 }
2292
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002293 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002294
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002295 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002296
2297 std::vector<std::string> paths;
2298 paths.push_back(basedir);
2299 paths.push_back(".");
2300
Paolo Jovone6601bf2018-07-07 20:43:33 +02002301 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002302 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002303 if (failMsgOut) {
2304 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002305 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002306 return false;
2307 }
2308
Paolo Jovone6601bf2018-07-07 20:43:33 +02002309 std::vector<unsigned char> buf;
2310 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002311 bool fileRead =
2312 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002313 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002314 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002315 (*failMsgOut) +=
2316 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002317 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002318 return false;
2319 }
2320
Paolo Jovone6601bf2018-07-07 20:43:33 +02002321 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002322 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002323 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002324 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002325 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002326 return false;
2327 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002328
2329 if (checkSize) {
2330 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002331 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002332 return true;
2333 } else {
2334 std::stringstream ss;
2335 ss << "File size mismatch : " << filepath << ", requestedBytes "
2336 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002337 if (failMsgOut) {
2338 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002339 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002340 return false;
2341 }
2342 }
2343
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002344 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002345 return true;
2346}
2347
Squareysff644d82018-03-13 22:36:18 +01002348void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002349 LoadImageData = func;
2350 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002351 user_image_loader_ = true;
2352}
2353
2354void TinyGLTF::RemoveImageLoader() {
2355 LoadImageData =
2356#ifndef TINYGLTF_NO_STB_IMAGE
2357 &tinygltf::LoadImageData;
2358#else
2359 nullptr;
2360#endif
2361
2362 load_image_user_data_ = nullptr;
2363 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002364}
2365
Squareys2d3594d2018-03-13 22:40:53 +01002366#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002367bool LoadImageData(Image *image, const int image_idx, std::string *err,
2368 std::string *warn, int req_width, int req_height,
2369 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002370 (void)warn;
2371
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002372 LoadImageDataOption option;
2373 if (user_data) {
2374 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2375 }
2376
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002377 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002378
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002379 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002380
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002381 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002382 // false: force 32-bit textures for common Vulkan compatibility. It appears
2383 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002384 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002385 int bits = 8;
2386 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002387
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002388 // It is possible that the image we want to load is a 16bit per channel image
2389 // We are going to attempt to load it as 16bit per channel, and if it worked,
imallettd9ce9eb2022-10-07 10:37:09 -07002390 // set the image data accordingly. We are casting the returned pointer into
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002391 // unsigned char, because we are representing "bytes". But we are updating
2392 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2393 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002394 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002395 data = reinterpret_cast<unsigned char *>(
2396 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002397 if (data) {
2398 bits = 16;
2399 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2400 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002401 }
2402
2403 // at this point, if data is still NULL, it means that the image wasn't
2404 // 16bit per channel, we are going to load it as a normal 8bit per channel
imallettd9ce9eb2022-10-07 10:37:09 -07002405 // image as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002406 // if image cannot be decoded, ignore parsing and keep it by its path
2407 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002408 // FIXME we should only enter this function if the image is embedded. If
2409 // image->uri references
2410 // an image file, it should be left as it is. Image loading should not be
2411 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002412 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002413 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002414 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002415 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002416 (*err) +=
2417 "Unknown image format. STB cannot decode image data for image[" +
2418 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002419 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002420 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002421 }
2422
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002423 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002424 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002425 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002426 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2427 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002428 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002429 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002430 }
2431
2432 if (req_width > 0) {
2433 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002434 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002435 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002436 (*err) += "Image width mismatch for image[" +
2437 std::to_string(image_idx) + "] name = \"" + image->name +
2438 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002439 }
2440 return false;
2441 }
2442 }
2443
2444 if (req_height > 0) {
2445 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002446 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002447 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002448 (*err) += "Image height mismatch. for image[" +
2449 std::to_string(image_idx) + "] name = \"" + image->name +
2450 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002451 }
2452 return false;
2453 }
2454 }
2455
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002456 if (req_comp != 0) {
2457 // loaded data has `req_comp` channels(components)
2458 comp = req_comp;
2459 }
2460
Syoyo Fujitabeded612016-05-01 20:03:43 +09002461 image->width = w;
2462 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002463 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002464 image->bits = bits;
2465 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002466 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2467 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002468 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002469
Syoyo Fujitabeded612016-05-01 20:03:43 +09002470 return true;
2471}
Squareys2d3594d2018-03-13 22:40:53 +01002472#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002473
johan bowald642a3432018-04-01 12:37:18 +02002474void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2475 WriteImageData = func;
2476 write_image_user_data_ = user_data;
2477}
2478
2479#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2480static void WriteToMemory_stbi(void *context, void *data, int size) {
2481 std::vector<unsigned char> *buffer =
2482 reinterpret_cast<std::vector<unsigned char> *>(context);
2483
2484 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2485
2486 buffer->insert(buffer->end(), pData, pData + size);
2487}
2488
2489bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002490 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002491 const std::string ext = GetFilePathExtension(*filename);
2492
Paolo Jovone6601bf2018-07-07 20:43:33 +02002493 // Write image to temporary buffer
2494 std::string header;
2495 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002496
Paolo Jovone6601bf2018-07-07 20:43:33 +02002497 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002498 if ((image->bits != 8) ||
2499 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002500 // Unsupported pixel format
2501 return false;
2502 }
2503
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002504 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002505 image->height, image->component,
2506 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002507 return false;
2508 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002509 header = "data:image/png;base64,";
2510 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002511 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002512 image->height, image->component,
2513 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002514 return false;
2515 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002516 header = "data:image/jpeg;base64,";
2517 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002518 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002519 image->height, image->component,
2520 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002521 return false;
2522 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002523 header = "data:image/bmp;base64,";
2524 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002525 // Error: can't output requested format to file
2526 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002527 }
johan bowald642a3432018-04-01 12:37:18 +02002528
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002529 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002530 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002531 if (data.size()) {
2532 image->uri =
2533 header +
2534 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
2535 } else {
2536 // Throw error?
2537 }
2538 } else {
2539 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002540 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002541 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002542 const std::string imagefilepath = JoinPath(*basepath, *filename);
2543 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002544 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2545 fs->user_data)) {
2546 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002547 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002548 }
johan bowald642a3432018-04-01 12:37:18 +02002549 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002550 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002551 }
2552 image->uri = *filename;
2553 }
2554
2555 return true;
2556}
2557#endif
2558
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002559void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002560
Harokyangfb256602019-10-30 16:13:52 +08002561#ifdef _WIN32
2562static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002563 int wstr_size =
2564 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
imallett56e10982022-10-07 10:35:16 -07002565 std::wstring wstr((size_t)wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002566 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2567 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002568 return wstr;
2569}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002570
2571static inline std::string WcharToUTF8(const std::wstring &wstr) {
2572 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
imallett56e10982022-10-07 10:35:16 -07002573 nullptr, 0, nullptr, nullptr);
2574 std::string str((size_t)str_size, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002575 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
imallett56e10982022-10-07 10:35:16 -07002576 (int)str.size(), nullptr, nullptr);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002577 return str;
2578}
Harokyangfb256602019-10-30 16:13:52 +08002579#endif
2580
Paolo Jovone6601bf2018-07-07 20:43:33 +02002581#ifndef TINYGLTF_NO_FS
2582// Default implementations of filesystem functions
2583
2584bool FileExists(const std::string &abs_filename, void *) {
2585 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002586#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002587 if (asset_manager) {
2588 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2589 AASSET_MODE_STREAMING);
2590 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002591 return false;
2592 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002593 AAsset_close(asset);
2594 ret = true;
2595 } else {
2596 return false;
2597 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002598#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002599#ifdef _WIN32
operatios1668d1e2022-09-24 22:37:14 +03002600#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
Syoyo Fujita45cac782019-11-09 20:42:55 +09002601 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002602 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002603 if (err != 0) {
2604 return false;
2605 }
2606#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002607 FILE *fp = nullptr;
2608 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2609 if (err != 0) {
2610 return false;
2611 }
2612#endif
2613
2614#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002615 FILE *fp = fopen(abs_filename.c_str(), "rb");
2616#endif
2617 if (fp) {
2618 ret = true;
2619 fclose(fp);
2620 } else {
2621 ret = false;
2622 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002623#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002624
2625 return ret;
2626}
2627
2628std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002629 // https://github.com/syoyo/tinygltf/issues/368
2630 //
2631 // No file path expansion in built-in FS function anymore, since glTF URI
2632 // should not contain tilde('~') and environment variables, and for security
2633 // reason(`wordexp`).
2634 //
2635 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2636 // `LoadBinaryFromMemory`) in expanded absolute path.
2637
2638 return filepath;
2639
2640#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002641#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002642 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002643 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002644 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002645 wchar_t *wstr = new wchar_t[wlen];
2646 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002647
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002648 std::wstring ws(wstr);
2649 delete[] wstr;
2650 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002651
Paolo Jovone6601bf2018-07-07 20:43:33 +02002652#else
2653
2654#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002655 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002656 // no expansion
2657 std::string s = filepath;
2658#else
2659 std::string s;
2660 wordexp_t p;
2661
2662 if (filepath.empty()) {
2663 return "";
2664 }
2665
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002666 // Quote the string to keep any spaces in filepath intact.
2667 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002668 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002669 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002670 if (ret) {
2671 // err
2672 s = filepath;
2673 return s;
2674 }
2675
2676 // Use first element only.
2677 if (p.we_wordv) {
2678 s = std::string(p.we_wordv[0]);
2679 wordfree(&p);
2680 } else {
2681 s = filepath;
2682 }
2683
2684#endif
2685
2686 return s;
2687#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002688#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002689}
2690
2691bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2692 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002693#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2694 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002695 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2696 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002697 if (!asset) {
2698 if (err) {
2699 (*err) += "File open error : " + filepath + "\n";
2700 }
2701 return false;
2702 }
2703 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002704 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002705 if (err) {
2706 (*err) += "Invalid file size : " + filepath +
2707 " (does the path point to a directory?)";
2708 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002709 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002710 }
2711 out->resize(size);
2712 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2713 AAsset_close(asset);
2714 return true;
2715 } else {
2716 if (err) {
2717 (*err) += "No asset manager specified : " + filepath + "\n";
2718 }
2719 return false;
2720 }
2721#else
Harokyang5cecef22019-10-30 15:16:46 +08002722#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002723#if defined(__GLIBCXX__) // mingw
2724 int file_descriptor =
2725 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002726 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2727 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002728#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002729 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2730 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002731 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002732#else
2733 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002734 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2735#endif
Harokyang5cecef22019-10-30 15:16:46 +08002736#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002737 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002738#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002739 if (!f) {
2740 if (err) {
2741 (*err) += "File open error : " + filepath + "\n";
2742 }
2743 return false;
2744 }
2745
2746 f.seekg(0, f.end);
2747 size_t sz = static_cast<size_t>(f.tellg());
2748 f.seekg(0, f.beg);
2749
Syoyo Fujitae8862472019-10-20 17:47:50 +09002750 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002751 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002752 (*err) += "Invalid file size : " + filepath +
2753 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002754 }
2755 return false;
2756 } else if (sz == 0) {
2757 if (err) {
2758 (*err) += "File is empty : " + filepath + "\n";
2759 }
2760 return false;
2761 }
2762
2763 out->resize(sz);
2764 f.read(reinterpret_cast<char *>(&out->at(0)),
2765 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002766
2767 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002768#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002769}
2770
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002771bool WriteWholeFile(std::string *err, const std::string &filepath,
2772 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002773#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002774#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002775 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2776 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2777 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2778 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002779 std::ostream f(&wfile_buf);
2780#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002781 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002782#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002783 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2784#endif
Harokyangfb256602019-10-30 16:13:52 +08002785#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002786 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002787#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002788 if (!f) {
2789 if (err) {
2790 (*err) += "File open error for writing : " + filepath + "\n";
2791 }
2792 return false;
2793 }
2794
2795 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2796 static_cast<std::streamsize>(contents.size()));
2797 if (!f) {
2798 if (err) {
2799 (*err) += "File write error: " + filepath + "\n";
2800 }
2801 return false;
2802 }
2803
Paolo Jovone6601bf2018-07-07 20:43:33 +02002804 return true;
2805}
2806
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002807#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002808
johan bowald642a3432018-04-01 12:37:18 +02002809static std::string MimeToExt(const std::string &mimeType) {
2810 if (mimeType == "image/jpeg") {
2811 return "jpg";
2812 } else if (mimeType == "image/png") {
2813 return "png";
2814 } else if (mimeType == "image/bmp") {
2815 return "bmp";
2816 } else if (mimeType == "image/gif") {
2817 return "gif";
2818 }
2819
2820 return "";
2821}
2822
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002823static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002824 bool embedImages,
2825 WriteImageDataFunction *WriteImageData = nullptr,
2826 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002827 std::string filename;
2828 std::string ext;
imallettd9ce9eb2022-10-07 10:37:09 -07002829 // If image has uri, use it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002830 if (image.uri.size()) {
2831 filename = GetBaseFilename(image.uri);
2832 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002833 } else if (image.bufferView != -1) {
2834 // If there's no URI and the data exists in a buffer,
2835 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002836 } else if (image.name.size()) {
2837 ext = MimeToExt(image.mimeType);
2838 // Otherwise use name as filename
2839 filename = image.name + "." + ext;
2840 } else {
2841 ext = MimeToExt(image.mimeType);
2842 // Fallback to index of image as filename
2843 filename = std::to_string(index) + "." + ext;
2844 }
2845
2846 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002847 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002848 std::string uri;
2849 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2850 }
2851}
2852
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002853bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002854 std::string header = "data:application/octet-stream;base64,";
2855 if (in.find(header) == 0) {
2856 return true;
2857 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002858
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002859 header = "data:image/jpeg;base64,";
2860 if (in.find(header) == 0) {
2861 return true;
2862 }
Squareys43374632018-03-13 22:20:48 +01002863
Syoyo Fujita620eed12016-01-02 23:37:12 +09002864 header = "data:image/png;base64,";
2865 if (in.find(header) == 0) {
2866 return true;
2867 }
Squareys43374632018-03-13 22:20:48 +01002868
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002869 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002870 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002871 return true;
2872 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002873
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002874 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002875 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002876 return true;
2877 }
2878
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002879 header = "data:text/plain;base64,";
2880 if (in.find(header) == 0) {
2881 return true;
2882 }
2883
Syoyo Fujita20244e12018-03-15 11:01:05 -05002884 header = "data:application/gltf-buffer;base64,";
2885 if (in.find(header) == 0) {
2886 return true;
2887 }
2888
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002889 return false;
2890}
2891
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002892bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2893 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002894 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002895 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002896 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002897 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002898 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002899
2900 if (data.empty()) {
2901 header = "data:image/jpeg;base64,";
2902 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002903 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002904 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002905 }
2906 }
2907
2908 if (data.empty()) {
2909 header = "data:image/png;base64,";
2910 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002911 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002912 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002913 }
2914 }
Squareys43374632018-03-13 22:20:48 +01002915
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002916 if (data.empty()) {
2917 header = "data:image/bmp;base64,";
2918 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002919 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002920 data = base64_decode(in.substr(header.size())); // cut mime string.
2921 }
2922 }
2923
2924 if (data.empty()) {
2925 header = "data:image/gif;base64,";
2926 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002927 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002928 data = base64_decode(in.substr(header.size())); // cut mime string.
2929 }
2930 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002931
2932 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002933 header = "data:text/plain;base64,";
2934 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002935 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002936 data = base64_decode(in.substr(header.size()));
2937 }
2938 }
2939
2940 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002941 header = "data:application/gltf-buffer;base64,";
2942 if (in.find(header) == 0) {
2943 data = base64_decode(in.substr(header.size()));
2944 }
2945 }
2946
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002947 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002948 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002949 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002950 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002951
2952 if (checkSize) {
2953 if (data.size() != reqBytes) {
2954 return false;
2955 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002956 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002957 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002958 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002959 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002960 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002961 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002962}
2963
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002964namespace {
2965bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002966#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002967 if (!o.IsDouble()) {
2968 if (o.IsInt()) {
2969 val = o.GetInt();
2970 return true;
2971 } else if (o.IsUint()) {
2972 val = static_cast<int>(o.GetUint());
2973 return true;
2974 } else if (o.IsInt64()) {
2975 val = static_cast<int>(o.GetInt64());
2976 return true;
2977 } else if (o.IsUint64()) {
2978 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002979 return true;
2980 }
jrkoonce5cecc412019-08-29 11:45:04 -05002981 }
2982
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002983 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002984#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002985 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002986
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002987 if ((type == json::value_t::number_integer) ||
2988 (type == json::value_t::number_unsigned)) {
2989 val = static_cast<int>(o.get<int64_t>());
2990 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002991 }
2992
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002993 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002994#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05002995}
2996
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002997#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002998bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002999 if (o.IsDouble()) {
3000 val = o.GetDouble();
3001 return true;
3002 }
3003
3004 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003005}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003006#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003007
3008bool GetNumber(const json &o, double &val) {
3009#ifdef TINYGLTF_USE_RAPIDJSON
3010 if (o.IsNumber()) {
3011 val = o.GetDouble();
3012 return true;
3013 }
3014
3015 return false;
3016#else
3017 if (o.is_number()) {
3018 val = o.get<double>();
3019 return true;
3020 }
3021
3022 return false;
3023#endif
3024}
3025
3026bool GetString(const json &o, std::string &val) {
3027#ifdef TINYGLTF_USE_RAPIDJSON
3028 if (o.IsString()) {
3029 val = o.GetString();
3030 return true;
3031 }
3032
3033 return false;
3034#else
3035 if (o.type() == json::value_t::string) {
3036 val = o.get<std::string>();
3037 return true;
3038 }
3039
3040 return false;
3041#endif
3042}
3043
3044bool IsArray(const json &o) {
3045#ifdef TINYGLTF_USE_RAPIDJSON
3046 return o.IsArray();
3047#else
3048 return o.is_array();
3049#endif
3050}
3051
3052json_const_array_iterator ArrayBegin(const json &o) {
3053#ifdef TINYGLTF_USE_RAPIDJSON
3054 return o.Begin();
3055#else
3056 return o.begin();
3057#endif
3058}
3059
3060json_const_array_iterator ArrayEnd(const json &o) {
3061#ifdef TINYGLTF_USE_RAPIDJSON
3062 return o.End();
3063#else
3064 return o.end();
3065#endif
3066}
3067
3068bool IsObject(const json &o) {
3069#ifdef TINYGLTF_USE_RAPIDJSON
3070 return o.IsObject();
3071#else
3072 return o.is_object();
3073#endif
3074}
3075
3076json_const_iterator ObjectBegin(const json &o) {
3077#ifdef TINYGLTF_USE_RAPIDJSON
3078 return o.MemberBegin();
3079#else
3080 return o.begin();
3081#endif
3082}
3083
3084json_const_iterator ObjectEnd(const json &o) {
3085#ifdef TINYGLTF_USE_RAPIDJSON
3086 return o.MemberEnd();
3087#else
3088 return o.end();
3089#endif
3090}
3091
Rahul Sheth01d54382020-07-10 14:27:37 -04003092// Making this a const char* results in a pointer to a temporary when
3093// TINYGLTF_USE_RAPIDJSON is off.
3094std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003095#ifdef TINYGLTF_USE_RAPIDJSON
3096 return it->name.GetString();
3097#else
3098 return it.key().c_str();
3099#endif
3100}
3101
3102bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3103#ifdef TINYGLTF_USE_RAPIDJSON
3104 if (!o.IsObject()) {
3105 return false;
3106 }
3107 it = o.FindMember(member);
3108 return it != o.MemberEnd();
3109#else
3110 it = o.find(member);
3111 return it != o.end();
3112#endif
3113}
3114
3115const json &GetValue(json_const_iterator &it) {
3116#ifdef TINYGLTF_USE_RAPIDJSON
3117 return it->value;
3118#else
3119 return it.value();
3120#endif
3121}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003122
3123std::string JsonToString(const json &o, int spacing = -1) {
3124#ifdef TINYGLTF_USE_RAPIDJSON
3125 using namespace rapidjson;
3126 StringBuffer buffer;
3127 if (spacing == -1) {
3128 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003129 // TODO: Better error handling.
3130 // https://github.com/syoyo/tinygltf/issues/332
3131 if (!o.Accept(writer)) {
3132 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3133 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003134 } else {
3135 PrettyWriter<StringBuffer> writer(buffer);
3136 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003137 if (!o.Accept(writer)) {
3138 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3139 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003140 }
3141 return buffer.GetString();
3142#else
3143 return o.dump(spacing);
3144#endif
3145}
3146
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003147} // namespace
3148
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003149static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003150 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003151#ifdef TINYGLTF_USE_RAPIDJSON
3152 using rapidjson::Type;
3153 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003154 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003155 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003156 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003157 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003158 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003159 if (entry.Type() != NULL_TYPE)
3160 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003161 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003162 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003163 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003164 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003165 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003166 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003167 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003168 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003169 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003170 if (entry.Type() != NULL_TYPE)
3171 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003172 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003173 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003174 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003175 case Type::kStringType:
3176 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003177 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003178 case Type::kFalseType:
3179 case Type::kTrueType:
3180 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003181 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003182 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003183 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003184 int i = 0;
3185 GetInt(o, i);
3186 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003187 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003188 double d = 0.0;
3189 GetDouble(o, d);
3190 val = Value(d);
3191 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003192 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003193 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003194 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003195 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003196 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003197#else
3198 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003199 case json::value_t::object: {
3200 Value::Object value_object;
3201 for (auto it = o.begin(); it != o.end(); it++) {
3202 Value entry;
3203 ParseJsonAsValue(&entry, it.value());
3204 if (entry.Type() != NULL_TYPE)
3205 value_object.emplace(it.key(), std::move(entry));
3206 }
3207 if (value_object.size() > 0) val = Value(std::move(value_object));
3208 } break;
3209 case json::value_t::array: {
3210 Value::Array value_array;
3211 value_array.reserve(o.size());
3212 for (auto it = o.begin(); it != o.end(); it++) {
3213 Value entry;
3214 ParseJsonAsValue(&entry, it.value());
3215 if (entry.Type() != NULL_TYPE)
3216 value_array.emplace_back(std::move(entry));
3217 }
3218 if (value_array.size() > 0) val = Value(std::move(value_array));
3219 } break;
3220 case json::value_t::string:
3221 val = Value(o.get<std::string>());
3222 break;
3223 case json::value_t::boolean:
3224 val = Value(o.get<bool>());
3225 break;
3226 case json::value_t::number_integer:
3227 case json::value_t::number_unsigned:
3228 val = Value(static_cast<int>(o.get<int64_t>()));
3229 break;
3230 case json::value_t::number_float:
3231 val = Value(o.get<double>());
3232 break;
3233 case json::value_t::null:
3234 case json::value_t::discarded:
Christopher Sean Morrison0bfcb4f2022-02-23 10:02:49 -05003235 case json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003236 // default:
3237 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003238 }
3239#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003240 const bool isNotNull = val.Type() != NULL_TYPE;
3241
jrkooncecba5d6c2019-08-29 11:26:22 -05003242 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003243
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003244 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003245}
3246
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003247static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003248 json_const_iterator it;
3249 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003250 return false;
3251 }
3252
jrkooncecba5d6c2019-08-29 11:26:22 -05003253 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003254}
3255
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003256static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003257 const std::string &property,
3258 const bool required,
3259 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003260 json_const_iterator it;
3261 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003262 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003263 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003264 (*err) += "'" + property + "' property is missing";
3265 if (!parent_node.empty()) {
3266 (*err) += " in " + parent_node;
3267 }
3268 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003269 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003270 }
3271 return false;
3272 }
3273
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003274 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003275
3276 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003277 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003278#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003279 isBoolean = value.IsBool();
3280 if (isBoolean) {
3281 boolValue = value.GetBool();
3282 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003283#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003284 isBoolean = value.is_boolean();
3285 if (isBoolean) {
3286 boolValue = value.get<bool>();
3287 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003288#endif
3289 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003290 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003291 if (err) {
3292 (*err) += "'" + property + "' property is not a bool type.\n";
3293 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003294 }
3295 return false;
3296 }
3297
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003298 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003299 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003300 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003301
3302 return true;
3303}
3304
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003305static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3306 const std::string &property,
3307 const bool required,
3308 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003309 json_const_iterator it;
3310 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003311 if (required) {
3312 if (err) {
3313 (*err) += "'" + property + "' property is missing";
3314 if (!parent_node.empty()) {
3315 (*err) += " in " + parent_node;
3316 }
3317 (*err) += ".\n";
3318 }
3319 }
3320 return false;
3321 }
3322
jrkooncecba5d6c2019-08-29 11:26:22 -05003323 int intValue;
3324 bool isInt = GetInt(GetValue(it), intValue);
3325 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003326 if (required) {
3327 if (err) {
3328 (*err) += "'" + property + "' property is not an integer type.\n";
3329 }
3330 }
3331 return false;
3332 }
3333
3334 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003335 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003336 }
3337
3338 return true;
3339}
3340
3341static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3342 const std::string &property,
3343 const bool required,
3344 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003345 json_const_iterator it;
3346 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003347 if (required) {
3348 if (err) {
3349 (*err) += "'" + property + "' property is missing";
3350 if (!parent_node.empty()) {
3351 (*err) += " in " + parent_node;
3352 }
3353 (*err) += ".\n";
3354 }
3355 }
3356 return false;
3357 }
3358
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003359 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003360
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003361 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003362 bool isUValue;
3363#ifdef TINYGLTF_USE_RAPIDJSON
3364 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003365 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003366 uValue = value.GetUint();
3367 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003368 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003369 uValue = value.GetUint64();
3370 isUValue = true;
3371 }
3372#else
3373 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003374 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003375 uValue = value.get<size_t>();
3376 }
3377#endif
3378 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003379 if (required) {
3380 if (err) {
3381 (*err) += "'" + property + "' property is not a positive integer.\n";
3382 }
3383 }
3384 return false;
3385 }
3386
3387 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003388 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003389 }
3390
3391 return true;
3392}
3393
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003394static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003395 const std::string &property,
3396 const bool required,
3397 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003398 json_const_iterator it;
3399
3400 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003401 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003402 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003403 (*err) += "'" + property + "' property is missing";
3404 if (!parent_node.empty()) {
3405 (*err) += " in " + parent_node;
3406 }
3407 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003408 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003409 }
3410 return false;
3411 }
3412
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003413 double numberValue;
3414 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003415
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003416 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003417 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003418 if (err) {
3419 (*err) += "'" + property + "' property is not a number type.\n";
3420 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003421 }
3422 return false;
3423 }
3424
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003425 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003426 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003427 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003428
3429 return true;
3430}
3431
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003432static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003433 const json &o, const std::string &property,
3434 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003435 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003436 json_const_iterator it;
3437 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003438 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003439 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003440 (*err) += "'" + property + "' property is missing";
3441 if (!parent_node.empty()) {
3442 (*err) += " in " + parent_node;
3443 }
3444 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003445 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003446 }
3447 return false;
3448 }
3449
jrkooncecba5d6c2019-08-29 11:26:22 -05003450 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003451 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003452 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003453 (*err) += "'" + property + "' property is not an array";
3454 if (!parent_node.empty()) {
3455 (*err) += " in " + parent_node;
3456 }
3457 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003458 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003459 }
3460 return false;
3461 }
3462
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003463 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003464 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003465 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003466 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003467 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003468 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003469 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003470 if (err) {
3471 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003472 if (!parent_node.empty()) {
3473 (*err) += " in " + parent_node;
3474 }
3475 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003476 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003477 }
3478 return false;
3479 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003480 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003481 }
3482
3483 return true;
3484}
3485
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003486static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3487 const json &o,
3488 const std::string &property,
3489 bool required,
3490 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003491 json_const_iterator it;
3492 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003493 if (required) {
3494 if (err) {
3495 (*err) += "'" + property + "' property is missing";
3496 if (!parent_node.empty()) {
3497 (*err) += " in " + parent_node;
3498 }
3499 (*err) += ".\n";
3500 }
3501 }
3502 return false;
3503 }
3504
jrkooncecba5d6c2019-08-29 11:26:22 -05003505 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003506 if (required) {
3507 if (err) {
3508 (*err) += "'" + property + "' property is not an array";
3509 if (!parent_node.empty()) {
3510 (*err) += " in " + parent_node;
3511 }
3512 (*err) += ".\n";
3513 }
3514 }
3515 return false;
3516 }
3517
3518 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003519 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003520 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003521 int numberValue;
3522 bool isNumber = GetInt(*i, numberValue);
3523 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003524 if (required) {
3525 if (err) {
3526 (*err) += "'" + property + "' property is not an integer type.\n";
3527 if (!parent_node.empty()) {
3528 (*err) += " in " + parent_node;
3529 }
3530 (*err) += ".\n";
3531 }
3532 }
3533 return false;
3534 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003535 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003536 }
3537
3538 return true;
3539}
3540
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003541static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003542 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003543 const std::string &property, bool required,
3544 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003545 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003546 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003547 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003548 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003549 (*err) += "'" + property + "' property is missing";
3550 if (parent_node.empty()) {
3551 (*err) += ".\n";
3552 } else {
3553 (*err) += " in `" + parent_node + "'.\n";
3554 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003555 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003556 }
3557 return false;
3558 }
3559
jrkooncecba5d6c2019-08-29 11:26:22 -05003560 std::string strValue;
3561 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003562 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003563 if (err) {
3564 (*err) += "'" + property + "' property is not a string type.\n";
3565 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003566 }
3567 return false;
3568 }
3569
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003570 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003571 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003572 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003573
3574 return true;
3575}
3576
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003577static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3578 std::string *err, const json &o,
3579 const std::string &property,
3580 bool required,
3581 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003582 json_const_iterator it;
3583 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003584 if (required) {
3585 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003586 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003587 (*err) +=
3588 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003589 } else {
3590 (*err) += "'" + property + "' property is missing.\n";
3591 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003592 }
3593 }
3594 return false;
3595 }
3596
jrkooncecba5d6c2019-08-29 11:26:22 -05003597 const json &dict = GetValue(it);
3598
Luke San Antonio19894c72016-06-14 21:19:51 -04003599 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003600 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003601 if (required) {
3602 if (err) {
3603 (*err) += "'" + property + "' property is not an object.\n";
3604 }
3605 }
3606 return false;
3607 }
3608
3609 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003610
jrkooncecba5d6c2019-08-29 11:26:22 -05003611 json_const_iterator dictIt(ObjectBegin(dict));
3612 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003613
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003614 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003615 int intVal;
3616 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003617 if (required) {
3618 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003619 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003620 }
3621 }
3622 return false;
3623 }
3624
3625 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003626 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003627 }
3628 return true;
3629}
3630
Syoyo Fujita5b407452017-06-04 17:42:41 +09003631static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003632 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003633 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003634 json_const_iterator it;
3635 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003636 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003637 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003638 (*err) += "'" + property + "' property is missing. \n'";
3639 }
3640 }
3641 return false;
3642 }
3643
jrkooncecba5d6c2019-08-29 11:26:22 -05003644 const json &obj = GetValue(it);
3645
3646 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003647 if (required) {
3648 if (err) {
3649 (*err) += "'" + property + "' property is not a JSON object.\n";
3650 }
3651 }
3652 return false;
3653 }
3654
3655 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003656
jrkooncecba5d6c2019-08-29 11:26:22 -05003657 json_const_iterator it2(ObjectBegin(obj));
3658 json_const_iterator itEnd(ObjectEnd(obj));
3659 for (; it2 != itEnd; ++it2) {
3660 double numVal;
3661 if (GetNumber(GetValue(it2), numVal))
3662 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003663 }
3664
3665 return true;
3666}
3667
Selmar09d2ff12018-03-15 17:30:42 +01003668static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003669 const json &o, const std::string &prop,
3670 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003671 // A parameter value can either be a string or an array of either a boolean or
3672 // a number. Booleans of any kind aren't supported here. Granted, it
3673 // complicates the Parameter structure and breaks it semantically in the sense
3674 // that the client probably works off the assumption that if the string is
3675 // empty the vector is used, etc. Would a tagged union work?
3676 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3677 // Found string property.
3678 return true;
3679 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3680 false)) {
3681 // Found a number array.
3682 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003683 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02003684 param->has_number_value = true;
3685 return true;
Selmar09d2ff12018-03-15 17:30:42 +01003686 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3687 false)) {
3688 return true;
3689 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3690 return true;
3691 } else {
3692 if (required) {
3693 if (err) {
3694 (*err) += "parameter must be a string or number / number array.\n";
3695 }
3696 }
3697 return false;
3698 }
3699}
3700
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003701static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3702 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003703 (void)err;
3704
jrkooncecba5d6c2019-08-29 11:26:22 -05003705 json_const_iterator it;
3706 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003707 return false;
3708 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003709
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003710 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003711 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003712 return false;
3713 }
3714 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003715 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003716 json_const_iterator extEnd = ObjectEnd(obj);
3717 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003718 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003719 if (!IsObject(itObj)) continue;
3720 std::string key(GetKey(extIt));
3721 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003722 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003723 // create empty object so that an extension object is still of type
3724 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003725 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003726 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003727 }
Selmar09d2ff12018-03-15 17:30:42 +01003728 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003729 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003730 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003731 }
3732 return true;
3733}
3734
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003735static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3736 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003737 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3738 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3739 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003740 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003741
Selmar09d2ff12018-03-15 17:30:42 +01003742 ParseExtensionsProperty(&asset->extensions, err, o);
3743
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003744 // Unity exporter version is added as extra here
3745 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003746
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003747 if (store_original_json_for_extras_and_extensions) {
3748 {
3749 json_const_iterator it;
3750 if (FindMember(o, "extensions", it)) {
3751 asset->extensions_json_string = JsonToString(GetValue(it));
3752 }
3753 }
3754 {
3755 json_const_iterator it;
3756 if (FindMember(o, "extras", it)) {
3757 asset->extras_json_string = JsonToString(GetValue(it));
3758 }
3759 }
3760 }
3761
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003762 return true;
3763}
3764
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003765static bool ParseImage(Image *image, const int image_idx, std::string *err,
3766 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003767 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003768 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003769 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003770 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003771 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003772
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003773 // schema says oneOf [`bufferView`, `uri`]
3774 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003775 json_const_iterator it;
3776 bool hasBufferView = FindMember(o, "bufferView", it);
3777 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003778
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003779 ParseStringProperty(&image->name, err, o, "name", false);
3780
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003781 if (hasBufferView && hasURI) {
3782 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003783 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003784 (*err) +=
3785 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003786 "defined for image[" +
3787 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003788 }
3789 return false;
3790 }
3791
3792 if (!hasBufferView && !hasURI) {
3793 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003794 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3795 std::to_string(image_idx) + "] name = \"" + image->name +
3796 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003797 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003798 return false;
3799 }
3800
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003801 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003802 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003803
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003804 if (store_original_json_for_extras_and_extensions) {
3805 {
3806 json_const_iterator eit;
3807 if (FindMember(o, "extensions", eit)) {
3808 image->extensions_json_string = JsonToString(GetValue(eit));
3809 }
3810 }
3811 {
3812 json_const_iterator eit;
3813 if (FindMember(o, "extras", eit)) {
3814 image->extras_json_string = JsonToString(GetValue(eit));
3815 }
3816 }
3817 }
3818
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003819 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003820 int bufferView = -1;
3821 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003822 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003823 (*err) += "Failed to parse `bufferView` for image[" +
3824 std::to_string(image_idx) + "] name = \"" + image->name +
3825 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003826 }
3827 return false;
3828 }
3829
3830 std::string mime_type;
3831 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3832
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003833 int width = 0;
3834 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003835
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003836 int height = 0;
3837 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003838
3839 // Just only save some information here. Loading actual image data from
3840 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003841 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003842 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003843 image->width = width;
3844 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003845
3846 return true;
3847 }
3848
Syoyo Fujita246654a2018-03-21 20:32:22 +09003849 // Parse URI & Load image data.
3850
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003851 std::string uri;
3852 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003853 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3854 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003855 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3856 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003857 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003858 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003859 }
3860
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003861 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003862
Syoyo Fujita246654a2018-03-21 20:32:22 +09003863 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003864 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003865 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003866 (*err) += "Failed to decode 'uri' for image[" +
3867 std::to_string(image_idx) + "] name = [" + image->name +
3868 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003869 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003870 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003871 }
3872 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003873 // Assume external file
3874 // Keep texture path (for textures that cannot be decoded)
3875 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003876#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003877 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01003878#else
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003879 std::string decoded_uri = dlib::urldecode(uri);
3880 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3881 /* required */ false, /* required bytes */ 0,
3882 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003883 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003884 (*warn) += "Failed to load external 'uri' for image[" +
3885 std::to_string(image_idx) + "] name = [" + image->name +
3886 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003887 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003888 // If the image cannot be loaded, keep uri as image->uri.
3889 return true;
3890 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003891
Syoyo Fujita246654a2018-03-21 20:32:22 +09003892 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003893 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003894 (*warn) += "Image data is empty for image[" +
3895 std::to_string(image_idx) + "] name = [" + image->name +
3896 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003897 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003898 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003899 }
AlvaroBarua43172232022-09-11 00:41:43 +01003900#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003901 }
3902
Squareysff644d82018-03-13 22:36:18 +01003903 if (*LoadImageData == nullptr) {
3904 if (err) {
3905 (*err) += "No LoadImageData callback specified.\n";
3906 }
3907 return false;
3908 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003909 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003910 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003911}
3912
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003913static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003914 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003915 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003916 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003917 int sampler = -1;
3918 int source = -1;
3919 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003920
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003921 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003922
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003923 texture->sampler = sampler;
3924 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003925
Selmar Kokfa7022f2018-04-04 18:10:20 +02003926 ParseExtensionsProperty(&texture->extensions, err, o);
3927 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003928
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003929 if (store_original_json_for_extras_and_extensions) {
3930 {
3931 json_const_iterator it;
3932 if (FindMember(o, "extensions", it)) {
3933 texture->extensions_json_string = JsonToString(GetValue(it));
3934 }
3935 }
3936 {
3937 json_const_iterator it;
3938 if (FindMember(o, "extras", it)) {
3939 texture->extras_json_string = JsonToString(GetValue(it));
3940 }
3941 }
3942 }
3943
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003944 ParseStringProperty(&texture->name, err, o, "name", false);
3945
Syoyo Fujitabde70212016-02-07 17:38:17 +09003946 return true;
3947}
3948
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003949static bool ParseTextureInfo(
3950 TextureInfo *texinfo, std::string *err, const json &o,
3951 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003952 if (texinfo == nullptr) {
3953 return false;
3954 }
3955
3956 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3957 /* required */ true, "TextureInfo")) {
3958 return false;
3959 }
3960
3961 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3962
3963 ParseExtensionsProperty(&texinfo->extensions, err, o);
3964 ParseExtrasProperty(&texinfo->extras, o);
3965
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003966 if (store_original_json_for_extras_and_extensions) {
3967 {
3968 json_const_iterator it;
3969 if (FindMember(o, "extensions", it)) {
3970 texinfo->extensions_json_string = JsonToString(GetValue(it));
3971 }
3972 }
3973 {
3974 json_const_iterator it;
3975 if (FindMember(o, "extras", it)) {
3976 texinfo->extras_json_string = JsonToString(GetValue(it));
3977 }
3978 }
3979 }
3980
Syoyo Fujita046400b2019-07-24 19:26:48 +09003981 return true;
3982}
3983
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003984static bool ParseNormalTextureInfo(
3985 NormalTextureInfo *texinfo, std::string *err, const json &o,
3986 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003987 if (texinfo == nullptr) {
3988 return false;
3989 }
3990
3991 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3992 /* required */ true, "NormalTextureInfo")) {
3993 return false;
3994 }
3995
3996 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3997 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
3998
3999 ParseExtensionsProperty(&texinfo->extensions, err, o);
4000 ParseExtrasProperty(&texinfo->extras, o);
4001
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004002 if (store_original_json_for_extras_and_extensions) {
4003 {
4004 json_const_iterator it;
4005 if (FindMember(o, "extensions", it)) {
4006 texinfo->extensions_json_string = JsonToString(GetValue(it));
4007 }
4008 }
4009 {
4010 json_const_iterator it;
4011 if (FindMember(o, "extras", it)) {
4012 texinfo->extras_json_string = JsonToString(GetValue(it));
4013 }
4014 }
4015 }
4016
Syoyo Fujita046400b2019-07-24 19:26:48 +09004017 return true;
4018}
4019
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004020static bool ParseOcclusionTextureInfo(
4021 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
4022 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004023 if (texinfo == nullptr) {
4024 return false;
4025 }
4026
4027 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4028 /* required */ true, "NormalTextureInfo")) {
4029 return false;
4030 }
4031
4032 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4033 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4034
4035 ParseExtensionsProperty(&texinfo->extensions, err, o);
4036 ParseExtrasProperty(&texinfo->extras, o);
4037
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004038 if (store_original_json_for_extras_and_extensions) {
4039 {
4040 json_const_iterator it;
4041 if (FindMember(o, "extensions", it)) {
4042 texinfo->extensions_json_string = JsonToString(GetValue(it));
4043 }
4044 }
4045 {
4046 json_const_iterator it;
4047 if (FindMember(o, "extras", it)) {
4048 texinfo->extras_json_string = JsonToString(GetValue(it));
4049 }
4050 }
4051 }
4052
Syoyo Fujita046400b2019-07-24 19:26:48 +09004053 return true;
4054}
4055
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004056static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004057 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004058 FsCallbacks *fs, const std::string &basedir,
4059 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004060 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004061 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004062 size_t byteLength;
4063 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4064 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004065 return false;
4066 }
4067
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004068 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004069 buffer->uri.clear();
4070 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004071
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004072 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004073 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004074 if (err) {
4075 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4076 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004077 }
4078
jrkooncecba5d6c2019-08-29 11:26:22 -05004079 json_const_iterator type;
4080 if (FindMember(o, "type", type)) {
4081 std::string typeStr;
4082 if (GetString(GetValue(type), typeStr)) {
4083 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004084 // buffer.type = "arraybuffer";
4085 }
4086 }
4087 }
4088
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004089 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004090 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004091 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004092 // First try embedded data URI.
4093 if (IsDataURI(buffer->uri)) {
4094 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004095 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004096 true)) {
4097 if (err) {
4098 (*err) +=
4099 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4100 }
4101 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004102 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004103 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004104 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004105 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004106 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004107 decoded_uri, basedir, /* required */ true,
4108 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004109 return false;
4110 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004111 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004112 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004113 // load data from (embedded) binary data
4114
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004115 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004116 if (err) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09004117 (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004118 }
4119 return false;
4120 }
4121
4122 if (byteLength > bin_size) {
4123 if (err) {
4124 std::stringstream ss;
4125 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004126 "`byteLength' = "
4127 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004128 (*err) += ss.str();
4129 }
4130 return false;
4131 }
4132
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004133 // Read buffer data
4134 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004135 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004136 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004137
4138 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004139 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004140 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004141 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4142 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004143 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004144 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004145 }
4146 return false;
4147 }
4148 } else {
4149 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004150 std::string decoded_uri = dlib::urldecode(buffer->uri);
4151 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4152 basedir, /* required */ true, byteLength,
4153 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004154 return false;
4155 }
4156 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004157 }
4158
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004159 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004160
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004161 ParseExtensionsProperty(&buffer->extensions, err, o);
4162 ParseExtrasProperty(&buffer->extras, o);
4163
4164 if (store_original_json_for_extras_and_extensions) {
4165 {
4166 json_const_iterator it;
4167 if (FindMember(o, "extensions", it)) {
4168 buffer->extensions_json_string = JsonToString(GetValue(it));
4169 }
4170 }
4171 {
4172 json_const_iterator it;
4173 if (FindMember(o, "extras", it)) {
4174 buffer->extras_json_string = JsonToString(GetValue(it));
4175 }
4176 }
4177 }
4178
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004179 return true;
4180}
4181
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004182static bool ParseBufferView(
4183 BufferView *bufferView, std::string *err, const json &o,
4184 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004185 int buffer = -1;
4186 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004187 return false;
4188 }
4189
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004190 size_t byteOffset = 0;
4191 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004192
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004193 size_t byteLength = 1;
4194 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4195 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004196 return false;
4197 }
4198
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004199 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004200 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004201 // Spec says: When byteStride of referenced bufferView is not defined, it
4202 // means that accessor elements are tightly packed, i.e., effective stride
4203 // equals the size of the element.
4204 // We cannot determine the actual byteStride until Accessor are parsed, thus
4205 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4206 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004207 }
4208
4209 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4210 if (err) {
4211 std::stringstream ss;
4212 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4213 "4 : "
4214 << byteStride << std::endl;
4215
4216 (*err) += ss.str();
4217 }
4218 return false;
4219 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004220
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004221 int target = 0;
4222 ParseIntegerProperty(&target, err, o, "target", false);
4223 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4224 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004225 // OK
4226 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004227 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004228 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004229 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004230
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004231 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004232
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004233 ParseExtensionsProperty(&bufferView->extensions, err, o);
4234 ParseExtrasProperty(&bufferView->extras, o);
4235
4236 if (store_original_json_for_extras_and_extensions) {
4237 {
4238 json_const_iterator it;
4239 if (FindMember(o, "extensions", it)) {
4240 bufferView->extensions_json_string = JsonToString(GetValue(it));
4241 }
4242 }
4243 {
4244 json_const_iterator it;
4245 if (FindMember(o, "extras", it)) {
4246 bufferView->extras_json_string = JsonToString(GetValue(it));
4247 }
4248 }
4249 }
4250
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004251 bufferView->buffer = buffer;
4252 bufferView->byteOffset = byteOffset;
4253 bufferView->byteLength = byteLength;
4254 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004255 return true;
4256}
4257
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004258static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4259 const json &o) {
4260 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004261
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004262 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004263 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4264 return false;
4265 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004266
jrkooncecba5d6c2019-08-29 11:26:22 -05004267 json_const_iterator indices_iterator;
4268 json_const_iterator values_iterator;
4269 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004270 (*err) = "the sparse object of this accessor doesn't have indices";
4271 return false;
4272 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004273
jrkooncecba5d6c2019-08-29 11:26:22 -05004274 if (!FindMember(o, "values", values_iterator)) {
imallettd9ce9eb2022-10-07 10:37:09 -07004275 (*err) = "the sparse object of this accessor doesn't have values";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004276 return false;
4277 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004278
jrkooncecba5d6c2019-08-29 11:26:22 -05004279 const json &indices_obj = GetValue(indices_iterator);
4280 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004281
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004282 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004283 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4284 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004285 return false;
4286 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004287 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004288 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004289 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004290 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004291 return false;
4292 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004293
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004294 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004295 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004296 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004297 return false;
4298 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004299 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004300 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004301
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004302 accessor->sparse.count = count;
4303 accessor->sparse.indices.bufferView = indices_buffer_view;
4304 accessor->sparse.indices.byteOffset = indices_byte_offset;
4305 accessor->sparse.indices.componentType = component_type;
4306 accessor->sparse.values.bufferView = values_buffer_view;
4307 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004308
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004309 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004310}
4311
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004312static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4313 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004314 int bufferView = -1;
4315 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004316
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004317 size_t byteOffset = 0;
4318 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004319
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004320 bool normalized = false;
4321 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4322
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004323 size_t componentType = 0;
4324 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4325 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004326 return false;
4327 }
4328
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004329 size_t count = 0;
4330 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004331 return false;
4332 }
4333
4334 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004335 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004336 return false;
4337 }
4338
4339 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004340 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004341 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004342 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004343 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004344 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004345 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004346 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004347 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004348 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004349 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004350 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004351 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004352 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004353 } else {
4354 std::stringstream ss;
4355 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004356 if (err) {
4357 (*err) += ss.str();
4358 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004359 return false;
4360 }
4361
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004362 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004363
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004364 accessor->minValues.clear();
4365 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004366 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4367 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004368
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004369 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4370 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004371
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004372 accessor->count = count;
4373 accessor->bufferView = bufferView;
4374 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004375 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004376 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004377 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4378 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004379 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004380 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004381 } else {
4382 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004383 ss << "Invalid `componentType` in accessor. Got " << componentType
4384 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004385 if (err) {
4386 (*err) += ss.str();
4387 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004388 return false;
4389 }
4390 }
4391
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004392 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004393 ParseExtrasProperty(&(accessor->extras), o);
4394
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004395 if (store_original_json_for_extras_and_extensions) {
4396 {
4397 json_const_iterator it;
4398 if (FindMember(o, "extensions", it)) {
4399 accessor->extensions_json_string = JsonToString(GetValue(it));
4400 }
4401 }
4402 {
4403 json_const_iterator it;
4404 if (FindMember(o, "extras", it)) {
4405 accessor->extras_json_string = JsonToString(GetValue(it));
4406 }
4407 }
4408 }
4409
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004410 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004411 json_const_iterator iterator;
4412 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004413 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004414 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004415 }
4416
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004417 return true;
4418}
4419
Alex Wood7319db72019-01-24 15:38:16 -05004420#ifdef TINYGLTF_ENABLE_DRACO
4421
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004422static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4423 std::vector<uint8_t> &outBuffer) {
4424 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004425 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004426 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4427 outBuffer.size());
4428 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004429 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004430 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4431 const draco::Mesh::Face &face = mesh->face(f);
4432 if (componentSize == 2) {
4433 uint16_t indices[3] = {(uint16_t)face[0].value(),
4434 (uint16_t)face[1].value(),
4435 (uint16_t)face[2].value()};
4436 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4437 faceStride);
4438 } else {
4439 uint8_t indices[3] = {(uint8_t)face[0].value(),
4440 (uint8_t)face[1].value(),
4441 (uint8_t)face[2].value()};
4442 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4443 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004444 }
4445 }
4446 }
4447}
4448
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004449template <typename T>
4450static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4451 const draco::PointAttribute *pAttribute,
4452 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004453 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004454 T values[4] = {0, 0, 0, 0};
4455 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004456 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004457 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4458 values))
Alex Wood7319db72019-01-24 15:38:16 -05004459 return false;
4460
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004461 memcpy(outBuffer.data() + byteOffset, &values[0],
4462 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004463 byteOffset += sizeof(T) * pAttribute->num_components();
4464 }
4465
4466 return true;
4467}
4468
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004469static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4470 const draco::PointAttribute *pAttribute,
4471 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004472 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004473 switch (componentType) {
4474 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4475 decodeResult =
4476 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4477 break;
4478 case TINYGLTF_COMPONENT_TYPE_BYTE:
4479 decodeResult =
4480 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4481 break;
4482 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4483 decodeResult =
4484 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4485 break;
4486 case TINYGLTF_COMPONENT_TYPE_SHORT:
4487 decodeResult =
4488 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4489 break;
4490 case TINYGLTF_COMPONENT_TYPE_INT:
4491 decodeResult =
4492 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4493 break;
4494 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4495 decodeResult =
4496 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4497 break;
4498 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4499 decodeResult =
4500 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4501 break;
4502 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4503 decodeResult =
4504 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4505 break;
4506 default:
4507 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004508 }
4509
4510 return decodeResult;
4511}
4512
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004513static bool ParseDracoExtension(Primitive *primitive, Model *model,
4514 std::string *err,
4515 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004516 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004517 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004518 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004519 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004520 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004521
4522 auto attributesObject = attributesValue.Get<Value::Object>();
4523 int bufferView = bufferViewValue.Get<int>();
4524
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004525 BufferView &view = model->bufferViews[bufferView];
4526 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004527 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004528 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004529 view.dracoDecoded = true;
4530
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004531 const char *bufferViewData =
4532 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004533 size_t bufferViewSize = view.byteLength;
4534
4535 // decode draco
4536 draco::DecoderBuffer decoderBuffer;
4537 decoderBuffer.Init(bufferViewData, bufferViewSize);
4538 draco::Decoder decoder;
4539 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4540 if (!decodeResult.ok()) {
4541 return false;
4542 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004543 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004544
4545 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004546 if (primitive->indices >= 0) {
4547 int32_t componentSize = GetComponentSizeInBytes(
4548 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004549 Buffer decodedIndexBuffer;
4550 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4551
4552 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4553
4554 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4555
4556 BufferView decodedIndexBufferView;
4557 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004558 decodedIndexBufferView.byteLength =
4559 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004560 decodedIndexBufferView.byteOffset = 0;
4561 decodedIndexBufferView.byteStride = 0;
4562 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4563 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4564
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004565 model->accessors[primitive->indices].bufferView =
4566 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004567 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004568 }
4569
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004570 for (const auto &attribute : attributesObject) {
4571 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004572 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004573 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004574
4575 int dracoAttributeIndex = attribute.second.Get<int>();
4576 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004577 const auto componentType =
4578 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004579
4580 // Create a new buffer for this decoded buffer
4581 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004582 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4583 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004584 decodedBuffer.data.resize(bufferSize);
4585
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004586 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4587 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004588 return false;
4589
4590 model->buffers.emplace_back(std::move(decodedBuffer));
4591
4592 BufferView decodedBufferView;
4593 decodedBufferView.buffer = int(model->buffers.size() - 1);
4594 decodedBufferView.byteLength = bufferSize;
4595 decodedBufferView.byteOffset = pAttribute->byte_offset();
4596 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004597 decodedBufferView.target = primitive->indices >= 0
4598 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4599 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004600 model->bufferViews.emplace_back(std::move(decodedBufferView));
4601
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004602 model->accessors[primitiveAttribute->second].bufferView =
4603 int(model->bufferViews.size() - 1);
4604 model->accessors[primitiveAttribute->second].count =
4605 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004606 }
4607
4608 return true;
4609}
4610#endif
4611
4612static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004613 const json &o,
4614 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004615 int material = -1;
4616 ParseIntegerProperty(&material, err, o, "material", false);
4617 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004618
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004619 int mode = TINYGLTF_MODE_TRIANGLES;
4620 ParseIntegerProperty(&mode, err, o, "mode", false);
imallettd9ce9eb2022-10-07 10:37:09 -07004621 primitive->mode = mode; // Why only triangles were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004622
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004623 int indices = -1;
4624 ParseIntegerProperty(&indices, err, o, "indices", false);
4625 primitive->indices = indices;
4626 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4627 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004628 return false;
4629 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004630
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004631 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004632 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004633 if (FindMember(o, "targets", targetsObject) &&
4634 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004635 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4636 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4637 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004638 std::map<std::string, int> targetAttribues;
4639
jrkooncecba5d6c2019-08-29 11:26:22 -05004640 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004641 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004642 json_const_iterator dictIt(ObjectBegin(dict));
4643 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004644
jrkooncecba5d6c2019-08-29 11:26:22 -05004645 for (; dictIt != dictItEnd; ++dictIt) {
4646 int iVal;
4647 if (GetInt(GetValue(dictIt), iVal))
4648 targetAttribues[GetKey(dictIt)] = iVal;
4649 }
4650 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004651 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004652 }
4653 }
4654
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004655 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004656 ParseExtensionsProperty(&primitive->extensions, err, o);
4657
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004658 if (store_original_json_for_extras_and_extensions) {
4659 {
4660 json_const_iterator it;
4661 if (FindMember(o, "extensions", it)) {
4662 primitive->extensions_json_string = JsonToString(GetValue(it));
4663 }
4664 }
4665 {
4666 json_const_iterator it;
4667 if (FindMember(o, "extras", it)) {
4668 primitive->extras_json_string = JsonToString(GetValue(it));
4669 }
4670 }
4671 }
4672
Alex Wood7319db72019-01-24 15:38:16 -05004673#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004674 auto dracoExtension =
4675 primitive->extensions.find("KHR_draco_mesh_compression");
4676 if (dracoExtension != primitive->extensions.end()) {
4677 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004678 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004679#else
4680 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004681#endif
4682
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004683 return true;
4684}
4685
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004686static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4687 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004688 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004689
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004690 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004691 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004692 if (FindMember(o, "primitives", primObject) &&
4693 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004694 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4695 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4696 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004697 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004698 if (ParsePrimitive(&primitive, model, err, *i,
4699 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004700 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004701 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004702 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004703 }
4704 }
4705
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004706 // Should probably check if has targets and if dimensions fit
4707 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4708
Selmar09d2ff12018-03-15 17:30:42 +01004709 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004710 ParseExtrasProperty(&(mesh->extras), o);
4711
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004712 if (store_original_json_for_extras_and_extensions) {
4713 {
4714 json_const_iterator it;
4715 if (FindMember(o, "extensions", it)) {
4716 mesh->extensions_json_string = JsonToString(GetValue(it));
4717 }
4718 }
4719 {
4720 json_const_iterator it;
4721 if (FindMember(o, "extras", it)) {
4722 mesh->extras_json_string = JsonToString(GetValue(it));
4723 }
4724 }
4725 }
4726
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004727 return true;
4728}
4729
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004730static bool ParseNode(Node *node, std::string *err, const json &o,
4731 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004732 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004733
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004734 int skin = -1;
4735 ParseIntegerProperty(&skin, err, o, "skin", false);
4736 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004737
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004738 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004739 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004740 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4741 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4742 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4743 }
4744
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004745 int camera = -1;
4746 ParseIntegerProperty(&camera, err, o, "camera", false);
4747 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004748
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004749 int mesh = -1;
4750 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4751 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004752
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004753 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004754 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004755
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004756 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4757
Selmar09d2ff12018-03-15 17:30:42 +01004758 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004759 ParseExtrasProperty(&(node->extras), o);
4760
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004761 if (store_original_json_for_extras_and_extensions) {
4762 {
4763 json_const_iterator it;
4764 if (FindMember(o, "extensions", it)) {
4765 node->extensions_json_string = JsonToString(GetValue(it));
4766 }
4767 }
4768 {
4769 json_const_iterator it;
4770 if (FindMember(o, "extras", it)) {
4771 node->extras_json_string = JsonToString(GetValue(it));
4772 }
4773 }
4774 }
4775
Emanuel Schrade186322b2017-11-06 11:14:41 +01004776 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004777}
4778
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004779static bool ParsePbrMetallicRoughness(
4780 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4781 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004782 if (pbr == nullptr) {
4783 return false;
4784 }
4785
4786 std::vector<double> baseColorFactor;
4787 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4788 /* required */ false)) {
4789 if (baseColorFactor.size() != 4) {
4790 if (err) {
4791 (*err) +=
4792 "Array length of `baseColorFactor` parameter in "
4793 "pbrMetallicRoughness must be 4, but got " +
4794 std::to_string(baseColorFactor.size()) + "\n";
4795 }
4796 return false;
4797 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004798 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004799 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004800
4801 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004802 json_const_iterator it;
4803 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004804 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4805 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004806 }
4807 }
4808
4809 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004810 json_const_iterator it;
4811 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004812 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4813 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004814 }
4815 }
4816
4817 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4818 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4819
4820 ParseExtensionsProperty(&pbr->extensions, err, o);
4821 ParseExtrasProperty(&pbr->extras, o);
4822
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004823 if (store_original_json_for_extras_and_extensions) {
4824 {
4825 json_const_iterator it;
4826 if (FindMember(o, "extensions", it)) {
4827 pbr->extensions_json_string = JsonToString(GetValue(it));
4828 }
4829 }
4830 {
4831 json_const_iterator it;
4832 if (FindMember(o, "extras", it)) {
4833 pbr->extras_json_string = JsonToString(GetValue(it));
4834 }
4835 }
4836 }
4837
Syoyo Fujita046400b2019-07-24 19:26:48 +09004838 return true;
4839}
4840
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004841static bool ParseMaterial(Material *material, std::string *err, const json &o,
4842 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004843 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4844
Syoyo Fujitaff515702019-08-24 16:29:14 +09004845 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4846 "emissiveFactor",
4847 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004848 if (material->emissiveFactor.size() != 3) {
4849 if (err) {
4850 (*err) +=
4851 "Array length of `emissiveFactor` parameter in "
4852 "material must be 3, but got " +
4853 std::to_string(material->emissiveFactor.size()) + "\n";
4854 }
4855 return false;
4856 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004857 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004858 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004859 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004860 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004861
4862 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4863 /* required */ false);
4864 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4865 /* required */ false);
4866 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4867 /* required */ false);
4868
4869 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004870 json_const_iterator it;
4871 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004872 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004873 GetValue(it),
4874 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004875 }
4876 }
4877
4878 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004879 json_const_iterator it;
4880 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004881 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4882 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004883 }
4884 }
4885
4886 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004887 json_const_iterator it;
4888 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004889 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4890 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004891 }
4892 }
4893
4894 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004895 json_const_iterator it;
4896 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004897 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4898 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004899 }
4900 }
4901
4902 // Old code path. For backward compatibility, we still store material values
4903 // as Parameter. This will create duplicated information for
imallettd9ce9eb2022-10-07 10:37:09 -07004904 // example(pbrMetallicRoughness), but should be negligible in terms of memory
Syoyo Fujita046400b2019-07-24 19:26:48 +09004905 // consumption.
4906 // TODO(syoyo): Remove in the next major release.
4907 material->values.clear();
4908 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004909
jrkooncecba5d6c2019-08-29 11:26:22 -05004910 json_const_iterator it(ObjectBegin(o));
4911 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004912
jrkooncecba5d6c2019-08-29 11:26:22 -05004913 for (; it != itEnd; ++it) {
4914 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004915 if (key == "pbrMetallicRoughness") {
4916 if (IsObject(GetValue(it))) {
4917 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004918
jrkoonce06c30c42019-09-03 15:56:48 -05004919 json_const_iterator itVal(ObjectBegin(values_object));
4920 json_const_iterator itValEnd(ObjectEnd(values_object));
4921
4922 for (; itVal != itValEnd; ++itVal) {
4923 Parameter param;
4924 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004925 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004926 material->values.emplace(GetKey(itVal), std::move(param));
4927 }
4928 }
4929 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004930 } else if (key == "extensions" || key == "extras") {
4931 // done later, skip, otherwise poorly parsed contents will be saved in the
4932 // parametermap and serialized again later
4933 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004934 Parameter param;
4935 if (ParseParameterProperty(&param, err, o, key, false)) {
4936 // names of materials have already been parsed. Putting it in this map
imallettd9ce9eb2022-10-07 10:37:09 -07004937 // doesn't correctly reflect the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004938 if (key != "name")
4939 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004940 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004941 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004942 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004943
Syoyo Fujita046400b2019-07-24 19:26:48 +09004944 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004945 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004946 ParseExtrasProperty(&(material->extras), o);
4947
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004948 if (store_original_json_for_extras_and_extensions) {
4949 {
4950 json_const_iterator eit;
4951 if (FindMember(o, "extensions", eit)) {
4952 material->extensions_json_string = JsonToString(GetValue(eit));
4953 }
4954 }
4955 {
4956 json_const_iterator eit;
4957 if (FindMember(o, "extras", eit)) {
4958 material->extras_json_string = JsonToString(GetValue(eit));
4959 }
4960 }
4961 }
4962
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004963 return true;
4964}
4965
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004966static bool ParseAnimationChannel(
4967 AnimationChannel *channel, std::string *err, const json &o,
4968 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004969 int samplerIndex = -1;
4970 int targetIndex = -1;
4971 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4972 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004973 if (err) {
4974 (*err) += "`sampler` field is missing in animation channels\n";
4975 }
4976 return false;
4977 }
4978
jrkooncecba5d6c2019-08-29 11:26:22 -05004979 json_const_iterator targetIt;
4980 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4981 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004982
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004983 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004984 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004985 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004986 }
4987 return false;
4988 }
4989
4990 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4991 true)) {
4992 if (err) {
4993 (*err) += "`path` field is missing in animation.channels.target\n";
4994 }
4995 return false;
4996 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004997 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4998 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01004999 json_const_iterator it;
5000 if (FindMember(target_object, "extensions", it)) {
5001 channel->target_extensions_json_string = JsonToString(GetValue(it));
5002 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005003 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005004 }
5005
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005006 channel->sampler = samplerIndex;
5007 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005008
Selmar Kok4e2988e2019-08-16 14:08:08 +02005009 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005010 ParseExtrasProperty(&(channel->extras), o);
5011
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005012 if (store_original_json_for_extras_and_extensions) {
5013 {
5014 json_const_iterator it;
5015 if (FindMember(o, "extensions", it)) {
5016 channel->extensions_json_string = JsonToString(GetValue(it));
5017 }
5018 }
5019 {
5020 json_const_iterator it;
5021 if (FindMember(o, "extras", it)) {
5022 channel->extras_json_string = JsonToString(GetValue(it));
5023 }
5024 }
5025 }
5026
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005027 return true;
5028}
5029
5030static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005031 const json &o,
5032 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005033 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005034 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005035 if (FindMember(o, "channels", channelsIt) &&
5036 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005037 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
5038 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
5039 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005040 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005041 if (ParseAnimationChannel(
5042 &channel, err, *i,
5043 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005044 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005045 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005046 }
5047 }
5048 }
5049 }
5050
5051 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005052 json_const_iterator samplerIt;
5053 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5054 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005055
jrkooncecba5d6c2019-08-29 11:26:22 -05005056 json_const_array_iterator it = ArrayBegin(sampler_array);
5057 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005058
jrkooncecba5d6c2019-08-29 11:26:22 -05005059 for (; it != itEnd; ++it) {
5060 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005061
5062 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005063 int inputIndex = -1;
5064 int outputIndex = -1;
5065 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005066 if (err) {
5067 (*err) += "`input` field is missing in animation.sampler\n";
5068 }
5069 return false;
5070 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005071 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5072 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005073 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005074 if (err) {
5075 (*err) += "`output` field is missing in animation.sampler\n";
5076 }
5077 return false;
5078 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005079 sampler.input = inputIndex;
5080 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005081 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005082 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005083
5084 if (store_original_json_for_extras_and_extensions) {
5085 {
5086 json_const_iterator eit;
5087 if (FindMember(o, "extensions", eit)) {
5088 sampler.extensions_json_string = JsonToString(GetValue(eit));
5089 }
5090 }
5091 {
5092 json_const_iterator eit;
5093 if (FindMember(o, "extras", eit)) {
5094 sampler.extras_json_string = JsonToString(GetValue(eit));
5095 }
5096 }
5097 }
5098
jrkooncecba5d6c2019-08-29 11:26:22 -05005099 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005100 }
5101 }
5102 }
5103
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005104 ParseStringProperty(&animation->name, err, o, "name", false);
5105
Selmar Kok4e2988e2019-08-16 14:08:08 +02005106 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005107 ParseExtrasProperty(&(animation->extras), o);
5108
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005109 if (store_original_json_for_extras_and_extensions) {
5110 {
5111 json_const_iterator it;
5112 if (FindMember(o, "extensions", it)) {
5113 animation->extensions_json_string = JsonToString(GetValue(it));
5114 }
5115 }
5116 {
5117 json_const_iterator it;
5118 if (FindMember(o, "extras", it)) {
5119 animation->extras_json_string = JsonToString(GetValue(it));
5120 }
5121 }
5122 }
5123
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005124 return true;
5125}
5126
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005127static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5128 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005129 ParseStringProperty(&sampler->name, err, o, "name", false);
5130
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005131 int minFilter = -1;
5132 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005133 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5134 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005135 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005136 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5137 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5138 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5139 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005140 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5141 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005142
imallettd9ce9eb2022-10-07 10:37:09 -07005143 // TODO(syoyo): Check the value is allowed one.
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005144 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005145
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005146 sampler->minFilter = minFilter;
5147 sampler->magFilter = magFilter;
5148 sampler->wrapS = wrapS;
5149 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005150 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005151
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005152 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005153 ParseExtrasProperty(&(sampler->extras), o);
5154
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005155 if (store_original_json_for_extras_and_extensions) {
5156 {
5157 json_const_iterator it;
5158 if (FindMember(o, "extensions", it)) {
5159 sampler->extensions_json_string = JsonToString(GetValue(it));
5160 }
5161 }
5162 {
5163 json_const_iterator it;
5164 if (FindMember(o, "extras", it)) {
5165 sampler->extras_json_string = JsonToString(GetValue(it));
5166 }
5167 }
5168 }
5169
Syoyo Fujitac2615632016-06-19 21:56:06 +09005170 return true;
5171}
5172
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005173static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5174 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005175 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005176
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005177 std::vector<int> joints;
5178 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005179 return false;
5180 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005181 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005182
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005183 int skeleton = -1;
5184 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5185 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005186
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005187 int invBind = -1;
5188 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5189 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005190
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005191 ParseExtensionsProperty(&(skin->extensions), err, o);
5192 ParseExtrasProperty(&(skin->extras), o);
5193
5194 if (store_original_json_for_extras_and_extensions) {
5195 {
5196 json_const_iterator it;
5197 if (FindMember(o, "extensions", it)) {
5198 skin->extensions_json_string = JsonToString(GetValue(it));
5199 }
5200 }
5201 {
5202 json_const_iterator it;
5203 if (FindMember(o, "extras", it)) {
5204 skin->extras_json_string = JsonToString(GetValue(it));
5205 }
5206 }
5207 }
5208
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005209 return true;
5210}
5211
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005212static bool ParsePerspectiveCamera(
5213 PerspectiveCamera *camera, std::string *err, const json &o,
5214 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005215 double yfov = 0.0;
5216 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5217 return false;
5218 }
5219
5220 double znear = 0.0;
5221 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5222 "PerspectiveCamera")) {
5223 return false;
5224 }
5225
5226 double aspectRatio = 0.0; // = invalid
5227 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5228 "PerspectiveCamera");
5229
5230 double zfar = 0.0; // = invalid
5231 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5232
Selmar Kok31cb7f92018-10-03 15:39:05 +02005233 camera->aspectRatio = aspectRatio;
5234 camera->zfar = zfar;
5235 camera->yfov = yfov;
5236 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005237
Selmar09d2ff12018-03-15 17:30:42 +01005238 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005239 ParseExtrasProperty(&(camera->extras), o);
5240
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005241 if (store_original_json_for_extras_and_extensions) {
5242 {
5243 json_const_iterator it;
5244 if (FindMember(o, "extensions", it)) {
5245 camera->extensions_json_string = JsonToString(GetValue(it));
5246 }
5247 }
5248 {
5249 json_const_iterator it;
5250 if (FindMember(o, "extras", it)) {
5251 camera->extras_json_string = JsonToString(GetValue(it));
5252 }
5253 }
5254 }
5255
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005256 // TODO(syoyo): Validate parameter values.
5257
5258 return true;
5259}
5260
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005261static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5262 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005263 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5264 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005265
Johan Bowald52936a02019-07-17 09:06:45 +02005266 ParseExtensionsProperty(&light->extensions, err, o);
5267 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005268
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005269 if (store_original_json_for_extras_and_extensions) {
5270 {
5271 json_const_iterator it;
5272 if (FindMember(o, "extensions", it)) {
5273 light->extensions_json_string = JsonToString(GetValue(it));
5274 }
5275 }
5276 {
5277 json_const_iterator it;
5278 if (FindMember(o, "extras", it)) {
5279 light->extras_json_string = JsonToString(GetValue(it));
5280 }
5281 }
5282 }
5283
Johan Bowald52936a02019-07-17 09:06:45 +02005284 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005285
Johan Bowald52936a02019-07-17 09:06:45 +02005286 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005287}
5288
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005289static bool ParseOrthographicCamera(
5290 OrthographicCamera *camera, std::string *err, const json &o,
5291 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005292 double xmag = 0.0;
5293 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5294 return false;
5295 }
5296
5297 double ymag = 0.0;
5298 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5299 return false;
5300 }
5301
5302 double zfar = 0.0;
5303 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5304 return false;
5305 }
5306
5307 double znear = 0.0;
5308 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5309 "OrthographicCamera")) {
5310 return false;
5311 }
5312
Selmar09d2ff12018-03-15 17:30:42 +01005313 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005314 ParseExtrasProperty(&(camera->extras), o);
5315
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005316 if (store_original_json_for_extras_and_extensions) {
5317 {
5318 json_const_iterator it;
5319 if (FindMember(o, "extensions", it)) {
5320 camera->extensions_json_string = JsonToString(GetValue(it));
5321 }
5322 }
5323 {
5324 json_const_iterator it;
5325 if (FindMember(o, "extras", it)) {
5326 camera->extras_json_string = JsonToString(GetValue(it));
5327 }
5328 }
5329 }
5330
Selmar Kok31cb7f92018-10-03 15:39:05 +02005331 camera->xmag = xmag;
5332 camera->ymag = ymag;
5333 camera->zfar = zfar;
5334 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005335
5336 // TODO(syoyo): Validate parameter values.
5337
5338 return true;
5339}
5340
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005341static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5342 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005343 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5344 return false;
5345 }
5346
5347 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005348 json_const_iterator orthoIt;
5349 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005350 if (err) {
5351 std::stringstream ss;
imallettd9ce9eb2022-10-07 10:37:09 -07005352 ss << "Orthographic camera description not found." << std::endl;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005353 (*err) += ss.str();
5354 }
5355 return false;
5356 }
5357
jrkooncecba5d6c2019-08-29 11:26:22 -05005358 const json &v = GetValue(orthoIt);
5359 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005360 if (err) {
5361 std::stringstream ss;
5362 ss << "\"orthographic\" is not a JSON object." << std::endl;
5363 (*err) += ss.str();
5364 }
5365 return false;
5366 }
5367
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005368 if (!ParseOrthographicCamera(
5369 &camera->orthographic, err, v,
5370 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005371 return false;
5372 }
5373 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005374 json_const_iterator perspIt;
5375 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005376 if (err) {
5377 std::stringstream ss;
5378 ss << "Perspective camera description not found." << std::endl;
5379 (*err) += ss.str();
5380 }
5381 return false;
5382 }
5383
jrkooncecba5d6c2019-08-29 11:26:22 -05005384 const json &v = GetValue(perspIt);
5385 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005386 if (err) {
5387 std::stringstream ss;
5388 ss << "\"perspective\" is not a JSON object." << std::endl;
5389 (*err) += ss.str();
5390 }
5391 return false;
5392 }
5393
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005394 if (!ParsePerspectiveCamera(
5395 &camera->perspective, err, v,
5396 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005397 return false;
5398 }
5399 } else {
5400 if (err) {
5401 std::stringstream ss;
5402 ss << "Invalid camera type: \"" << camera->type
5403 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5404 (*err) += ss.str();
5405 }
5406 return false;
5407 }
5408
5409 ParseStringProperty(&camera->name, err, o, "name", false);
5410
Selmar09d2ff12018-03-15 17:30:42 +01005411 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005412 ParseExtrasProperty(&(camera->extras), o);
5413
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005414 if (store_original_json_for_extras_and_extensions) {
5415 {
5416 json_const_iterator it;
5417 if (FindMember(o, "extensions", it)) {
5418 camera->extensions_json_string = JsonToString(GetValue(it));
5419 }
5420 }
5421 {
5422 json_const_iterator it;
5423 if (FindMember(o, "extras", it)) {
5424 camera->extras_json_string = JsonToString(GetValue(it));
5425 }
5426 }
5427 }
5428
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005429 return true;
5430}
5431
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005432static bool ParseLight(Light *light, std::string *err, const json &o,
5433 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005434 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5435 return false;
5436 }
5437
5438 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005439 json_const_iterator spotIt;
5440 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005441 if (err) {
5442 std::stringstream ss;
5443 ss << "Spot light description not found." << std::endl;
5444 (*err) += ss.str();
5445 }
5446 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005447 }
5448
jrkooncecba5d6c2019-08-29 11:26:22 -05005449 const json &v = GetValue(spotIt);
5450 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005451 if (err) {
5452 std::stringstream ss;
5453 ss << "\"spot\" is not a JSON object." << std::endl;
5454 (*err) += ss.str();
5455 }
5456 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005457 }
5458
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005459 if (!ParseSpotLight(&light->spot, err, v,
5460 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005461 return false;
5462 }
5463 }
5464
5465 ParseStringProperty(&light->name, err, o, "name", false);
5466 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5467 ParseNumberProperty(&light->range, err, o, "range", false);
5468 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5469 ParseExtensionsProperty(&light->extensions, err, o);
5470 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005471
5472 if (store_original_json_for_extras_and_extensions) {
5473 {
5474 json_const_iterator it;
5475 if (FindMember(o, "extensions", it)) {
5476 light->extensions_json_string = JsonToString(GetValue(it));
5477 }
5478 }
5479 {
5480 json_const_iterator it;
5481 if (FindMember(o, "extras", it)) {
5482 light->extras_json_string = JsonToString(GetValue(it));
5483 }
5484 }
5485 }
5486
Johan Bowald52936a02019-07-17 09:06:45 +02005487 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005488}
5489
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005490bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005491 const char *json_str,
5492 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005493 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005494 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005495 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005496 if (err) {
5497 (*err) = "JSON string too short.\n";
5498 }
5499 return false;
5500 }
5501
jrkooncecba5d6c2019-08-29 11:26:22 -05005502 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005503
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005504#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5505 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005506 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005507 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005508 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005509
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005510 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005511 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005512 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005513 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005514 return false;
5515 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005516#else
5517 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005518 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005519
jrkooncecba5d6c2019-08-29 11:26:22 -05005520 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005521 // Assume parsing was failed.
5522 if (err) {
5523 (*err) = "Failed to parse JSON object\n";
5524 }
5525 return false;
5526 }
5527 }
5528#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005529
jrkooncecba5d6c2019-08-29 11:26:22 -05005530 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005531 // root is not an object.
5532 if (err) {
5533 (*err) = "Root element is not a JSON object\n";
5534 }
5535 return false;
5536 }
5537
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005538 {
5539 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005540 json_const_iterator it;
5541 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005542 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005543 json_const_iterator version_it;
5544 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005545 if (FindMember(itObj, "version", version_it) &&
5546 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005547 version_found = true;
5548 }
5549 }
5550 if (version_found) {
5551 // OK
5552 } else if (check_sections & REQUIRE_VERSION) {
5553 if (err) {
5554 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5555 }
5556 return false;
5557 }
5558 }
5559
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005560 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005561 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005562
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005563 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005564 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005565 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005566 };
5567
Syoyo Fujita83675312017-12-02 21:14:13 +09005568 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005569 if ((check_sections & REQUIRE_SCENES) &&
5570 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005571 if (err) {
5572 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5573 }
5574 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005575 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005576 }
5577
Syoyo Fujita83675312017-12-02 21:14:13 +09005578 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005579 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005580 if (err) {
5581 (*err) += "\"nodes\" object not found in .gltf\n";
5582 }
5583 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005584 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005585 }
5586
Syoyo Fujita83675312017-12-02 21:14:13 +09005587 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005588 if ((check_sections & REQUIRE_ACCESSORS) &&
5589 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005590 if (err) {
5591 (*err) += "\"accessors\" object not found in .gltf\n";
5592 }
5593 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005594 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005595 }
5596
Syoyo Fujita83675312017-12-02 21:14:13 +09005597 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005598 if ((check_sections & REQUIRE_BUFFERS) &&
5599 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005600 if (err) {
5601 (*err) += "\"buffers\" object not found in .gltf\n";
5602 }
5603 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005604 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005605 }
5606
Syoyo Fujita83675312017-12-02 21:14:13 +09005607 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005608 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5609 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005610 if (err) {
5611 (*err) += "\"bufferViews\" object not found in .gltf\n";
5612 }
5613 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005614 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005615 }
5616
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005617 model->buffers.clear();
5618 model->bufferViews.clear();
5619 model->accessors.clear();
5620 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005621 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005622 model->nodes.clear();
5623 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005624 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005625 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005626 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005627
Syoyo Fujita83675312017-12-02 21:14:13 +09005628 // 1. Parse Asset
5629 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005630 json_const_iterator it;
5631 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5632 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005633
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005634 ParseAsset(&model->asset, err, root,
5635 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005636 }
5637 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005638
jrkoonce51453942019-09-03 09:48:30 -05005639#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005640 auto ForEachInArray = [](const json &_v, const char *member,
5641 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005642#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005643 // The std::function<> implementation can be less efficient because it will
5644 // allocate heap when the size of the captured lambda is above 16 bytes with
5645 // clang and gcc, but it does not require C++14.
5646 auto ForEachInArray = [](const json &_v, const char *member,
5647 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005648#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005649 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005650 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005651 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005652 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005653 auto it = ArrayBegin(root);
5654 auto end = ArrayEnd(root);
5655 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005656 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005657 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005658 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005659 return true;
5660 };
5661
jrkooncecba5d6c2019-08-29 11:26:22 -05005662 // 2. Parse extensionUsed
5663 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005664 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005665 std::string str;
5666 GetString(o, str);
5667 model->extensionsUsed.emplace_back(std::move(str));
5668 return true;
5669 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005670 }
5671
Syoyo Fujita83675312017-12-02 21:14:13 +09005672 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005673 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005674 std::string str;
5675 GetString(o, str);
5676 model->extensionsRequired.emplace_back(std::move(str));
5677 return true;
5678 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005679 }
5680
Syoyo Fujita83675312017-12-02 21:14:13 +09005681 // 3. Parse Buffer
5682 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005683 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005684 if (!IsObject(o)) {
5685 if (err) {
5686 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005687 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005688 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005689 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005690 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005691 if (!ParseBuffer(&buffer, err, o,
5692 store_original_json_for_extras_and_extensions_, &fs,
5693 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005694 return false;
5695 }
5696
5697 model->buffers.emplace_back(std::move(buffer));
5698 return true;
5699 });
5700
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005701 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005702 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005703 }
5704 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005705 // 4. Parse BufferView
5706 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005707 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005708 if (!IsObject(o)) {
5709 if (err) {
5710 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005711 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005712 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005713 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005714 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005715 if (!ParseBufferView(&bufferView, err, o,
5716 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005717 return false;
5718 }
5719
5720 model->bufferViews.emplace_back(std::move(bufferView));
5721 return true;
5722 });
5723
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005724 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005725 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005726 }
5727 }
5728
Syoyo Fujita83675312017-12-02 21:14:13 +09005729 // 5. Parse Accessor
5730 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005731 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005732 if (!IsObject(o)) {
5733 if (err) {
5734 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005735 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005736 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005737 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005738 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005739 if (!ParseAccessor(&accessor, err, o,
5740 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005741 return false;
5742 }
5743
5744 model->accessors.emplace_back(std::move(accessor));
5745 return true;
5746 });
5747
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005748 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005749 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005750 }
5751 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005752
Syoyo Fujita83675312017-12-02 21:14:13 +09005753 // 6. Parse Mesh
5754 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005755 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005756 if (!IsObject(o)) {
5757 if (err) {
5758 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005759 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005760 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005761 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005762 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005763 if (!ParseMesh(&mesh, model, err, o,
5764 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005765 return false;
5766 }
5767
5768 model->meshes.emplace_back(std::move(mesh));
5769 return true;
5770 });
5771
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005772 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005773 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005774 }
5775 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005776
viperscape9df05802018-12-05 14:11:01 -05005777 // Assign missing bufferView target types
5778 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005779 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005780 for (auto &mesh : model->meshes) {
5781 for (auto &primitive : mesh.primitives) {
5782 if (primitive.indices >
5783 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005784 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005785 if (size_t(primitive.indices) >= model->accessors.size()) {
5786 if (err) {
5787 (*err) += "primitive indices accessor out of bounds";
5788 }
5789 return false;
5790 }
5791
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005792 auto bufferView =
5793 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005794 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5795 if (err) {
5796 (*err) += "accessor[" + std::to_string(primitive.indices) +
5797 "] invalid bufferView";
5798 }
5799 return false;
5800 }
5801
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005802 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005803 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
imallettd9ce9eb2022-10-07 10:37:09 -07005804 // we could optionally check if accessors' bufferView type is Scalar, as
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005805 // it should be
viperscape9df05802018-12-05 14:11:01 -05005806 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005807
5808 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07005809 const auto accessorsIndex = size_t(attribute.second);
5810 if (accessorsIndex < model->accessors.size()) {
5811 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5812 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005813 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005814 model->bufferViews[size_t(bufferView)].target =
5815 TINYGLTF_TARGET_ARRAY_BUFFER;
5816 }
5817 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005818 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005819
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005820 for (auto &target : primitive.targets) {
5821 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07005822 const auto accessorsIndex = size_t(attribute.second);
5823 if (accessorsIndex < model->accessors.size()) {
5824 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5825 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005826 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005827 model->bufferViews[size_t(bufferView)].target =
5828 TINYGLTF_TARGET_ARRAY_BUFFER;
5829 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04005830 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005831 }
5832 }
viperscape9df05802018-12-05 14:11:01 -05005833 }
5834 }
5835
Syoyo Fujita83675312017-12-02 21:14:13 +09005836 // 7. Parse Node
5837 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005838 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005839 if (!IsObject(o)) {
5840 if (err) {
5841 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005842 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005843 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005844 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005845 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005846 if (!ParseNode(&node, err, o,
5847 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005848 return false;
5849 }
5850
5851 model->nodes.emplace_back(std::move(node));
5852 return true;
5853 });
5854
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005855 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005856 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005857 }
5858 }
5859
5860 // 8. Parse scenes.
5861 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005862 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005863 if (!IsObject(o)) {
5864 if (err) {
5865 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005866 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005867 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005868 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005869 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005870 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005871
5872 Scene scene;
5873 scene.nodes = std::move(nodes);
5874
5875 ParseStringProperty(&scene.name, err, o, "name", false);
5876
5877 ParseExtensionsProperty(&scene.extensions, err, o);
5878 ParseExtrasProperty(&scene.extras, o);
5879
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005880 if (store_original_json_for_extras_and_extensions_) {
5881 {
5882 json_const_iterator it;
5883 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005884 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005885 }
5886 }
5887 {
5888 json_const_iterator it;
5889 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005890 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005891 }
5892 }
5893 }
5894
jrkooncecba5d6c2019-08-29 11:26:22 -05005895 model->scenes.emplace_back(std::move(scene));
5896 return true;
5897 });
5898
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005899 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005900 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005901 }
5902 }
5903
5904 // 9. Parse default scenes.
5905 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005906 json_const_iterator rootIt;
5907 int iVal;
5908 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5909 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005910 }
5911 }
5912
5913 // 10. Parse Material
5914 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005915 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005916 if (!IsObject(o)) {
5917 if (err) {
5918 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005919 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005920 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005921 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005922 Material material;
5923 ParseStringProperty(&material.name, err, o, "name", false);
5924
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005925 if (!ParseMaterial(&material, err, o,
5926 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005927 return false;
5928 }
5929
5930 model->materials.emplace_back(std::move(material));
5931 return true;
5932 });
5933
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005934 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005935 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005936 }
5937 }
5938
5939 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005940 void *load_image_user_data{nullptr};
5941
5942 LoadImageDataOption load_image_option;
5943
5944 if (user_image_loader_) {
5945 // Use user supplied pointer
5946 load_image_user_data = load_image_user_data_;
5947 } else {
5948 load_image_option.preserve_channels = preserve_image_channels_;
5949 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5950 }
5951
Syoyo Fujita83675312017-12-02 21:14:13 +09005952 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005953 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005954 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005955 if (!IsObject(o)) {
5956 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005957 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005958 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005959 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005960 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005961 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005962 if (!ParseImage(&image, idx, err, warn, o,
5963 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005964 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005965 return false;
5966 }
5967
5968 if (image.bufferView != -1) {
5969 // Load image from the buffer view.
5970 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5971 if (err) {
5972 std::stringstream ss;
5973 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005974 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005975 (*err) += ss.str();
5976 }
5977 return false;
5978 }
5979
5980 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005981 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005982 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5983 if (err) {
5984 std::stringstream ss;
5985 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005986 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005987 (*err) += ss.str();
5988 }
5989 return false;
5990 }
5991 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5992
5993 if (*LoadImageData == nullptr) {
5994 if (err) {
5995 (*err) += "No LoadImageData callback specified.\n";
5996 }
5997 return false;
5998 }
5999 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006000 &image, idx, err, warn, image.width, image.height,
6001 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006002 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05006003 if (!ret) {
6004 return false;
6005 }
6006 }
6007
6008 model->images.emplace_back(std::move(image));
6009 ++idx;
6010 return true;
6011 });
6012
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006013 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006014 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006015 }
6016 }
6017
6018 // 12. Parse Texture
6019 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006020 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006021 if (!IsObject(o)) {
6022 if (err) {
6023 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006024 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006025 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006026 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006027 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006028 if (!ParseTexture(&texture, err, o,
6029 store_original_json_for_extras_and_extensions_,
6030 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006031 return false;
6032 }
6033
6034 model->textures.emplace_back(std::move(texture));
6035 return true;
6036 });
6037
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006038 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006039 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006040 }
6041 }
6042
6043 // 13. Parse Animation
6044 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006045 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006046 if (!IsObject(o)) {
6047 if (err) {
6048 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006049 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006050 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006051 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006052 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006053 if (!ParseAnimation(&animation, err, o,
6054 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006055 return false;
6056 }
6057
6058 model->animations.emplace_back(std::move(animation));
6059 return true;
6060 });
6061
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006062 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006063 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006064 }
6065 }
6066
6067 // 14. Parse Skin
6068 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006069 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006070 if (!IsObject(o)) {
6071 if (err) {
6072 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006073 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006074 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006075 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006076 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006077 if (!ParseSkin(&skin, err, o,
6078 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006079 return false;
6080 }
6081
6082 model->skins.emplace_back(std::move(skin));
6083 return true;
6084 });
6085
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006086 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006087 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006088 }
6089 }
6090
6091 // 15. Parse Sampler
6092 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006093 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006094 if (!IsObject(o)) {
6095 if (err) {
6096 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006097 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006098 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006099 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006100 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006101 if (!ParseSampler(&sampler, err, o,
6102 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006103 return false;
6104 }
6105
6106 model->samplers.emplace_back(std::move(sampler));
6107 return true;
6108 });
6109
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006110 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006111 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006112 }
6113 }
6114
6115 // 16. Parse Camera
6116 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006117 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006118 if (!IsObject(o)) {
6119 if (err) {
6120 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006121 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006122 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006123 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006124 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006125 if (!ParseCamera(&camera, err, o,
6126 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006127 return false;
6128 }
6129
6130 model->cameras.emplace_back(std::move(camera));
6131 return true;
6132 });
6133
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006134 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006135 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006136 }
6137 }
6138
6139 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006140 ParseExtensionsProperty(&model->extensions, err, v);
6141
6142 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006143 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006144 json_const_iterator rootIt;
6145 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6146 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006147
jrkooncecba5d6c2019-08-29 11:26:22 -05006148 json_const_iterator it(ObjectBegin(root));
6149 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006150 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006151 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006152 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006153 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006154 const json &object = GetValue(it);
6155 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006156 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006157 const json &lights = GetValue(itLight);
6158 if (!IsArray(lights)) {
6159 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006160 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006161
6162 auto arrayIt(ArrayBegin(lights));
6163 auto arrayItEnd(ArrayEnd(lights));
6164 for (; arrayIt != arrayItEnd; ++arrayIt) {
6165 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006166 if (!ParseLight(&light, err, *arrayIt,
6167 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006168 return false;
6169 }
6170 model->lights.emplace_back(std::move(light));
6171 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006172 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006173 }
6174 }
6175 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006176 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006177
mynzc0d4d1c2018-06-28 23:06:00 +09006178 // 19. Parse Extras
6179 ParseExtrasProperty(&model->extras, v);
6180
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006181 if (store_original_json_for_extras_and_extensions_) {
6182 model->extras_json_string = JsonToString(v["extras"]);
6183 model->extensions_json_string = JsonToString(v["extensions"]);
6184 }
6185
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006186 return true;
6187}
6188
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006189bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006190 std::string *warn, const char *str,
6191 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006192 const std::string &base_dir,
6193 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006194 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006195 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006196 bin_size_ = 0;
6197
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006198 return LoadFromString(model, err, warn, str, length, base_dir,
6199 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006200}
6201
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006202bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006203 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006204 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006205 std::stringstream ss;
6206
Paolo Jovone6601bf2018-07-07 20:43:33 +02006207 if (fs.ReadWholeFile == nullptr) {
6208 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006209 ss << "Failed to read file: " << filename
6210 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006211 if (err) {
6212 (*err) = ss.str();
6213 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006214 return false;
6215 }
6216
Paolo Jovone6601bf2018-07-07 20:43:33 +02006217 std::vector<unsigned char> data;
6218 std::string fileerr;
6219 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006220 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006221 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6222 if (err) {
6223 (*err) = ss.str();
6224 }
6225 return false;
6226 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006227
Paolo Jovone6601bf2018-07-07 20:43:33 +02006228 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006229 if (sz == 0) {
6230 if (err) {
6231 (*err) = "Empty file.";
6232 }
6233 return false;
6234 }
6235
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006236 std::string basedir = GetBaseDir(filename);
6237
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006238 bool ret = LoadASCIIFromString(
6239 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6240 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006241
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006242 return ret;
6243}
6244
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006245bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006246 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006247 const unsigned char *bytes,
6248 unsigned int size,
6249 const std::string &base_dir,
6250 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006251 if (size < 20) {
6252 if (err) {
6253 (*err) = "Too short data size for glTF Binary.";
6254 }
6255 return false;
6256 }
6257
Syoyo Fujitabeded612016-05-01 20:03:43 +09006258 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6259 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006260 // ok
6261 } else {
6262 if (err) {
6263 (*err) = "Invalid magic.";
6264 }
6265 return false;
6266 }
6267
Syoyo Fujitabeded612016-05-01 20:03:43 +09006268 unsigned int version; // 4 bytes
6269 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006270 unsigned int chunk0_length; // 4 bytes
6271 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006272
Syoyo Fujitabeded612016-05-01 20:03:43 +09006273 memcpy(&version, bytes + 4, 4);
6274 swap4(&version);
6275 memcpy(&length, bytes + 8, 4);
6276 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006277 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6278 swap4(&chunk0_length);
6279 memcpy(&chunk0_format, bytes + 16, 4);
6280 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006281
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006282 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6283 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006284 // In case the Bin buffer is not present, the size is exactly 20 + size of
6285 // JSON contents,
6286 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006287 //
6288 // https://github.com/syoyo/tinygltf/issues/372
6289 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006290 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006291
Syoyo Fujitac670f082022-09-17 19:52:25 +09006292 if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006293 // Do not allow 4GB or more GLB data.
6294 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6295 }
6296
Syoyo Fujitac670f082022-09-17 19:52:25 +09006297 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6298 (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006299 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006300 if (err) {
6301 (*err) = "Invalid glTF binary.";
6302 }
6303 return false;
6304 }
6305
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006306 // Padding check
6307 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6308 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006309 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006310 if (err) {
6311 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6312 }
6313 }
6314
Syoyo Fujita612e5782022-09-18 21:01:39 +09006315 //std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6316 //std::cout << "length = " << length << "\n";
6317
Syoyo Fujitac670f082022-09-17 19:52:25 +09006318 // Chunk1(BIN) data
6319 // 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 +09006320 // So when header + JSON data == binary size, Chunk1 is omitted.
6321 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006322
Syoyo Fujitac670f082022-09-17 19:52:25 +09006323 bin_data_ = nullptr;
6324 bin_size_ = 0;
6325 } else {
6326 // Read Chunk1 info(BIN data)
imallettd9ce9eb2022-10-07 10:37:09 -07006327 // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes)
Syoyo Fujita612e5782022-09-18 21:01:39 +09006328 if ((header_and_json_size + 12ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006329 if (err) {
Syoyo Fujita612e5782022-09-18 21:01:39 +09006330 (*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 +09006331 }
6332 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006333 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006334
Syoyo Fujitac670f082022-09-17 19:52:25 +09006335 unsigned int chunk1_length; // 4 bytes
6336 unsigned int chunk1_format; // 4 bytes;
6337 memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
6338 swap4(&chunk1_length);
6339 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6340 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006341
Syoyo Fujita612e5782022-09-18 21:01:39 +09006342 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6343
Syoyo Fujitac670f082022-09-17 19:52:25 +09006344 if (chunk1_length < 4) {
6345 if (err) {
6346 (*err) = "Insufficient Chunk1(BIN) data size.";
6347 }
6348 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006349 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006350
Syoyo Fujitac670f082022-09-17 19:52:25 +09006351 if ((chunk1_length % 4) != 0) {
6352 if (err) {
6353 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6354 }
6355 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006356 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006357
Syoyo Fujitac670f082022-09-17 19:52:25 +09006358 if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
6359 if (err) {
6360 (*err) = "BIN Chunk data length exceeds the GLB size.";
6361 }
6362 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006363 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006364
Syoyo Fujitac670f082022-09-17 19:52:25 +09006365 if (chunk1_format != 0x004e4942) {
6366 if (err) {
imallettd9ce9eb2022-10-07 10:37:09 -07006367 (*err) = "Invalid type for chunk1 data.";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006368 }
6369 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006370 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006371
Syoyo Fujita612e5782022-09-18 21:01:39 +09006372 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6373
Syoyo Fujitac670f082022-09-17 19:52:25 +09006374 bin_data_ = bytes + header_and_json_size +
6375 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6376
6377 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006378 }
6379
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006380 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006381 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006382 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006383
6384 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006385
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006386 bool ret = LoadFromString(model, err, warn,
6387 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006388 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006389 if (!ret) {
6390 return ret;
6391 }
6392
6393 return true;
6394}
6395
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006396bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006397 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006398 const std::string &filename,
6399 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006400 std::stringstream ss;
6401
Paolo Jovone6601bf2018-07-07 20:43:33 +02006402 if (fs.ReadWholeFile == nullptr) {
6403 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006404 ss << "Failed to read file: " << filename
6405 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006406 if (err) {
6407 (*err) = ss.str();
6408 }
6409 return false;
6410 }
6411
Paolo Jovone6601bf2018-07-07 20:43:33 +02006412 std::vector<unsigned char> data;
6413 std::string fileerr;
6414 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006415 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006416 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6417 if (err) {
6418 (*err) = ss.str();
6419 }
6420 return false;
6421 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006422
Syoyo Fujitabeded612016-05-01 20:03:43 +09006423 std::string basedir = GetBaseDir(filename);
6424
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006425 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6426 static_cast<unsigned int>(data.size()),
6427 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006428
6429 return ret;
6430}
6431
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006432///////////////////////
6433// GLTF Serialization
6434///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006435namespace {
6436json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006437#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006438 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006439#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006440 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006441#endif
jrkoonce63419a12019-09-03 17:06:41 -05006442}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006443
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006444void JsonAssign(json &dest, const json &src) {
6445#ifdef TINYGLTF_USE_RAPIDJSON
6446 dest.CopyFrom(src, GetAllocator());
6447#else
6448 dest = src;
6449#endif
6450}
6451
6452void JsonAddMember(json &o, const char *key, json &&value) {
6453#ifdef TINYGLTF_USE_RAPIDJSON
6454 if (!o.IsObject()) {
6455 o.SetObject();
6456 }
6457 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6458#else
6459 o[key] = std::move(value);
6460#endif
6461}
6462
6463void JsonPushBack(json &o, json &&value) {
6464#ifdef TINYGLTF_USE_RAPIDJSON
6465 o.PushBack(std::move(value), GetAllocator());
6466#else
6467 o.push_back(std::move(value));
6468#endif
6469}
6470
6471bool JsonIsNull(const json &o) {
6472#ifdef TINYGLTF_USE_RAPIDJSON
6473 return o.IsNull();
6474#else
6475 return o.is_null();
6476#endif
6477}
6478
6479void JsonSetObject(json &o) {
6480#ifdef TINYGLTF_USE_RAPIDJSON
6481 o.SetObject();
6482#else
6483 o = o.object({});
6484#endif
6485}
6486
6487void JsonReserveArray(json &o, size_t s) {
6488#ifdef TINYGLTF_USE_RAPIDJSON
6489 o.SetArray();
6490 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6491#endif
6492 (void)(o);
6493 (void)(s);
6494}
6495} // namespace
6496
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006497// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006498
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006499template <typename T>
6500static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006501 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006502 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006503 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006504 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006505 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006506}
6507
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006508#ifdef TINYGLTF_USE_RAPIDJSON
6509template <>
6510void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6511 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6512}
6513#endif
6514
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006515template <typename T>
6516static void SerializeNumberArrayProperty(const std::string &key,
6517 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006518 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006519 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006520
jrkooncecba5d6c2019-08-29 11:26:22 -05006521 json ary;
6522 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006523 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006524 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006525 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006526 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006527}
6528
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006529static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006530 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006531 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006532}
6533
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006534static void SerializeStringArrayProperty(const std::string &key,
6535 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006536 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006537 json ary;
6538 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006539 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006540 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006541 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006542 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006543}
6544
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006545static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006546 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006547#ifdef TINYGLTF_USE_RAPIDJSON
6548 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006549 case REAL_TYPE:
6550 obj.SetDouble(value.Get<double>());
6551 break;
6552 case INT_TYPE:
6553 obj.SetInt(value.Get<int>());
6554 break;
6555 case BOOL_TYPE:
6556 obj.SetBool(value.Get<bool>());
6557 break;
6558 case STRING_TYPE:
6559 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6560 break;
6561 case ARRAY_TYPE: {
6562 obj.SetArray();
6563 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6564 GetAllocator());
6565 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6566 Value elementValue = value.Get(int(i));
6567 json elementJson;
6568 if (ValueToJson(value.Get(int(i)), &elementJson))
6569 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006570 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006571 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006572 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006573 case BINARY_TYPE:
6574 // TODO
6575 // obj = json(value.Get<std::vector<unsigned char>>());
6576 return false;
6577 break;
6578 case OBJECT_TYPE: {
6579 obj.SetObject();
6580 Value::Object objMap = value.Get<Value::Object>();
6581 for (auto &it : objMap) {
6582 json elementJson;
6583 if (ValueToJson(it.second, &elementJson)) {
6584 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6585 std::move(elementJson), GetAllocator());
6586 }
6587 }
6588 break;
6589 }
6590 case NULL_TYPE:
6591 default:
6592 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006593 }
6594#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006595 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006596 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006597 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006598 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006599 case INT_TYPE:
6600 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006601 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006602 case BOOL_TYPE:
6603 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006604 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006605 case STRING_TYPE:
6606 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006607 break;
6608 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006609 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6610 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006611 json elementJson;
6612 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006613 obj.push_back(elementJson);
6614 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006615 break;
6616 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006617 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006618 // TODO
6619 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006620 return false;
6621 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006622 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006623 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006624 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006625 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006626 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006627 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006628 break;
6629 }
6630 case NULL_TYPE:
6631 default:
6632 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006633 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006634#endif
6635 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006636 return true;
6637}
6638
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006639static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006640 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006641 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006642 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006643 JsonAddMember(obj, key.c_str(), std::move(ret));
6644 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006645}
6646
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006647static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006648 json &o) {
6649 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006650 if (data.size() > 0) {
6651 std::string encodedData =
6652 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6653 SerializeStringProperty("uri", header + encodedData, o);
6654 } else {
6655 // Issue #229
imallettd9ce9eb2022-10-07 10:37:09 -07006656 // size 0 is allowed. Just emit mime header.
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006657 SerializeStringProperty("uri", header, o);
6658 }
johan bowald30c53472018-03-30 11:49:36 +02006659}
6660
Selmar Koke4677492018-10-25 16:45:49 +02006661static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006662 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006663#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006664#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006665 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6666 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6667 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6668 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006669 std::ostream output(&wfile_buf);
6670 if (!wfile_buf.is_open()) return false;
6671#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006672 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006673 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006674#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006675 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006676 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006677#endif
6678#else
6679 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6680 if (!output.is_open()) return false;
6681#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006682 if (data.size() > 0) {
6683 output.write(reinterpret_cast<const char *>(&data[0]),
6684 std::streamsize(data.size()));
6685 } else {
6686 // Issue #229
6687 // size 0 will be still valid buffer data.
6688 // write empty file.
6689 }
Selmar Koke4677492018-10-25 16:45:49 +02006690 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006691}
6692
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006693#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006694static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006695 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6696 ++paramIt) {
6697 if (paramIt->second.number_array.size()) {
6698 SerializeNumberArrayProperty<double>(paramIt->first,
6699 paramIt->second.number_array, o);
6700 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006701 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006702 for (std::map<std::string, double>::iterator it =
6703 paramIt->second.json_double_value.begin();
6704 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006705 if (it->first == "index") {
6706 json_double_value[it->first] = paramIt->second.TextureIndex();
6707 } else {
6708 json_double_value[it->first] = it->second;
6709 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006710 }
6711
Syoyo Fujita83675312017-12-02 21:14:13 +09006712 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006713 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006714 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006715 } else if (paramIt->second.has_number_value) {
6716 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006717 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006718 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006719 }
6720 }
6721}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006722#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006723
Selmar Kok81b672b2019-10-18 16:08:44 +02006724static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006725 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006726
6727 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006728 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006729 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006730 // Allow an empty object for extension(#97)
6731 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006732 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006733 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006734 isNull = JsonIsNull(ret);
6735 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006736 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006737 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006738 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6739 // create empty object so that an extension name is still included in
6740 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006741 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006742 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006743 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006744 }
6745 }
Selmar09d2ff12018-03-15 17:30:42 +01006746 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006747 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006748}
6749
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006750static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006751 if (accessor.bufferView >= 0)
6752 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006753
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006754 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006755 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006756
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006757 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6758 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006759
6760 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6761 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6762 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6763 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6764 } else {
6765 // Issue #301. Serialize as integer.
6766 // Assume int value is within [-2**31-1, 2**31-1]
6767 {
6768 std::vector<int> values;
6769 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6770 std::back_inserter(values),
6771 [](double v) { return static_cast<int>(v); });
6772
6773 SerializeNumberArrayProperty<int>("min", values, o);
6774 }
6775
6776 {
6777 std::vector<int> values;
6778 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6779 std::back_inserter(values),
6780 [](double v) { return static_cast<int>(v); });
6781
6782 SerializeNumberArrayProperty<int>("max", values, o);
6783 }
6784 }
6785
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006786 if (accessor.normalized)
6787 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006788 std::string type;
6789 switch (accessor.type) {
6790 case TINYGLTF_TYPE_SCALAR:
6791 type = "SCALAR";
6792 break;
6793 case TINYGLTF_TYPE_VEC2:
6794 type = "VEC2";
6795 break;
6796 case TINYGLTF_TYPE_VEC3:
6797 type = "VEC3";
6798 break;
6799 case TINYGLTF_TYPE_VEC4:
6800 type = "VEC4";
6801 break;
6802 case TINYGLTF_TYPE_MAT2:
6803 type = "MAT2";
6804 break;
6805 case TINYGLTF_TYPE_MAT3:
6806 type = "MAT3";
6807 break;
6808 case TINYGLTF_TYPE_MAT4:
6809 type = "MAT4";
6810 break;
6811 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006812
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006813 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006814 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006815
6816 if (accessor.extras.Type() != NULL_TYPE) {
6817 SerializeValue("extras", accessor.extras, o);
6818 }
feiy0b315432022-08-13 10:08:17 +08006819
6820 // sparse
6821 if (accessor.sparse.isSparse)
6822 {
6823 json sparse;
6824 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
6825 {
6826 json indices;
6827 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
6828 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
6829 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
6830 JsonAddMember(sparse, "indices", std::move(indices));
6831 }
6832 {
6833 json values;
6834 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08006835 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
feiy0b315432022-08-13 10:08:17 +08006836 JsonAddMember(sparse, "values", std::move(values));
6837 }
6838 JsonAddMember(o, "sparse", std::move(sparse));
6839 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006840}
6841
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006842static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006843 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006844 {
6845 json target;
6846 SerializeNumberProperty("node", channel.target_node, target);
6847 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006848
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006849 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006850
jrkooncecba5d6c2019-08-29 11:26:22 -05006851 JsonAddMember(o, "target", std::move(target));
6852 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006853
6854 if (channel.extras.Type() != NULL_TYPE) {
6855 SerializeValue("extras", channel.extras, o);
6856 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006857
Selmar Kok4e2988e2019-08-16 14:08:08 +02006858 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006859}
6860
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006861static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006862 SerializeNumberProperty("input", sampler.input, o);
6863 SerializeNumberProperty("output", sampler.output, o);
6864 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006865
6866 if (sampler.extras.Type() != NULL_TYPE) {
6867 SerializeValue("extras", sampler.extras, o);
6868 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006869}
6870
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006871static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006872 if (!animation.name.empty())
6873 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006874
jrkooncecba5d6c2019-08-29 11:26:22 -05006875 {
6876 json channels;
6877 JsonReserveArray(channels, animation.channels.size());
6878 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6879 json channel;
6880 AnimationChannel gltfChannel = animation.channels[i];
6881 SerializeGltfAnimationChannel(gltfChannel, channel);
6882 JsonPushBack(channels, std::move(channel));
6883 }
6884
6885 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006886 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006887
jrkooncecba5d6c2019-08-29 11:26:22 -05006888 {
6889 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006890 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006891 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6892 json sampler;
6893 AnimationSampler gltfSampler = animation.samplers[i];
6894 SerializeGltfAnimationSampler(gltfSampler, sampler);
6895 JsonPushBack(samplers, std::move(sampler));
6896 }
6897 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006898 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006899
Jens Olssonb3af2f12018-06-04 10:17:49 +02006900 if (animation.extras.Type() != NULL_TYPE) {
6901 SerializeValue("extras", animation.extras, o);
6902 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006903
Selmar Kok4e2988e2019-08-16 14:08:08 +02006904 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006905}
6906
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006907static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006908 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006909 SerializeStringProperty("generator", asset.generator, o);
6910 }
6911
Christophe820ede82019-07-04 15:21:21 +09006912 if (!asset.copyright.empty()) {
6913 SerializeStringProperty("copyright", asset.copyright, o);
6914 }
6915
Syoyo Fujitab702de72021-03-02 19:08:29 +09006916 if (asset.version.empty()) {
6917 // Just in case
6918 // `version` must be defined
6919 asset.version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006920 }
6921
Syoyo Fujitab702de72021-03-02 19:08:29 +09006922 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6923 SerializeStringProperty("version", asset.version, o);
6924
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006925 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006926 SerializeValue("extras", asset.extras, o);
6927 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006928
Selmar09d2ff12018-03-15 17:30:42 +01006929 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006930}
6931
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006932static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6933 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006934 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006935 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006936
6937 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6938
6939 if (buffer.extras.Type() != NULL_TYPE) {
6940 SerializeValue("extras", buffer.extras, o);
6941 }
6942}
6943
johan bowald30c53472018-03-30 11:49:36 +02006944static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6945 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6946 SerializeGltfBufferData(buffer.data, o);
6947
6948 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006949
6950 if (buffer.extras.Type() != NULL_TYPE) {
6951 SerializeValue("extras", buffer.extras, o);
6952 }
johan bowald30c53472018-03-30 11:49:36 +02006953}
6954
Selmar Koke4677492018-10-25 16:45:49 +02006955static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006956 const std::string &binFilename,
6957 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006958 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006959 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006960 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006961
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006962 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006963
6964 if (buffer.extras.Type() != NULL_TYPE) {
6965 SerializeValue("extras", buffer.extras, o);
6966 }
Selmar Koke4677492018-10-25 16:45:49 +02006967 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006968}
6969
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006970static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006971 SerializeNumberProperty("buffer", bufferView.buffer, o);
6972 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006973
Johan Bowaldfaa27222018-03-28 14:44:45 +02006974 // byteStride is optional, minimum allowed is 4
6975 if (bufferView.byteStride >= 4) {
6976 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6977 }
6978 // byteOffset is optional, default is 0
6979 if (bufferView.byteOffset > 0) {
6980 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6981 }
6982 // Target is optional, check if it contains a valid value
6983 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6984 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6985 SerializeNumberProperty("target", bufferView.target, o);
6986 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006987 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006988 SerializeStringProperty("name", bufferView.name, o);
6989 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006990
6991 if (bufferView.extras.Type() != NULL_TYPE) {
6992 SerializeValue("extras", bufferView.extras, o);
6993 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006994}
6995
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006996static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006997 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08006998 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006999 SerializeStringProperty("mimeType", image.mimeType, o);
7000 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7001 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007002 // TODO(syoyo): dlib::urilencode?
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007003 SerializeStringProperty("uri", image.uri, o);
7004 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007005
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007006 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007007 SerializeStringProperty("name", image.name, o);
7008 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007009
7010 if (image.extras.Type() != NULL_TYPE) {
7011 SerializeValue("extras", image.extras, o);
7012 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09007013
7014 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007015}
7016
Syoyo Fujita046400b2019-07-24 19:26:48 +09007017static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
7018 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007019
Syoyo Fujita046400b2019-07-24 19:26:48 +09007020 if (texinfo.texCoord != 0) {
7021 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7022 }
7023
7024 if (texinfo.extras.Type() != NULL_TYPE) {
7025 SerializeValue("extras", texinfo.extras, o);
7026 }
7027
7028 SerializeExtensionMap(texinfo.extensions, o);
7029}
7030
7031static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
7032 json &o) {
7033 SerializeNumberProperty("index", texinfo.index, o);
7034
7035 if (texinfo.texCoord != 0) {
7036 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7037 }
7038
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007039 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007040 SerializeNumberProperty("scale", texinfo.scale, o);
7041 }
7042
7043 if (texinfo.extras.Type() != NULL_TYPE) {
7044 SerializeValue("extras", texinfo.extras, o);
7045 }
7046
7047 SerializeExtensionMap(texinfo.extensions, o);
7048}
7049
7050static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
7051 json &o) {
7052 SerializeNumberProperty("index", texinfo.index, o);
7053
7054 if (texinfo.texCoord != 0) {
7055 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7056 }
7057
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007058 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007059 SerializeNumberProperty("strength", texinfo.strength, o);
7060 }
7061
7062 if (texinfo.extras.Type() != NULL_TYPE) {
7063 SerializeValue("extras", texinfo.extras, o);
7064 }
7065
7066 SerializeExtensionMap(texinfo.extensions, o);
7067}
7068
7069static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
7070 json &o) {
7071 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7072 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7073 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7074 o);
7075 }
7076
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007077 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007078 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7079 }
7080
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007081 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007082 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7083 }
7084
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007085 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007086 json texinfo;
7087 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007088 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007089 }
7090
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007091 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007092 json texinfo;
7093 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007094 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007095 }
7096
7097 SerializeExtensionMap(pbr.extensions, o);
7098
7099 if (pbr.extras.Type() != NULL_TYPE) {
7100 SerializeValue("extras", pbr.extras, o);
7101 }
7102}
7103
7104static void SerializeGltfMaterial(Material &material, json &o) {
7105 if (material.name.size()) {
7106 SerializeStringProperty("name", material.name, o);
7107 }
7108
7109 // QUESTION(syoyo): Write material parameters regardless of its default value?
7110
7111 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7112 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7113 }
7114
Patrick Härtld9a468b2019-08-14 14:14:07 +02007115 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007116 SerializeStringProperty("alphaMode", material.alphaMode, o);
7117 }
7118
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007119 if (material.doubleSided != false)
7120 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007121
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007122 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007123 json texinfo;
7124 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007125 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007126 }
7127
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007128 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007129 json texinfo;
7130 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007131 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007132 }
7133
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007134 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007135 json texinfo;
7136 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007137 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007138 }
7139
7140 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7141 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7142 SerializeNumberArrayProperty<double>("emissiveFactor",
7143 material.emissiveFactor, o);
7144 }
7145
7146 {
7147 json pbrMetallicRoughness;
7148 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7149 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007150 // Issue 204
7151 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7152 // default values(json is null). Otherwise it will serialize to
7153 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
imallettd9ce9eb2022-10-07 10:37:09 -07007154 // importers (and validators).
Syoyo Fujita7e009042019-09-13 15:32:22 +09007155 //
7156 if (!JsonIsNull(pbrMetallicRoughness)) {
7157 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7158 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007159 }
7160
7161#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007162 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007163 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007164 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007165 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007166 }
7167
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007168 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007169#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007170
Syoyo Fujita046400b2019-07-24 19:26:48 +09007171#endif
7172
7173 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007174
7175 if (material.extras.Type() != NULL_TYPE) {
7176 SerializeValue("extras", material.extras, o);
7177 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007178}
7179
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007180static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007181 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007182 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007183 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007184 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007185 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007186 {
7187 json attributes;
7188 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007189 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007190 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7191 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007192
jrkooncecba5d6c2019-08-29 11:26:22 -05007193 JsonAddMember(primitive, "attributes", std::move(attributes));
7194 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007195
imallettd9ce9eb2022-10-07 10:37:09 -07007196 // Indices is optional
Johan Bowaldfaa27222018-03-28 14:44:45 +02007197 if (gltfPrimitive.indices > -1) {
7198 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7199 }
7200 // Material is optional
7201 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007202 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7203 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007204 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007205 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007206
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007207 // Morph targets
7208 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007209 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007210 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007211 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007212 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007213 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7214 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7215 attrIt != targetData.end(); ++attrIt) {
7216 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7217 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007218 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007219 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007220 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007221 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007222 }
7223
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007224 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007225
Jens Olssonb3af2f12018-06-04 10:17:49 +02007226 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7227 SerializeValue("extras", gltfPrimitive.extras, primitive);
7228 }
7229
jrkooncecba5d6c2019-08-29 11:26:22 -05007230 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007231 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007232
jrkooncecba5d6c2019-08-29 11:26:22 -05007233 JsonAddMember(o, "primitives", std::move(primitives));
7234
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007235 if (mesh.weights.size()) {
7236 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7237 }
7238
7239 if (mesh.name.size()) {
7240 SerializeStringProperty("name", mesh.name, o);
7241 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007242
Selmar Kok81b672b2019-10-18 16:08:44 +02007243 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007244 if (mesh.extras.Type() != NULL_TYPE) {
7245 SerializeValue("extras", mesh.extras, o);
7246 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007247}
7248
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007249static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007250 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7251 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7252 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007253 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007254 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007255 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007256}
7257
Syoyo Fujita83675312017-12-02 21:14:13 +09007258static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007259 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007260 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007261 if (light.range > 0.0) {
7262 SerializeNumberProperty("range", light.range, o);
7263 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007264 SerializeNumberArrayProperty("color", light.color, o);
7265 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007266 if (light.type == "spot") {
7267 json spot;
7268 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007269 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007270 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007271 SerializeExtensionMap(light.extensions, o);
7272 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007273 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007274 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007275}
7276
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007277static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007278 if (node.translation.size() > 0) {
7279 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7280 }
7281 if (node.rotation.size() > 0) {
7282 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7283 }
7284 if (node.scale.size() > 0) {
7285 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7286 }
7287 if (node.matrix.size() > 0) {
7288 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7289 }
7290 if (node.mesh != -1) {
7291 SerializeNumberProperty<int>("mesh", node.mesh, o);
7292 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007293
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007294 if (node.skin != -1) {
7295 SerializeNumberProperty<int>("skin", node.skin, o);
7296 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007297
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007298 if (node.camera != -1) {
7299 SerializeNumberProperty<int>("camera", node.camera, o);
7300 }
7301
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007302 if (node.weights.size() > 0) {
7303 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7304 }
7305
Jens Olssona9718662018-05-24 15:48:49 +02007306 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007307 SerializeValue("extras", node.extras, o);
7308 }
7309
Selmar09d2ff12018-03-15 17:30:42 +01007310 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007311 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007312 SerializeNumberArrayProperty<int>("children", node.children, o);
7313}
7314
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007315static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007316 if (sampler.magFilter != -1) {
7317 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7318 }
7319 if (sampler.minFilter != -1) {
7320 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7321 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007322 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007323 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7324 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007325
7326 if (sampler.extras.Type() != NULL_TYPE) {
7327 SerializeValue("extras", sampler.extras, o);
7328 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007329}
7330
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007331static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007332 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007333 SerializeNumberProperty("zfar", camera.zfar, o);
7334 SerializeNumberProperty("znear", camera.znear, o);
7335 SerializeNumberProperty("xmag", camera.xmag, o);
7336 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007337
7338 if (camera.extras.Type() != NULL_TYPE) {
7339 SerializeValue("extras", camera.extras, o);
7340 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007341}
7342
7343static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007344 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007345 SerializeNumberProperty("zfar", camera.zfar, o);
7346 SerializeNumberProperty("znear", camera.znear, o);
7347 if (camera.aspectRatio > 0) {
7348 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7349 }
7350
7351 if (camera.yfov > 0) {
7352 SerializeNumberProperty("yfov", camera.yfov, o);
7353 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007354
7355 if (camera.extras.Type() != NULL_TYPE) {
7356 SerializeValue("extras", camera.extras, o);
7357 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007358}
7359
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007360static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007361 SerializeStringProperty("type", camera.type, o);
7362 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007363 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007364 }
7365
7366 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007367 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007368 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007369 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007370 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007371 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007372 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007373 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007374 } else {
7375 // ???
7376 }
Syoyofe77cc52020-05-09 02:41:07 +09007377
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007378 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007379 SerializeValue("extras", camera.extras, o);
7380 }
7381 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007382}
7383
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007384static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007385 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7386
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007387 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007388 SerializeStringProperty("name", scene.name, o);
7389 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007390 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007391 SerializeValue("extras", scene.extras, o);
7392 }
7393 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007394}
7395
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007396static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007397 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007398 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007399
7400 if (skin.inverseBindMatrices >= 0) {
7401 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7402 }
7403
7404 if (skin.skeleton >= 0) {
7405 SerializeNumberProperty("skeleton", skin.skeleton, o);
7406 }
7407
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007408 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007409 SerializeStringProperty("name", skin.name, o);
7410 }
7411}
7412
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007413static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007414 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007415 SerializeNumberProperty("sampler", texture.sampler, o);
7416 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007417 if (texture.source > -1) {
7418 SerializeNumberProperty("source", texture.source, o);
7419 }
Christophe820ede82019-07-04 15:21:21 +09007420 if (texture.name.size()) {
7421 SerializeStringProperty("name", texture.name, o);
7422 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007423 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007424 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007425 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007426 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007427}
7428
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007429///
7430/// Serialize all properties except buffers and images.
7431///
7432static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007433 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007434 if (model->accessors.size()) {
7435 json accessors;
7436 JsonReserveArray(accessors, model->accessors.size());
7437 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7438 json accessor;
7439 SerializeGltfAccessor(model->accessors[i], accessor);
7440 JsonPushBack(accessors, std::move(accessor));
7441 }
7442 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007443 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007444
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007445 // ANIMATIONS
7446 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007447 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007448 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007449 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7450 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007451 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007452 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007453 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007454 }
7455 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007456
jrkooncecba5d6c2019-08-29 11:26:22 -05007457 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007458 }
7459
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007460 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007461 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007462 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007463 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007464
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007465 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007466 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007467 json bufferViews;
7468 JsonReserveArray(bufferViews, model->bufferViews.size());
7469 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7470 json bufferView;
7471 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7472 JsonPushBack(bufferViews, std::move(bufferView));
7473 }
7474 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007475 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007476
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007477 // Extensions required
7478 if (model->extensionsRequired.size()) {
7479 SerializeStringArrayProperty("extensionsRequired",
7480 model->extensionsRequired, o);
7481 }
7482
7483 // MATERIALS
7484 if (model->materials.size()) {
7485 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007486 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007487 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7488 json material;
7489 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007490
7491 if (JsonIsNull(material)) {
7492 // Issue 294.
7493 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007494 // so the result may be null(unmodified) when all material parameters
7495 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007496 //
7497 // null is not allowed thus we create an empty JSON object.
7498 JsonSetObject(material);
7499 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007500 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007501 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007502 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007503 }
7504
7505 // MESHES
7506 if (model->meshes.size()) {
7507 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007508 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007509 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7510 json mesh;
7511 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007512 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007513 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007514 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007515 }
7516
7517 // NODES
7518 if (model->nodes.size()) {
7519 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007520 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007521 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7522 json node;
7523 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007524 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007525 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007526 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007527 }
7528
7529 // SCENE
7530 if (model->defaultScene > -1) {
7531 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7532 }
7533
7534 // SCENES
7535 if (model->scenes.size()) {
7536 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007537 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007538 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7539 json currentScene;
7540 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007541 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007542 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007543 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007544 }
7545
7546 // SKINS
7547 if (model->skins.size()) {
7548 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007549 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007550 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7551 json skin;
7552 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007553 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007554 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007555 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007556 }
7557
7558 // TEXTURES
7559 if (model->textures.size()) {
7560 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007561 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007562 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7563 json texture;
7564 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007565 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007566 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007567 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007568 }
7569
7570 // SAMPLERS
7571 if (model->samplers.size()) {
7572 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007573 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007574 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7575 json sampler;
7576 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007577 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007578 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007579 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007580 }
7581
7582 // CAMERAS
7583 if (model->cameras.size()) {
7584 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007585 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007586 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7587 json camera;
7588 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007589 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007590 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007591 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007592 }
7593
7594 // EXTENSIONS
7595 SerializeExtensionMap(model->extensions, o);
7596
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007597 auto extensionsUsed = model->extensionsUsed;
7598
7599 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007600 if (model->lights.size()) {
7601 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007602 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007603 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7604 json light;
7605 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007606 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007607 }
7608 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007609 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007610 json ext_j;
7611
jrkooncecba5d6c2019-08-29 11:26:22 -05007612 {
7613 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007614 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007615 JsonAssign(ext_j, GetValue(it));
7616 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007617 }
7618
jrkooncecba5d6c2019-08-29 11:26:22 -05007619 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007620
jrkooncecba5d6c2019-08-29 11:26:22 -05007621 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007622
7623 // Also add "KHR_lights_punctual" to `extensionsUsed`
7624 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007625 auto has_khr_lights_punctual =
7626 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7627 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007628 return (s.compare("KHR_lights_punctual") == 0);
7629 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007630
7631 if (has_khr_lights_punctual == extensionsUsed.end()) {
7632 extensionsUsed.push_back("KHR_lights_punctual");
7633 }
7634 }
7635 }
7636
7637 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007638 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007639 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007640 }
7641
7642 // EXTRAS
7643 if (model->extras.Type() != NULL_TYPE) {
7644 SerializeValue("extras", model->extras, o);
7645 }
7646}
7647
Johan Bowald52936a02019-07-17 09:06:45 +02007648static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007649 stream << content << std::endl;
7650 return true;
7651}
7652
7653static bool WriteGltfFile(const std::string &output,
7654 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007655#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007656#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007657 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007658#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007659 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7660 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7661 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7662 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007663 std::ostream gltfFile(&wfile_buf);
7664 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007665#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007666 std::ofstream gltfFile(output.c_str());
7667 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007668#endif
7669#else
7670 std::ofstream gltfFile(output.c_str());
7671 if (!gltfFile.is_open()) return false;
7672#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007673 return WriteGltfStream(gltfFile, content);
7674}
7675
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007676static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007677 const std::string &content,
7678 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007679 const std::string header = "glTF";
7680 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007681
Alexander Wood190382a2021-10-08 12:19:13 -04007682 const uint32_t content_size = uint32_t(content.size());
7683 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7684 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007685 const uint32_t content_padding_size =
7686 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7687 const uint32_t bin_padding_size =
7688 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007689
7690 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007691 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007692 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007693 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007694 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007695
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007696 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007697 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7698 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7699
7700 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007701 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007702 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007703 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007704 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007705 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007706 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007707 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007708
7709 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007710 if (content_padding_size > 0) {
7711 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007712 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007713 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007714 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007715 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007716 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7717 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007718 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007719 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007720 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007721 sizeof(bin_format));
7722 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7723 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007724 // Chunksize must be multiplies of 4, so pad with zeroes
7725 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007726 const std::vector<unsigned char> padding =
7727 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7728 stream.write(reinterpret_cast<const char *>(padding.data()),
7729 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007730 }
7731 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007732
7733 // TODO: Check error on stream.write
7734 return true;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007735}
7736
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007737static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007738 const std::string &content,
7739 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007740#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007741#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007742 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007743#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007744 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7745 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7746 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7747 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007748 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007749#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007750 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007751#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007752#else
7753 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007754#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007755 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007756}
7757
7758bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7759 bool prettyPrint = true,
7760 bool writeBinary = false) {
7761 JsonDocument output;
7762
7763 /// Serialize all properties except buffers and images.
7764 SerializeGltfModel(model, output);
7765
7766 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007767 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007768 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007769 json buffers;
7770 JsonReserveArray(buffers, model->buffers.size());
7771 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7772 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007773 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7774 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007775 } else {
7776 SerializeGltfBuffer(model->buffers[i], buffer);
7777 }
7778 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007779 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007780 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007781 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007782
7783 // IMAGES
7784 if (model->images.size()) {
7785 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007786 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007787 for (unsigned int i = 0; i < model->images.size(); ++i) {
7788 json image;
7789
7790 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007791 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7792 // enabled, since we won't write separate images when writing to a stream
7793 // we
Hendrik Schwanekamp41e11022022-06-29 12:22:40 +02007794 UpdateImageObject(model->images[i], dummystring, int(i), true,
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007795 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007796 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007797 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007798 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007799 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007800 }
7801
7802 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007803 return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007804 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007805 return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007806 }
7807
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007808}
7809
7810bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7811 bool embedImages = false,
7812 bool embedBuffers = false,
7813 bool prettyPrint = true,
7814 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007815 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007816 std::string defaultBinFilename = GetBaseFilename(filename);
7817 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007818 std::string::size_type pos =
7819 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007820
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007821 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007822 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007823 }
johan bowald642a3432018-04-01 12:37:18 +02007824 std::string baseDir = GetBaseDir(filename);
7825 if (baseDir.empty()) {
7826 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007827 }
Johan Bowald52936a02019-07-17 09:06:45 +02007828 /// Serialize all properties except buffers and images.
7829 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007830
Selmar Kok7cb31e42018-10-05 16:02:29 +02007831 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007832 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007833 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007834 if (model->buffers.size()) {
7835 json buffers;
7836 JsonReserveArray(buffers, model->buffers.size());
7837 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7838 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007839 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7840 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007841 } else if (embedBuffers) {
7842 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007843 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007844 std::string binSavePath;
7845 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007846 if (!model->buffers[i].uri.empty() &&
7847 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007848 binUri = model->buffers[i].uri;
7849 } else {
7850 binUri = defaultBinFilename + defaultBinFileExt;
7851 bool inUse = true;
7852 int numUsed = 0;
7853 while (inUse) {
7854 inUse = false;
7855 for (const std::string &usedName : usedUris) {
7856 if (binUri.compare(usedName) != 0) continue;
7857 inUse = true;
7858 binUri = defaultBinFilename + std::to_string(numUsed++) +
7859 defaultBinFileExt;
7860 break;
7861 }
Selmar Kokc884e582018-10-05 16:25:54 +02007862 }
7863 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007864 usedUris.push_back(binUri);
7865 binSavePath = JoinPath(baseDir, binUri);
7866 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7867 binUri)) {
7868 return false;
7869 }
Selmar Kokc884e582018-10-05 16:25:54 +02007870 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007871 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007872 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007873 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007874 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007875
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007876 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007877 if (model->images.size()) {
7878 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007879 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007880 for (unsigned int i = 0; i < model->images.size(); ++i) {
7881 json image;
johan bowald642a3432018-04-01 12:37:18 +02007882
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007883 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007884 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007885 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007886 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007887 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007888 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007889 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007890
David Harmonda9eac22018-08-30 08:06:05 -04007891 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007892 return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007893 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007894 return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007895 }
7896
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007897}
7898
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007899} // namespace tinygltf
7900
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007901#ifdef __clang__
7902#pragma clang diagnostic pop
7903#endif
7904
Syoyo Fujita612e5782022-09-18 21:01:39 +09007905#endif // TINYGLTF_IMPLEMENTATION