blob: b83daa17fe21d82f01c394f424adb5905e43e893 [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 Fujita584f1df2022-12-29 21:05:53 +090029// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393.
Syoyo Fujitae0b62552022-09-19 03:36:58 +090030// - 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 +090031// - v2.6.2 Fix out-of-bounds access of accessors. PR#379.
Syoyo Fujita4581d372022-09-06 22:02:31 +090032// - v2.6.1 Better GLB validation check when loading.
Syoyo Fujita3bddc092022-08-19 18:21:24 +090033// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
34// Disable expanding file path for security(no use of awkward `wordexp` anymore).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +090035// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
imallettd9ce9eb2022-10-07 10:37:09 -070036// - v2.4.3 Fix null object output when material has all default
Syoyo Fujita7905a5b2021-01-21 20:33:11 +090037// parameters.
Syoyo Fujitac4166e42020-01-08 02:38:01 +090038// - v2.4.2 Decode percent-encoded URI.
Syoyo Fujita6e08b172019-10-30 17:25:38 +090039// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
40// `extras` property.
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +090041// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
Syoyo Fujitaee179b22019-08-16 13:11:30 +090042// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
Syoyo Fujita046400b2019-07-24 19:26:48 +090043// - v2.3.0 Modified Material representation according to glTF 2.0 schema
44// (and introduced TextureInfo class)
Syoyo Fujita150f2432019-07-25 19:22:44 +090045// Change the behavior of `Value::IsNumber`. It return true either the
46// value is int or real.
Syoyo Fujitaca56f722019-03-07 21:04:25 +090047// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
48// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090049// - v2.1.0 Add draco compression.
imallettd9ce9eb2022-10-07 10:37:09 -070050// - v2.0.1 Add comparison feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090051// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090052//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090053// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090054//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090055// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090056// - base64: base64 decode/encode library.
57// - stb_image: Image loading library.
58//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090059#ifndef TINY_GLTF_H_
60#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090061
Syoyo Fujitad42767e2018-03-15 21:52:00 -050062#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090063#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090064#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050065#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020066#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090067#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090068#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090069#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090070#include <string>
71#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090072
zbendefy69eeea12022-09-05 23:54:57 +020073//Auto-detect C++14 standard version
74#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
75#define TINYGLTF_USE_CPP14
76#endif
77
jrkoonce51453942019-09-03 09:48:30 -050078#ifndef TINYGLTF_USE_CPP14
79#include <functional>
80#endif
81
Sascha Willems5f9cb242018-12-28 20:53:41 +010082#ifdef __ANDROID__
83#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
84#include <android/asset_manager.h>
85#endif
86#endif
87
Selmar Kok79e3df22019-10-29 16:22:07 +010088#ifdef __GNUC__
89#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010090#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010091#else
92#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010093#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010094#else
95#define TINYGLTF_NOEXCEPT noexcept
96#endif
97
Syoyo Fujita6e08b172019-10-30 17:25:38 +090098#define DEFAULT_METHODS(x) \
99 ~x() = default; \
100 x(const x &) = default; \
101 x(x &&) TINYGLTF_NOEXCEPT = default; \
102 x &operator=(const x &) = default; \
103 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100104
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900105namespace tinygltf {
106
107#define TINYGLTF_MODE_POINTS (0)
108#define TINYGLTF_MODE_LINE (1)
109#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +0100110#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900111#define TINYGLTF_MODE_TRIANGLES (4)
112#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
113#define TINYGLTF_MODE_TRIANGLE_FAN (6)
114
115#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
116#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
117#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
118#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
119#define TINYGLTF_COMPONENT_TYPE_INT (5124)
120#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
121#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900122#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
123 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
124 // support double type even the schema seems allow any value of
125 // integer:
126 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900127
Syoyo Fujitac2615632016-06-19 21:56:06 +0900128#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
129#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
130#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
131#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
132#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
133#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
134
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100135#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900136#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400137#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900138
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400139// Redeclarations of the above for technique.parameters.
140#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
141#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
142#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
143#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
144#define TINYGLTF_PARAMETER_TYPE_INT (5124)
145#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
146#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
147
148#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
149#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
150#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
151
152#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
153#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
154#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
155
156#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
157#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
158#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
159#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
160
161#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
162#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
163#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
164
165#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
166
167// End parameter types
168
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900169#define TINYGLTF_TYPE_VEC2 (2)
170#define TINYGLTF_TYPE_VEC3 (3)
171#define TINYGLTF_TYPE_VEC4 (4)
172#define TINYGLTF_TYPE_MAT2 (32 + 2)
173#define TINYGLTF_TYPE_MAT3 (32 + 3)
174#define TINYGLTF_TYPE_MAT4 (32 + 4)
175#define TINYGLTF_TYPE_SCALAR (64 + 1)
176#define TINYGLTF_TYPE_VECTOR (64 + 4)
177#define TINYGLTF_TYPE_MATRIX (64 + 16)
178
179#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
180#define TINYGLTF_IMAGE_FORMAT_PNG (1)
181#define TINYGLTF_IMAGE_FORMAT_BMP (2)
182#define TINYGLTF_IMAGE_FORMAT_GIF (3)
183
Luke San Antonio6d616f52016-06-23 14:09:23 -0400184#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
185#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900186#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400187#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
188#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
189
Syoyo Fujitabde70212016-02-07 17:38:17 +0900190#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
191#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
192
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900193#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
194#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
195
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400196#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
197#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
198
Selmar Kok31cb7f92018-10-03 15:39:05 +0200199#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900200#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200201
Sascha Willems5f9cb242018-12-28 20:53:41 +0100202#ifdef __ANDROID__
203#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000204#ifdef TINYGLTF_IMPLEMENTATION
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000205AAssetManager *asset_manager = nullptr;
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000206#else
207extern AAssetManager *asset_manager;
208#endif
Sascha Willems5f9cb242018-12-28 20:53:41 +0100209#endif
210#endif
211
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900212typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100213 NULL_TYPE,
214 REAL_TYPE,
215 INT_TYPE,
216 BOOL_TYPE,
217 STRING_TYPE,
218 ARRAY_TYPE,
219 BINARY_TYPE,
220 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900221} Type;
222
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500223static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900224 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
225 return 1;
226 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
227 return 1;
228 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
229 return 2;
230 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
231 return 2;
232 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
233 return 4;
234 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
235 return 4;
236 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
237 return 4;
238 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
239 return 8;
240 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700241 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900242 return -1;
243 }
244}
245
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400246static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900247 if (ty == TINYGLTF_TYPE_SCALAR) {
248 return 1;
249 } else if (ty == TINYGLTF_TYPE_VEC2) {
250 return 2;
251 } else if (ty == TINYGLTF_TYPE_VEC3) {
252 return 3;
253 } else if (ty == TINYGLTF_TYPE_VEC4) {
254 return 4;
255 } else if (ty == TINYGLTF_TYPE_MAT2) {
256 return 4;
257 } else if (ty == TINYGLTF_TYPE_MAT3) {
258 return 9;
259 } else if (ty == TINYGLTF_TYPE_MAT4) {
260 return 16;
261 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700262 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900263 return -1;
264 }
265}
266
Syoyo Fujita150f2432019-07-25 19:22:44 +0900267// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200268bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900269bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
270 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200271
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900272#ifdef __clang__
273#pragma clang diagnostic push
274// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900275#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900276#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900277#endif
278
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900279// Simple class to represent JSON object
280class Value {
281 public:
282 typedef std::vector<Value> Array;
283 typedef std::map<std::string, Value> Object;
284
Syoyo Fujita046400b2019-07-24 19:26:48 +0900285 Value()
286 : type_(NULL_TYPE),
287 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900288 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900289 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900290
291 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900292 explicit Value(int i) : type_(INT_TYPE) {
293 int_value_ = i;
294 real_value_ = i;
295 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900296 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900297 explicit Value(const std::string &s) : type_(STRING_TYPE) {
298 string_value_ = s;
299 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900300 explicit Value(std::string &&s)
301 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900302 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900303 binary_value_.resize(n);
304 memcpy(binary_value_.data(), p, n);
305 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900306 explicit Value(std::vector<unsigned char> &&v) noexcept
307 : type_(BINARY_TYPE),
308 binary_value_(std::move(v)) {}
309 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
310 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
311 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500312
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900313 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
314 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
315 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100316
317 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900318
Hill Mad1e32862021-02-20 22:30:44 -0800319 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900320
321 bool IsBool() const { return (type_ == BOOL_TYPE); }
322
323 bool IsInt() const { return (type_ == INT_TYPE); }
324
Syoyo Fujita150f2432019-07-25 19:22:44 +0900325 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900326
Syoyo Fujita150f2432019-07-25 19:22:44 +0900327 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900328
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900329 bool IsString() const { return (type_ == STRING_TYPE); }
330
331 bool IsBinary() const { return (type_ == BINARY_TYPE); }
332
333 bool IsArray() const { return (type_ == ARRAY_TYPE); }
334
335 bool IsObject() const { return (type_ == OBJECT_TYPE); }
336
Syoyo Fujita150f2432019-07-25 19:22:44 +0900337 // Use this function if you want to have number value as double.
338 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900339 if (type_ == INT_TYPE) {
340 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900341 } else {
342 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900343 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900344 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900345
Syoyo Fujita150f2432019-07-25 19:22:44 +0900346 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900347 // TODO(syoyo): Support int value larger than 32 bits
348 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900349 if (type_ == REAL_TYPE) {
350 return int(real_value_);
351 } else {
352 return int_value_;
353 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900354 }
355
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900356 // Accessor
357 template <typename T>
358 const T &Get() const;
359 template <typename T>
360 T &Get();
361
362 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900363 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900364 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900365 assert(IsArray());
366 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900367 return (static_cast<size_t>(idx) < array_value_.size())
368 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900369 : null_value;
370 }
371
372 // Lookup value from a key-value pair
373 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900374 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900375 assert(IsObject());
376 Object::const_iterator it = object_value_.find(key);
377 return (it != object_value_.end()) ? it->second : null_value;
378 }
379
380 size_t ArrayLen() const {
381 if (!IsArray()) return 0;
382 return array_value_.size();
383 }
384
385 // Valid only for object type.
386 bool Has(const std::string &key) const {
387 if (!IsObject()) return false;
388 Object::const_iterator it = object_value_.find(key);
389 return (it != object_value_.end()) ? true : false;
390 }
391
392 // List keys
393 std::vector<std::string> Keys() const {
394 std::vector<std::string> keys;
395 if (!IsObject()) return keys; // empty
396
397 for (Object::const_iterator it = object_value_.begin();
398 it != object_value_.end(); ++it) {
399 keys.push_back(it->first);
400 }
401
402 return keys;
403 }
404
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900405 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900406
407 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000408
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900409 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900410 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900411
Syoyo Fujita046400b2019-07-24 19:26:48 +0900412 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900413 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900414 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900415 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900416 Array array_value_;
417 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900418 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900419};
420
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900421#ifdef __clang__
422#pragma clang diagnostic pop
423#endif
424
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900425#define TINYGLTF_VALUE_GET(ctype, var) \
426 template <> \
427 inline const ctype &Value::Get<ctype>() const { \
428 return var; \
429 } \
430 template <> \
431 inline ctype &Value::Get<ctype>() { \
432 return var; \
433 }
434TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900435TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900436TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900437TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900438TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900439TINYGLTF_VALUE_GET(Value::Array, array_value_)
440TINYGLTF_VALUE_GET(Value::Object, object_value_)
441#undef TINYGLTF_VALUE_GET
442
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900443#ifdef __clang__
444#pragma clang diagnostic push
445#pragma clang diagnostic ignored "-Wc++98-compat"
446#pragma clang diagnostic ignored "-Wpadded"
447#endif
448
imallettd9ce9eb2022-10-07 10:37:09 -0700449/// Aggregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100450using ColorValue = std::array<double, 4>;
451
Syoyo Fujita046400b2019-07-24 19:26:48 +0900452// === legacy interface ====
453// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500454struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200455 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700456 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900457 std::string string_value;
458 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000459 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200460 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900461
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500462 // context sensitive methods. depending the type of the Parameter you are
463 // accessing, these are either valid or not
464 // If this parameter represent a texture map in a material, will return the
465 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100466
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500467 /// Return the index of a texture if this Parameter is a texture map.
468 /// Returned value is only valid if the parameter represent a texture from a
469 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100470 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100471 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500472 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100473 return int(it->second);
474 }
475 return -1;
476 }
477
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000478 /// Return the index of a texture coordinate set if this Parameter is a
479 /// texture map. Returned value is only valid if the parameter represent a
480 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100481 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000482 const auto it = json_double_value.find("texCoord");
483 if (it != std::end(json_double_value)) {
484 return int(it->second);
485 }
imallettd9ce9eb2022-10-07 10:37:09 -0700486 // As per the spec, if texCoord is omitted, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000487 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100488 }
489
Christophe820ede82019-07-04 15:21:21 +0900490 /// Return the scale of a texture if this Parameter is a normal texture map.
491 /// Returned value is only valid if the parameter represent a normal texture
492 /// from a material
493 double TextureScale() const {
494 const auto it = json_double_value.find("scale");
495 if (it != std::end(json_double_value)) {
496 return it->second;
497 }
imallettd9ce9eb2022-10-07 10:37:09 -0700498 // As per the spec, if scale is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200499 return 1;
500 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200501
Arthur Brainville8a98d982019-07-05 00:26:02 +0200502 /// Return the strength of a texture if this Parameter is a an occlusion map.
503 /// Returned value is only valid if the parameter represent an occlusion map
504 /// from a material
505 double TextureStrength() const {
506 const auto it = json_double_value.find("strength");
507 if (it != std::end(json_double_value)) {
508 return it->second;
509 }
imallettd9ce9eb2022-10-07 10:37:09 -0700510 // As per the spec, if strength is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200511 return 1;
Christophe820ede82019-07-04 15:21:21 +0900512 }
513
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500514 /// Material factor, like the roughness or metalness of a material
515 /// Returned value is only valid if the parameter represent a texture from a
516 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700517 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100518
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500519 /// Return the color of a material
520 /// Returned value is only valid if the parameter represent a texture from a
521 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100522 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100523 return {
imallettd9ce9eb2022-10-07 10:37:09 -0700524 {// this aggregate initialize the std::array object, and uses C++11 RVO.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500525 number_array[0], number_array[1], number_array[2],
526 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100527 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200528
Selmar Kokff2b1f92019-10-21 17:58:09 +0200529 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100530 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900531 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100532};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900533
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900534#ifdef __clang__
535#pragma clang diagnostic pop
536#endif
537
538#ifdef __clang__
539#pragma clang diagnostic push
540#pragma clang diagnostic ignored "-Wpadded"
541#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900542
Syoyo Fujitabde70212016-02-07 17:38:17 +0900543typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200544typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900545
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000546struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900547 int sampler; // required
548 int target_node; // required (index of the node to target)
549 std::string target_path; // required in ["translation", "rotation", "scale",
550 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900551 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200552 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100553 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900554
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900555 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
556 std::string extras_json_string;
557 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100558 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900559
Syoyo Fujita5b407452017-06-04 17:42:41 +0900560 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100561 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900562 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000563};
564
565struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900566 int input; // required
567 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200568 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
569 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200570 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900571 ExtensionMap extensions;
572
573 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
574 std::string extras_json_string;
575 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000576
Syoyo Fujita5b407452017-06-04 17:42:41 +0900577 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100578 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900579 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000580};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900581
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900582struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900583 std::string name;
584 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000585 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900586 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200587 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200588
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900589 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
590 std::string extras_json_string;
591 std::string extensions_json_string;
592
Selmar Kokff2b1f92019-10-21 17:58:09 +0200593 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100594 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900595 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900596};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900597
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000598struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900599 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900600 int inverseBindMatrices; // required here but not in the spec
601 int skeleton; // The index of the node used as a skeleton root
602 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000603
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900604 Value extras;
605 ExtensionMap extensions;
606
607 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
608 std::string extras_json_string;
609 std::string extensions_json_string;
610
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900611 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000612 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000613 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000614 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100615 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900616 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000617};
618
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000619struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900620 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900621 // glTF 2.0 spec does not define default value for `minFilter` and
622 // `magFilter`. Set -1 in TinyGLTF(issue #186)
623 int minFilter =
624 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800625 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900626 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
627 int magFilter =
628 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
629 int wrapS =
630 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
631 // "REPEAT"], default "REPEAT"
632 int wrapT =
633 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
634 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900635 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
636 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900637
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900638 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900639 ExtensionMap extensions;
640
641 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
642 std::string extras_json_string;
643 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900644
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000645 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900646 : minFilter(-1),
647 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800648 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900649 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100650 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900651 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000652};
653
Syoyo Fujita5b407452017-06-04 17:42:41 +0900654struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900655 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900656 int width;
657 int height;
658 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000659 int bits; // bit depth per channel. 8(byte), 16 or 32.
660 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
661 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900662 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900663 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500664 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
665 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900666 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
667 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900668 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900669 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900670
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900671 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
672 std::string extras_json_string;
673 std::string extensions_json_string;
674
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900675 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
676 // compressed for "image/jpeg" mime) This feature is good if you use custom
677 // image loader function. (e.g. delayed decoding of images for faster glTF
678 // parsing) Default parser for Image does not provide as-is loading feature at
679 // the moment. (You can manipulate this by providing your own LoadImageData
680 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200681 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900682
683 Image() : as_is(false) {
684 bufferView = -1;
685 width = -1;
686 height = -1;
687 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900688 bits = -1;
689 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900690 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100691 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500692
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900693 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000694};
695
696struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200697 std::string name;
698
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000699 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200700 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900701 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200702 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900703
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900704 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
705 std::string extras_json_string;
706 std::string extensions_json_string;
707
Syoyo Fujita5b407452017-06-04 17:42:41 +0900708 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100709 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500710
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900711 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000712};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900713
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900714struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900715 int index = -1; // required.
716 int texCoord; // The set index of texture's TEXCOORD attribute used for
717 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900718
719 Value extras;
720 ExtensionMap extensions;
721
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900722 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
723 std::string extras_json_string;
724 std::string extensions_json_string;
725
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900726 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100727 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900728 bool operator==(const TextureInfo &) const;
729};
730
731struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900732 int index = -1; // required
733 int texCoord; // The set index of texture's TEXCOORD attribute used for
734 // texture coordinate mapping.
735 double scale; // scaledNormal = normalize((<sampled normal texture value>
736 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900737
738 Value extras;
739 ExtensionMap extensions;
740
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900741 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
742 std::string extras_json_string;
743 std::string extensions_json_string;
744
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900745 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100746 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900747 bool operator==(const NormalTextureInfo &) const;
748};
749
750struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900751 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900752 int texCoord; // The set index of texture's TEXCOORD attribute used for
753 // texture coordinate mapping.
754 double strength; // occludedColor = lerp(color, color * <sampled occlusion
755 // texture value>, <occlusion strength>)
756
757 Value extras;
758 ExtensionMap extensions;
759
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900760 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
761 std::string extras_json_string;
762 std::string extensions_json_string;
763
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900764 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100765 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900766 bool operator==(const OcclusionTextureInfo &) const;
767};
768
769// pbrMetallicRoughness class defined in glTF 2.0 spec.
770struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900771 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900772 TextureInfo baseColorTexture;
773 double metallicFactor; // default 1
774 double roughnessFactor; // default 1
775 TextureInfo metallicRoughnessTexture;
776
777 Value extras;
778 ExtensionMap extensions;
779
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900780 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
781 std::string extras_json_string;
782 std::string extensions_json_string;
783
784 PbrMetallicRoughness()
785 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
786 metallicFactor(1.0),
787 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100788 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900789 bool operator==(const PbrMetallicRoughness &) const;
790};
791
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000792// Each extension should be stored in a ParameterMap.
793// members not in the values could be included in the ParameterMap
794// to keep a single material model
795struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900796 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900797
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900798 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900799 std::string alphaMode; // default "OPAQUE"
800 double alphaCutoff; // default 0.5
801 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900802
803 PbrMetallicRoughness pbrMetallicRoughness;
804
805 NormalTextureInfo normalTexture;
806 OcclusionTextureInfo occlusionTexture;
807 TextureInfo emissiveTexture;
808
Syoyo Fujita046400b2019-07-24 19:26:48 +0900809 // For backward compatibility
810 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
811 ParameterMap values;
812 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100813
814 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900815 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200816
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900817 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
818 std::string extras_json_string;
819 std::string extensions_json_string;
820
Syoyo Fujita046400b2019-07-24 19:26:48 +0900821 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100822 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900823
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900824 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000825};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900826
Syoyo Fujita5b407452017-06-04 17:42:41 +0900827struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900828 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900829 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900830 size_t byteOffset{0}; // minimum 0, default 0
831 size_t byteLength{0}; // required, minimum 1. 0 = invalid
832 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900833 // understood to be tightly packed
834 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
imallettd9ce9eb2022-10-07 10:37:09 -0700835 // or attribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900836 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900837 ExtensionMap extensions;
838
839 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
840 std::string extras_json_string;
841 std::string extensions_json_string;
842
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900843 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900844
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900845 BufferView()
846 : buffer(-1),
847 byteOffset(0),
848 byteLength(0),
849 byteStride(0),
850 target(0),
851 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100852 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900853 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000854};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900855
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000856struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900857 int bufferView; // optional in spec but required here since sparse accessor
858 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900859 std::string name;
860 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100861 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000862 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900863 size_t count; // required
864 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900865 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900866 ExtensionMap extensions;
867
868 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
869 std::string extras_json_string;
870 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000871
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900872 std::vector<double>
873 minValues; // optional. integer value is promoted to double
874 std::vector<double>
875 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900876
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100877 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000878 int count;
879 bool isSparse;
880 struct {
881 int byteOffset;
882 int bufferView;
883 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
884 } indices;
885 struct {
886 int bufferView;
887 int byteOffset;
888 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100889 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000890
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900891 ///
892 /// Utility function to compute byteStride for a given bufferView object.
893 /// Returns -1 upon invalid glTF value or parameter configuration.
894 ///
895 int ByteStride(const BufferView &bufferViewObject) const {
896 if (bufferViewObject.byteStride == 0) {
897 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500898 int componentSizeInBytes =
899 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900900 if (componentSizeInBytes <= 0) {
901 return -1;
902 }
903
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900904 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
905 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900906 return -1;
907 }
908
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900909 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900910 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700911 // Check if byteStride is a multiple of the size of the accessor's component
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500912 // type.
913 int componentSizeInBytes =
914 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900915 if (componentSizeInBytes <= 0) {
916 return -1;
917 }
918
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900919 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900920 return -1;
921 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100922 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900923 }
924
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900925 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900926 }
927
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900928 Accessor()
929 : bufferView(-1),
930 byteOffset(0),
931 normalized(false),
932 componentType(-1),
933 count(0),
934 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000935 sparse.isSparse = false;
936 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100937 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900938 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000939};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900940
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900941struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200942 double aspectRatio; // min > 0
943 double yfov; // required. min > 0
944 double zfar; // min > 0
945 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900946
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900947 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900948 : aspectRatio(0.0),
949 yfov(0.0),
imallettd9ce9eb2022-10-07 10:37:09 -0700950 zfar(0.0) // 0 = use infinite projection matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900951 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900952 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100953 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900954 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900955
Selmar09d2ff12018-03-15 17:30:42 +0100956 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900957 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900958
959 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
960 std::string extras_json_string;
961 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900962};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000963
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900964struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200965 double xmag; // required. must not be zero.
966 double ymag; // required. must not be zero.
967 double zfar; // required. `zfar` must be greater than `znear`.
968 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000969
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900970 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100971 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900972 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900973
Selmar09d2ff12018-03-15 17:30:42 +0100974 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900975 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900976
977 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
978 std::string extras_json_string;
979 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900980};
981
982struct Camera {
983 std::string type; // required. "perspective" or "orthographic"
984 std::string name;
985
986 PerspectiveCamera perspective;
987 OrthographicCamera orthographic;
988
989 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100990 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900991 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900992
Selmar09d2ff12018-03-15 17:30:42 +0100993 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000994 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900995
996 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
997 std::string extras_json_string;
998 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900999};
1000
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001001struct Primitive {
1002 std::map<std::string, int> attributes; // (required) A dictionary object of
1003 // integer, where each integer
1004 // is the index of the accessor
1005 // containing an attribute.
1006 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +09001007 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001008 int indices; // The index of the accessor that contains the indices.
1009 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001010 std::vector<std::map<std::string, int> > targets; // array of morph targets,
imallettd9ce9eb2022-10-07 10:37:09 -07001011 // where each target is a dict with attributes in ["POSITION, "NORMAL",
Syoyo Fujita5b407452017-06-04 17:42:41 +09001012 // "TANGENT"] pointing
1013 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -05001014 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001015 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001016
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001017 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1018 std::string extras_json_string;
1019 std::string extensions_json_string;
1020
Syoyo Fujita5b407452017-06-04 17:42:41 +09001021 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001022 material = -1;
1023 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001024 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001025 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001026 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001027 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001028};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001029
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001030struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001031 std::string name;
1032 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001033 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001034 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001035 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001036
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001037 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1038 std::string extras_json_string;
1039 std::string extensions_json_string;
1040
jrkoonced1e14722019-08-27 11:51:02 -05001041 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001042 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001043 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001044};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001045
1046class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001047 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001048 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001049
Selmar Kokb74fade2019-10-29 16:09:32 +01001050 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001051
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001052 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001053
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001054 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001055
1056 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001057 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001058 int mesh;
1059 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001060 std::vector<double> rotation; // length must be 0 or 4
1061 std::vector<double> scale; // length must be 0 or 3
1062 std::vector<double> translation; // length must be 0 or 3
1063 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001064 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001065
Selmar09d2ff12018-03-15 17:30:42 +01001066 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001067 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001068
1069 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1070 std::string extras_json_string;
1071 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001072};
1073
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001074struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001075 std::string name;
1076 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001077 std::string
1078 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001079 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001080 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001081 ExtensionMap extensions;
1082
1083 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1084 std::string extras_json_string;
1085 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001086
Selmar Kokb74fade2019-10-29 16:09:32 +01001087 Buffer() = default;
1088 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001089 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001090};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001091
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001092struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001093 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001094 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001095 std::string minVersion;
1096 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001097 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001098 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001099
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001100 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1101 std::string extras_json_string;
1102 std::string extensions_json_string;
1103
jrkoonced1e14722019-08-27 11:51:02 -05001104 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001105 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001106 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001107};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001108
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001109struct Scene {
1110 std::string name;
1111 std::vector<int> nodes;
1112
Selmar09d2ff12018-03-15 17:30:42 +01001113 ExtensionMap extensions;
1114 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001115
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001116 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1117 std::string extras_json_string;
1118 std::string extensions_json_string;
1119
jrkoonced1e14722019-08-27 11:51:02 -05001120 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001121 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001122 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001123};
1124
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001125struct SpotLight {
1126 double innerConeAngle;
1127 double outerConeAngle;
1128
Johan Bowald52936a02019-07-17 09:06:45 +02001129 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001130 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001131 bool operator==(const SpotLight &) const;
1132
1133 ExtensionMap extensions;
1134 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001135
1136 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1137 std::string extras_json_string;
1138 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001139};
1140
Emanuel Schrade186322b2017-11-06 11:14:41 +01001141struct Light {
1142 std::string name;
1143 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001144 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001145 std::string type;
imallettd9ce9eb2022-10-07 10:37:09 -07001146 double range{0.0}; // 0.0 = infinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001147 SpotLight spot;
1148
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001149 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001150 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001151
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001152 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001153
1154 ExtensionMap extensions;
1155 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001156
1157 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1158 std::string extras_json_string;
1159 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001160};
1161
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001162class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001163 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001164 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001165 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001166
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001167 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001168
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001169 std::vector<Accessor> accessors;
1170 std::vector<Animation> animations;
1171 std::vector<Buffer> buffers;
1172 std::vector<BufferView> bufferViews;
1173 std::vector<Material> materials;
1174 std::vector<Mesh> meshes;
1175 std::vector<Node> nodes;
1176 std::vector<Texture> textures;
1177 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001178 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001179 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001180 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001181 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001182 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001183
sammyKhana0a62bd2020-01-17 13:41:16 +01001184 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001185 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001186 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001187
1188 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001189
1190 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001191 ExtensionMap extensions;
1192
1193 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1194 std::string extras_json_string;
1195 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001196};
1197
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001198enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001199 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001200 REQUIRE_VERSION = 0x01,
1201 REQUIRE_SCENE = 0x02,
1202 REQUIRE_SCENES = 0x04,
1203 REQUIRE_NODES = 0x08,
1204 REQUIRE_ACCESSORS = 0x10,
1205 REQUIRE_BUFFERS = 0x20,
1206 REQUIRE_BUFFER_VIEWS = 0x40,
1207 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001208};
1209
Squareysff644d82018-03-13 22:36:18 +01001210///
1211/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1212///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001213typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1214 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001215 const unsigned char *, int,
1216 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001217
johan bowald642a3432018-04-01 12:37:18 +02001218///
1219/// WriteImageDataFunction type. Signature for custom image writing callbacks.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001220/// The out_uri parameter becomes the URI written to the gltf and may reference
1221/// a file or contain a data URI.
johan bowald642a3432018-04-01 12:37:18 +02001222///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001223typedef bool (*WriteImageDataFunction)(const std::string *basepath,
1224 const std::string *filename,
1225 const Image *image, bool embedImages,
1226 std::string *out_uri, void *user_pointer);
johan bowald642a3432018-04-01 12:37:18 +02001227
Squareys2d3594d2018-03-13 22:40:53 +01001228#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001229// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001230bool LoadImageData(Image *image, const int image_idx, std::string *err,
1231 std::string *warn, int req_width, int req_height,
1232 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001233#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001234
johan bowald642a3432018-04-01 12:37:18 +02001235#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1236// Declaration of default image writer callback
1237bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001238 const Image *image, bool embedImages, std::string *out_uri,
1239 void *);
johan bowald642a3432018-04-01 12:37:18 +02001240#endif
1241
Paolo Jovone6601bf2018-07-07 20:43:33 +02001242///
1243/// FilExistsFunction type. Signature for custom filesystem callbacks.
1244///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001245typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001246
1247///
1248/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1249///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001250typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001251
1252///
1253/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1254///
1255typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001256 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001257 void *);
1258
1259///
1260/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1261///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001262typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001263 const std::vector<unsigned char> &,
1264 void *);
1265
1266///
1267/// A structure containing all required filesystem callbacks and a pointer to
1268/// their user data.
1269///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001270struct FsCallbacks {
1271 FileExistsFunction FileExists;
1272 ExpandFilePathFunction ExpandFilePath;
1273 ReadWholeFileFunction ReadWholeFile;
1274 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001275
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001276 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001277};
1278
1279#ifndef TINYGLTF_NO_FS
1280// Declaration of default filesystem callbacks
1281
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001282bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001283
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001284///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001285/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001286/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001287///
1288/// @param[in] filepath File path string. Assume UTF-8
1289/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1290///
1291std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001292
1293bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001294 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001295
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001296bool WriteWholeFile(std::string *err, const std::string &filepath,
1297 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001298#endif
1299
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001300///
imallettd9ce9eb2022-10-07 10:37:09 -07001301/// glTF Parser/Serializer context.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001302///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001303class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001304 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001305#ifdef __clang__
1306#pragma clang diagnostic push
1307#pragma clang diagnostic ignored "-Wc++98-compat"
1308#endif
1309
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001310 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001311
1312#ifdef __clang__
1313#pragma clang diagnostic pop
1314#endif
1315
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001316 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001317
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001318 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001319 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001320 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001321 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001322 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001323 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001324 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001325 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001326
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001327 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001328 /// Loads glTF ASCII asset from string(memory).
1329 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001330 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1331 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1332 /// message to `warn` for example it fails to load asserts. Returns false and
1333 /// 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 LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1336 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001337 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001338 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001339
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001340 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001341 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001342 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001343 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001344 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001345 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001346 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001347 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001348
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001349 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001350 /// Loads glTF binary asset from memory.
1351 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001352 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1353 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001354 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001355 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001356 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001357 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001358 const unsigned char *bytes,
1359 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001360 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001361 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001362
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001363 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001364 /// Write glTF to stream, buffers and images will be embedded
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001365 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001366 bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001367 bool prettyPrint, bool writeBinary);
1368
1369 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001370 /// Write glTF to file.
1371 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001372 bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001373 bool embedImages, bool embedBuffers,
1374 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001375
Squareysff644d82018-03-13 22:36:18 +01001376 ///
1377 /// Set callback to use for loading image data
1378 ///
1379 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1380
johan bowald642a3432018-04-01 12:37:18 +02001381 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001382 /// Unset(remove) callback of loading image data
1383 ///
1384 void RemoveImageLoader();
1385
1386 ///
johan bowald642a3432018-04-01 12:37:18 +02001387 /// Set callback to use for writing image data
1388 ///
1389 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1390
Paolo Jovone6601bf2018-07-07 20:43:33 +02001391 ///
1392 /// Set callbacks to use for filesystem (fs) access and their user data
1393 ///
1394 void SetFsCallbacks(FsCallbacks callbacks);
1395
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001396 ///
1397 /// Set serializing default values(default = false).
1398 /// When true, default values are force serialized to .glTF.
imallettd9ce9eb2022-10-07 10:37:09 -07001399 /// This may be helpful if you want to serialize a full description of glTF
Syoyo Fujitaff515702019-08-24 16:29:14 +09001400 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001401 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001402 /// TODO(LTE): Supply parsing option as function arguments to
1403 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001404 ///
1405 void SetSerializeDefaultValues(const bool enabled) {
1406 serialize_default_values_ = enabled;
1407 }
1408
Syoyo Fujitaff515702019-08-24 16:29:14 +09001409 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001410
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001411 ///
1412 /// Store original JSON string for `extras` and `extensions`.
1413 /// This feature will be useful when the user want to reconstruct custom data
1414 /// structure from JSON string.
1415 ///
1416 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1417 store_original_json_for_extras_and_extensions_ = enabled;
1418 }
1419
1420 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1421 return store_original_json_for_extras_and_extensions_;
1422 }
1423
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001424 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001425 /// Specify whether preserve image channels when loading images or not.
1426 /// (Not effective when the user supplies their own LoadImageData callbacks)
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001427 ///
1428 void SetPreserveImageChannels(bool onoff) {
1429 preserve_image_channels_ = onoff;
1430 }
1431
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001432 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001433
Syoyo Fujitabeded612016-05-01 20:03:43 +09001434 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001435 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001436 /// Loads glTF asset from string(memory).
1437 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001438 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001439 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001440 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001441 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1442 const char *str, const unsigned int length,
1443 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001444
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001445 const unsigned char *bin_data_ = nullptr;
1446 size_t bin_size_ = 0;
1447 bool is_binary_ = false;
1448
Syoyo Fujitaff515702019-08-24 16:29:14 +09001449 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001450
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001451 bool store_original_json_for_extras_and_extensions_ = false;
1452
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001453 bool preserve_image_channels_ = false; /// Default false(expand channels to
1454 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001455
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001456 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001457 std::string warn_;
1458 std::string err_;
1459
Paolo Jovone6601bf2018-07-07 20:43:33 +02001460 FsCallbacks fs = {
1461#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001462 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1463 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001464
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001465 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001466#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001467 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001468
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001469 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001470#endif
1471 };
1472
Squareysff644d82018-03-13 22:36:18 +01001473 LoadImageDataFunction LoadImageData =
1474#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001475 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001476#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001477 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001478#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001479 void *load_image_user_data_{nullptr};
1480 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001481
1482 WriteImageDataFunction WriteImageData =
1483#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001484 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001485#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001486 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001487#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001488 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001489};
1490
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001491#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001492#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001493#endif
1494
Syoyo Fujita7c877972016-03-08 01:31:49 +09001495} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001496
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001497#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001498
Selmar Kok31cb7f92018-10-03 15:39:05 +02001499#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001500#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001501//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001502#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001503#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001504#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001505#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001506#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001507
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001508#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001509// Disable some warnings for external files.
1510#pragma clang diagnostic push
1511#pragma clang diagnostic ignored "-Wfloat-equal"
1512#pragma clang diagnostic ignored "-Wexit-time-destructors"
1513#pragma clang diagnostic ignored "-Wconversion"
1514#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001515#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001516#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001517#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001518#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001519#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1520#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001521#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001522#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001523#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1524#pragma clang diagnostic ignored "-Wswitch-enum"
1525#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001526#pragma clang diagnostic ignored "-Wweak-vtables"
1527#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001528#if __has_warning("-Wdouble-promotion")
1529#pragma clang diagnostic ignored "-Wdouble-promotion"
1530#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001531#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001532#pragma clang diagnostic ignored "-Wcomma"
1533#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001534#if __has_warning("-Wzero-as-null-pointer-constant")
1535#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1536#endif
1537#if __has_warning("-Wcast-qual")
1538#pragma clang diagnostic ignored "-Wcast-qual"
1539#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001540#if __has_warning("-Wmissing-variable-declarations")
1541#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1542#endif
1543#if __has_warning("-Wmissing-prototypes")
1544#pragma clang diagnostic ignored "-Wmissing-prototypes"
1545#endif
1546#if __has_warning("-Wcast-align")
1547#pragma clang diagnostic ignored "-Wcast-align"
1548#endif
1549#if __has_warning("-Wnewline-eof")
1550#pragma clang diagnostic ignored "-Wnewline-eof"
1551#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001552#if __has_warning("-Wunused-parameter")
1553#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001554#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001555#if __has_warning("-Wmismatched-tags")
1556#pragma clang diagnostic ignored "-Wmismatched-tags"
1557#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001558#if __has_warning("-Wextra-semi-stmt")
1559#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1560#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001561#endif
1562
imallettd9ce9eb2022-10-07 10:37:09 -07001563// Disable GCC warnings
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001564#ifdef __GNUC__
1565#pragma GCC diagnostic push
1566#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001567#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001568
krokofc0116b2019-03-03 08:28:49 +02001569#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001570#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001571#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001572#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001573#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001574#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001575#include "prettywriter.h"
1576#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001577#include "stringbuffer.h"
1578#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001579#endif
krokof4b6d112019-03-03 01:11:31 +02001580#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001581#endif
Alex Wood7319db72019-01-24 15:38:16 -05001582
1583#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001584#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001585#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001586#endif
Squareys2d3594d2018-03-13 22:40:53 +01001587
1588#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001589#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001590#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001591#endif
krokof4b6d112019-03-03 01:11:31 +02001592#endif
Squareys2d3594d2018-03-13 22:40:53 +01001593
johan bowald642a3432018-04-01 12:37:18 +02001594#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001595#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001596#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001597#endif
krokof4b6d112019-03-03 01:11:31 +02001598#endif
johan bowald642a3432018-04-01 12:37:18 +02001599
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001600#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001601#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001602#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001603
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001604#ifdef __GNUC__
1605#pragma GCC diagnostic pop
1606#endif
1607
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001608#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001609
1610// issue 143.
1611// Define NOMINMAX to avoid min/max defines,
imallett3a295882022-10-07 11:20:39 -07001612// but undef it after included Windows.h
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001613#ifndef NOMINMAX
1614#define TINYGLTF_INTERNAL_NOMINMAX
1615#define NOMINMAX
1616#endif
1617
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001618#ifndef WIN32_LEAN_AND_MEAN
1619#define WIN32_LEAN_AND_MEAN
1620#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1621#endif
imallett3a295882022-10-07 11:20:39 -07001622#ifndef __MINGW32__
imallett56e10982022-10-07 10:35:16 -07001623#include <Windows.h> // include API for expanding a file path
imallett3a295882022-10-07 11:20:39 -07001624#else
1625#include <windows.h>
1626#endif
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001627
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001628#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1629#undef WIN32_LEAN_AND_MEAN
1630#endif
1631
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001632#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1633#undef NOMINMAX
1634#endif
1635
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001636#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001637
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001638#include <fcntl.h> // _O_RDONLY
1639
1640#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001641
1642#endif
1643
Julian Smith0598a202021-08-25 12:06:08 +01001644#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001645//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001646#endif
1647
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001648#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001649// Big endian
1650#else
1651#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1652#define TINYGLTF_LITTLE_ENDIAN 1
1653#endif
1654#endif
1655
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001656namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001657#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001658
1659#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001660// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1661// documents may be active at once.
1662using json =
1663 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1664using json_const_iterator = json::ConstMemberIterator;
1665using json_const_array_iterator = json const *;
1666using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001667 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001668rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1669rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001670#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001671// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1672// not thread safe. Only a single JsonDocument may be active at any one time,
1673// meaning only a single gltf load/save can be active any one time.
1674using json = rapidjson::Value;
1675using json_const_iterator = json::ConstMemberIterator;
1676using json_const_array_iterator = json const *;
1677rapidjson::Document *s_pActiveDocument = nullptr;
1678rapidjson::Document::AllocatorType &GetAllocator() {
1679 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1680 return s_pActiveDocument->GetAllocator();
1681}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001682
1683#ifdef __clang__
1684#pragma clang diagnostic push
1685// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1686#pragma clang diagnostic ignored "-Wunused-member-function"
1687#endif
1688
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001689struct JsonDocument : public rapidjson::Document {
1690 JsonDocument() {
1691 assert(s_pActiveDocument ==
1692 nullptr); // When using default allocator, only one document can be
1693 // active at a time, if you need multiple active at once,
1694 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1695 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001696 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001697 JsonDocument(const JsonDocument &) = delete;
1698 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001699 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001700 s_pActiveDocument = this;
1701 rhs.isNil = true;
1702 }
1703 ~JsonDocument() {
1704 if (!isNil) {
1705 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001706 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001707 }
jrkooncece7fa742019-09-04 13:31:44 -05001708
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001709 private:
1710 bool isNil = false;
1711};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001712
1713#ifdef __clang__
1714#pragma clang diagnostic pop
1715#endif
1716
jrkooncece7fa742019-09-04 13:31:44 -05001717#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001718
jrkooncecba5d6c2019-08-29 11:26:22 -05001719#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001720using nlohmann::json;
1721using json_const_iterator = json::const_iterator;
1722using json_const_array_iterator = json_const_iterator;
1723using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001724#endif
1725
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001726void JsonParse(JsonDocument &doc, const char *str, size_t length,
1727 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001728#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001729 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001730 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001731#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001732 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001733#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001734}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001735} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001736
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001737#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001738#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001739#endif
1740
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001741#ifdef __clang__
1742#pragma clang diagnostic push
1743#pragma clang diagnostic ignored "-Wc++98-compat"
1744#endif
1745
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001746namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001747
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001748///
1749/// Internal LoadImageDataOption struct.
1750/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001751/// The struct is not passed when the user supply their own LoadImageData
1752/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001753///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001754struct LoadImageDataOption {
1755 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1756 // channels) default `false`(channels are expanded to RGBA for backward
imallettd9ce9eb2022-10-07 10:37:09 -07001757 // compatibility).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001758 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001759};
1760
Selmar Kok31cb7f92018-10-03 15:39:05 +02001761// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001762static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1763 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001764
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001765 switch (one.Type()) {
1766 case NULL_TYPE:
1767 return true;
1768 case BOOL_TYPE:
1769 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001770 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001771 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1772 case INT_TYPE:
1773 return one.Get<int>() == other.Get<int>();
1774 case OBJECT_TYPE: {
1775 auto oneObj = one.Get<tinygltf::Value::Object>();
1776 auto otherObj = other.Get<tinygltf::Value::Object>();
1777 if (oneObj.size() != otherObj.size()) return false;
1778 for (auto &it : oneObj) {
1779 auto otherIt = otherObj.find(it.first);
1780 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001781
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001782 if (!Equals(it.second, otherIt->second)) return false;
1783 }
1784 return true;
1785 }
1786 case ARRAY_TYPE: {
1787 if (one.Size() != other.Size()) return false;
1788 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001789 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001790 return true;
1791 }
1792 case STRING_TYPE:
1793 return one.Get<std::string>() == other.Get<std::string>();
1794 case BINARY_TYPE:
1795 return one.Get<std::vector<unsigned char> >() ==
1796 other.Get<std::vector<unsigned char> >();
1797 default: {
1798 // unhandled type
1799 return false;
1800 }
1801 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001802}
1803
1804// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001805static bool Equals(const std::vector<double> &one,
1806 const std::vector<double> &other) {
1807 if (one.size() != other.size()) return false;
1808 for (int i = 0; i < int(one.size()); ++i) {
1809 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1810 }
1811 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001812}
1813
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001814bool Accessor::operator==(const Accessor &other) const {
1815 return this->bufferView == other.bufferView &&
1816 this->byteOffset == other.byteOffset &&
1817 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001818 this->count == other.count && this->extensions == other.extensions &&
1819 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001820 Equals(this->maxValues, other.maxValues) &&
1821 Equals(this->minValues, other.minValues) && this->name == other.name &&
1822 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001823}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001824bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001825 return this->channels == other.channels &&
1826 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001827 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001828}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001829bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001830 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001831 this->target_node == other.target_node &&
1832 this->target_path == other.target_path &&
1833 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001834}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001835bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001836 return this->extras == other.extras && this->extensions == other.extensions &&
1837 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001838 this->interpolation == other.interpolation &&
1839 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001840}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001841bool Asset::operator==(const Asset &other) const {
1842 return this->copyright == other.copyright &&
1843 this->extensions == other.extensions && this->extras == other.extras &&
1844 this->generator == other.generator &&
1845 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001846}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001847bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001848 return this->data == other.data && this->extensions == other.extensions &&
1849 this->extras == other.extras && this->name == other.name &&
1850 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001851}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001852bool BufferView::operator==(const BufferView &other) const {
1853 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1854 this->byteOffset == other.byteOffset &&
1855 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001856 this->target == other.target && this->extensions == other.extensions &&
1857 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001858 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001859}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001860bool Camera::operator==(const Camera &other) const {
1861 return this->name == other.name && this->extensions == other.extensions &&
1862 this->extras == other.extras &&
1863 this->orthographic == other.orthographic &&
1864 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001865}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001866bool Image::operator==(const Image &other) const {
1867 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001868 this->component == other.component &&
1869 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001870 this->height == other.height && this->image == other.image &&
1871 this->mimeType == other.mimeType && this->name == other.name &&
1872 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001873}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001874bool Light::operator==(const Light &other) const {
1875 return Equals(this->color, other.color) && this->name == other.name &&
1876 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001877}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001878bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001879 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1880 (this->normalTexture == other.normalTexture) &&
1881 (this->occlusionTexture == other.occlusionTexture) &&
1882 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001883 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001884 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001885 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001886 (this->doubleSided == other.doubleSided) &&
1887 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001888 (this->extras == other.extras) && (this->values == other.values) &&
1889 (this->additionalValues == other.additionalValues) &&
1890 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001891}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001892bool Mesh::operator==(const Mesh &other) const {
1893 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001894 this->name == other.name && Equals(this->weights, other.weights) &&
1895 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001896}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001897bool Model::operator==(const Model &other) const {
1898 return this->accessors == other.accessors &&
1899 this->animations == other.animations && this->asset == other.asset &&
1900 this->buffers == other.buffers &&
1901 this->bufferViews == other.bufferViews &&
1902 this->cameras == other.cameras &&
1903 this->defaultScene == other.defaultScene &&
1904 this->extensions == other.extensions &&
1905 this->extensionsRequired == other.extensionsRequired &&
1906 this->extensionsUsed == other.extensionsUsed &&
1907 this->extras == other.extras && this->images == other.images &&
1908 this->lights == other.lights && this->materials == other.materials &&
1909 this->meshes == other.meshes && this->nodes == other.nodes &&
1910 this->samplers == other.samplers && this->scenes == other.scenes &&
1911 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001912}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001913bool Node::operator==(const Node &other) const {
1914 return this->camera == other.camera && this->children == other.children &&
1915 this->extensions == other.extensions && this->extras == other.extras &&
1916 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1917 this->name == other.name && Equals(this->rotation, other.rotation) &&
1918 Equals(this->scale, other.scale) && this->skin == other.skin &&
1919 Equals(this->translation, other.translation) &&
1920 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001921}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001922bool SpotLight::operator==(const SpotLight &other) const {
1923 return this->extensions == other.extensions && this->extras == other.extras &&
1924 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1925 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1926}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001927bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1928 return this->extensions == other.extensions && this->extras == other.extras &&
1929 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1930 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1931 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1932 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001933}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001934bool Parameter::operator==(const Parameter &other) const {
1935 if (this->bool_value != other.bool_value ||
1936 this->has_number_value != other.has_number_value)
1937 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001938
Selmar Kok2bda71c2018-10-05 14:36:05 +02001939 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1940 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001941
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001942 if (this->json_double_value.size() != other.json_double_value.size())
1943 return false;
1944 for (auto &it : this->json_double_value) {
1945 auto otherIt = other.json_double_value.find(it.first);
1946 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001947
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001948 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1949 }
1950
1951 if (!Equals(this->number_array, other.number_array)) return false;
1952
1953 if (this->string_value != other.string_value) return false;
1954
1955 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001956}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001957bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1958 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1959 this->extensions == other.extensions && this->extras == other.extras &&
1960 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1961 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1962 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001963}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001964bool Primitive::operator==(const Primitive &other) const {
1965 return this->attributes == other.attributes && this->extras == other.extras &&
1966 this->indices == other.indices && this->material == other.material &&
1967 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001968}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001969bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001970 return this->extensions == other.extensions && this->extras == other.extras &&
1971 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001972 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001973 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001974
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001975 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02001976}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001977bool Scene::operator==(const Scene &other) const {
1978 return this->extensions == other.extensions && this->extras == other.extras &&
1979 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001980}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001981bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001982 return this->extensions == other.extensions && this->extras == other.extras &&
1983 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001984 this->joints == other.joints && this->name == other.name &&
1985 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001986}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001987bool Texture::operator==(const Texture &other) const {
1988 return this->extensions == other.extensions && this->extras == other.extras &&
1989 this->name == other.name && this->sampler == other.sampler &&
1990 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001991}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001992bool TextureInfo::operator==(const TextureInfo &other) const {
1993 return this->extensions == other.extensions && this->extras == other.extras &&
1994 this->index == other.index && this->texCoord == other.texCoord;
1995}
1996bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1997 return this->extensions == other.extensions && this->extras == other.extras &&
1998 this->index == other.index && this->texCoord == other.texCoord &&
1999 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
2000}
2001bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
2002 return this->extensions == other.extensions && this->extras == other.extras &&
2003 this->index == other.index && this->texCoord == other.texCoord &&
2004 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
2005}
2006bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2007 return this->extensions == other.extensions && this->extras == other.extras &&
2008 (this->baseColorTexture == other.baseColorTexture) &&
2009 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002010 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002011 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2012 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2013}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002014bool Value::operator==(const Value &other) const {
2015 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002016}
2017
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002018static void swap4(unsigned int *val) {
2019#ifdef TINYGLTF_LITTLE_ENDIAN
2020 (void)val;
2021#else
2022 unsigned int tmp = *val;
2023 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2024 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2025
2026 dst[0] = src[3];
2027 dst[1] = src[2];
2028 dst[2] = src[1];
2029 dst[3] = src[0];
2030#endif
2031}
2032
Syoyo Fujitabeded612016-05-01 20:03:43 +09002033static std::string JoinPath(const std::string &path0,
2034 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002035 if (path0.empty()) {
2036 return path1;
2037 } else {
2038 // check '/'
2039 char lastChar = *path0.rbegin();
2040 if (lastChar != '/') {
2041 return path0 + std::string("/") + path1;
2042 } else {
2043 return path0 + path1;
2044 }
2045 }
2046}
2047
Syoyo Fujita643ce102016-05-01 17:19:37 +09002048static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002049 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002050 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2051 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002052 // Error, fs callback[s] missing
2053 return std::string();
2054 }
2055
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002056 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002057 std::string absPath =
2058 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002059 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002060 return absPath;
2061 }
2062 }
2063
2064 return std::string();
2065}
2066
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002067static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002068 if (FileName.find_last_of(".") != std::string::npos)
2069 return FileName.substr(FileName.find_last_of(".") + 1);
2070 return "";
2071}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002072
Syoyo Fujita643ce102016-05-01 17:19:37 +09002073static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002074 if (filepath.find_last_of("/\\") != std::string::npos)
2075 return filepath.substr(0, filepath.find_last_of("/\\"));
2076 return "";
2077}
2078
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002079static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002080 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002081 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002082 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002083}
2084
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002085std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002086std::string base64_decode(std::string const &s);
2087
2088/*
2089 base64.cpp and base64.h
2090
2091 Copyright (C) 2004-2008 René Nyffenegger
2092
2093 This source code is provided 'as-is', without any express or implied
2094 warranty. In no event will the author be held liable for any damages
2095 arising from the use of this software.
2096
2097 Permission is granted to anyone to use this software for any purpose,
2098 including commercial applications, and to alter it and redistribute it
2099 freely, subject to the following restrictions:
2100
2101 1. The origin of this source code must not be misrepresented; you must not
2102 claim that you wrote the original source code. If you use this source code
2103 in a product, an acknowledgment in the product documentation would be
2104 appreciated but is not required.
2105
2106 2. Altered source versions must be plainly marked as such, and must not be
2107 misrepresented as being the original source code.
2108
2109 3. This notice may not be removed or altered from any source distribution.
2110
2111 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2112
2113*/
2114
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002115#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002116#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002117#pragma clang diagnostic ignored "-Wsign-conversion"
2118#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002119#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002120
2121static inline bool is_base64(unsigned char c) {
2122 return (isalnum(c) || (c == '+') || (c == '/'));
2123}
2124
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002125std::string base64_encode(unsigned char const *bytes_to_encode,
2126 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002127 std::string ret;
2128 int i = 0;
2129 int j = 0;
2130 unsigned char char_array_3[3];
2131 unsigned char char_array_4[4];
2132
Syoyo Fujitaff515702019-08-24 16:29:14 +09002133 const char *base64_chars =
2134 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2135 "abcdefghijklmnopqrstuvwxyz"
2136 "0123456789+/";
2137
johan bowald30c53472018-03-30 11:49:36 +02002138 while (in_len--) {
2139 char_array_3[i++] = *(bytes_to_encode++);
2140 if (i == 3) {
2141 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002142 char_array_4[1] =
2143 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2144 char_array_4[2] =
2145 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002146 char_array_4[3] = char_array_3[2] & 0x3f;
2147
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002148 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002149 i = 0;
2150 }
2151 }
2152
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002153 if (i) {
2154 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002155
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002156 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2157 char_array_4[1] =
2158 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2159 char_array_4[2] =
2160 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002161
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002162 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002163
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002164 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002165 }
2166
2167 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002168}
2169
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002170std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002171 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002172 int i = 0;
2173 int j = 0;
2174 int in_ = 0;
2175 unsigned char char_array_4[4], char_array_3[3];
2176 std::string ret;
2177
Syoyo Fujitaff515702019-08-24 16:29:14 +09002178 const std::string base64_chars =
2179 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2180 "abcdefghijklmnopqrstuvwxyz"
2181 "0123456789+/";
2182
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002183 while (in_len-- && (encoded_string[in_] != '=') &&
2184 is_base64(encoded_string[in_])) {
2185 char_array_4[i++] = encoded_string[in_];
2186 in_++;
2187 if (i == 4) {
2188 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002189 char_array_4[i] =
2190 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002191
2192 char_array_3[0] =
2193 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2194 char_array_3[1] =
2195 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2196 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2197
Syoyo Fujita7c877972016-03-08 01:31:49 +09002198 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002199 i = 0;
2200 }
2201 }
2202
2203 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002204 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002205
2206 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002207 char_array_4[j] =
2208 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002209
2210 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2211 char_array_3[1] =
2212 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2213 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2214
Syoyo Fujita7c877972016-03-08 01:31:49 +09002215 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002216 }
2217
2218 return ret;
2219}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002220#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002221#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002222#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002223
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002224// https://github.com/syoyo/tinygltf/issues/228
2225// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2226// decoding?
2227//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002228// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002229// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002230// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002231// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002232// License: Boost Software License
2233// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002234
Alexander Woode4bc6c72021-10-14 08:54:59 -04002235// Permission is hereby granted, free of charge, to any person or organization
2236// obtaining a copy of the software and accompanying documentation covered by
2237// this license (the "Software") to use, reproduce, display, distribute,
2238// execute, and transmit the Software, and to prepare derivative works of the
2239// Software, and to permit third-parties to whom the Software is furnished to
2240// do so, all subject to the following:
2241// The copyright notices in the Software and this entire statement, including
2242// the above license grant, this restriction and the following disclaimer,
2243// must be included in all copies of the Software, in whole or in part, and
2244// all derivative works of the Software, unless such copies or derivative
2245// works are solely in the form of machine-executable object code generated by
2246// a source language processor.
2247// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2248// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2249// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2250// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2251// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2252// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2253// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002254//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002255namespace dlib {
2256
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002257inline unsigned char from_hex(unsigned char ch) {
2258 if (ch <= '9' && ch >= '0')
2259 ch -= '0';
2260 else if (ch <= 'f' && ch >= 'a')
2261 ch -= 'a' - 10;
2262 else if (ch <= 'F' && ch >= 'A')
2263 ch -= 'A' - 10;
2264 else
2265 ch = 0;
2266 return ch;
2267}
2268
2269static const std::string urldecode(const std::string &str) {
2270 using namespace std;
2271 string result;
2272 string::size_type i;
2273 for (i = 0; i < str.size(); ++i) {
2274 if (str[i] == '+') {
2275 result += ' ';
2276 } else if (str[i] == '%' && str.size() > i + 2) {
2277 const unsigned char ch1 =
2278 from_hex(static_cast<unsigned char>(str[i + 1]));
2279 const unsigned char ch2 =
2280 from_hex(static_cast<unsigned char>(str[i + 2]));
2281 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2282 result += static_cast<char>(ch);
2283 i += 2;
2284 } else {
2285 result += str[i];
2286 }
2287 }
2288 return result;
2289}
2290
2291} // namespace dlib
2292// --- dlib end --------------------------------------------------------------
2293
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002294static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002295 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002296 const std::string &basedir, bool required,
2297 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002298 if (fs == nullptr || fs->FileExists == nullptr ||
2299 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002300 // This is a developer error, assert() ?
2301 if (err) {
2302 (*err) += "FS callback[s] not set\n";
2303 }
2304 return false;
2305 }
2306
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002307 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002308
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002309 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002310
2311 std::vector<std::string> paths;
2312 paths.push_back(basedir);
2313 paths.push_back(".");
2314
Paolo Jovone6601bf2018-07-07 20:43:33 +02002315 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002316 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002317 if (failMsgOut) {
2318 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002319 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002320 return false;
2321 }
2322
Paolo Jovone6601bf2018-07-07 20:43:33 +02002323 std::vector<unsigned char> buf;
2324 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002325 bool fileRead =
2326 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002327 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002328 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002329 (*failMsgOut) +=
2330 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002331 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002332 return false;
2333 }
2334
Paolo Jovone6601bf2018-07-07 20:43:33 +02002335 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002336 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002337 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002338 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002339 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002340 return false;
2341 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002342
2343 if (checkSize) {
2344 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002345 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002346 return true;
2347 } else {
2348 std::stringstream ss;
2349 ss << "File size mismatch : " << filepath << ", requestedBytes "
2350 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002351 if (failMsgOut) {
2352 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002353 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002354 return false;
2355 }
2356 }
2357
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002358 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002359 return true;
2360}
2361
Squareysff644d82018-03-13 22:36:18 +01002362void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002363 LoadImageData = func;
2364 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002365 user_image_loader_ = true;
2366}
2367
2368void TinyGLTF::RemoveImageLoader() {
2369 LoadImageData =
2370#ifndef TINYGLTF_NO_STB_IMAGE
2371 &tinygltf::LoadImageData;
2372#else
2373 nullptr;
2374#endif
2375
2376 load_image_user_data_ = nullptr;
2377 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002378}
2379
Squareys2d3594d2018-03-13 22:40:53 +01002380#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002381bool LoadImageData(Image *image, const int image_idx, std::string *err,
2382 std::string *warn, int req_width, int req_height,
2383 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002384 (void)warn;
2385
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002386 LoadImageDataOption option;
2387 if (user_data) {
2388 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2389 }
2390
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002391 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002392
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002393 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002394
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002395 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002396 // false: force 32-bit textures for common Vulkan compatibility. It appears
2397 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002398 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002399 int bits = 8;
2400 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002401
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002402 // It is possible that the image we want to load is a 16bit per channel image
2403 // We are going to attempt to load it as 16bit per channel, and if it worked,
imallettd9ce9eb2022-10-07 10:37:09 -07002404 // set the image data accordingly. We are casting the returned pointer into
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002405 // unsigned char, because we are representing "bytes". But we are updating
2406 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2407 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002408 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002409 data = reinterpret_cast<unsigned char *>(
2410 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002411 if (data) {
2412 bits = 16;
2413 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2414 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002415 }
2416
2417 // at this point, if data is still NULL, it means that the image wasn't
2418 // 16bit per channel, we are going to load it as a normal 8bit per channel
imallettd9ce9eb2022-10-07 10:37:09 -07002419 // image as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002420 // if image cannot be decoded, ignore parsing and keep it by its path
2421 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002422 // FIXME we should only enter this function if the image is embedded. If
2423 // image->uri references
2424 // an image file, it should be left as it is. Image loading should not be
2425 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002426 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002427 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002428 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002429 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002430 (*err) +=
2431 "Unknown image format. STB cannot decode image data for image[" +
2432 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002433 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002434 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002435 }
2436
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002437 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002438 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002439 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002440 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2441 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002442 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002443 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002444 }
2445
2446 if (req_width > 0) {
2447 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002448 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002449 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002450 (*err) += "Image width mismatch for image[" +
2451 std::to_string(image_idx) + "] name = \"" + image->name +
2452 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002453 }
2454 return false;
2455 }
2456 }
2457
2458 if (req_height > 0) {
2459 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002460 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002461 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002462 (*err) += "Image height mismatch. for image[" +
2463 std::to_string(image_idx) + "] name = \"" + image->name +
2464 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002465 }
2466 return false;
2467 }
2468 }
2469
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002470 if (req_comp != 0) {
2471 // loaded data has `req_comp` channels(components)
2472 comp = req_comp;
2473 }
2474
Syoyo Fujitabeded612016-05-01 20:03:43 +09002475 image->width = w;
2476 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002477 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002478 image->bits = bits;
2479 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002480 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2481 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002482 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002483
Syoyo Fujitabeded612016-05-01 20:03:43 +09002484 return true;
2485}
Squareys2d3594d2018-03-13 22:40:53 +01002486#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002487
johan bowald642a3432018-04-01 12:37:18 +02002488void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2489 WriteImageData = func;
2490 write_image_user_data_ = user_data;
2491}
2492
2493#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2494static void WriteToMemory_stbi(void *context, void *data, int size) {
2495 std::vector<unsigned char> *buffer =
2496 reinterpret_cast<std::vector<unsigned char> *>(context);
2497
2498 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2499
2500 buffer->insert(buffer->end(), pData, pData + size);
2501}
2502
2503bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002504 const Image *image, bool embedImages, std::string *out_uri,
2505 void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002506 const std::string ext = GetFilePathExtension(*filename);
2507
Paolo Jovone6601bf2018-07-07 20:43:33 +02002508 // Write image to temporary buffer
2509 std::string header;
2510 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002511
Paolo Jovone6601bf2018-07-07 20:43:33 +02002512 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002513 if ((image->bits != 8) ||
2514 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002515 // Unsupported pixel format
2516 return false;
2517 }
2518
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002519 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002520 image->height, image->component,
2521 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002522 return false;
2523 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002524 header = "data:image/png;base64,";
2525 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002526 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002527 image->height, image->component,
2528 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002529 return false;
2530 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002531 header = "data:image/jpeg;base64,";
2532 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002533 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002534 image->height, image->component,
2535 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002536 return false;
2537 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002538 header = "data:image/bmp;base64,";
2539 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002540 // Error: can't output requested format to file
2541 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002542 }
johan bowald642a3432018-04-01 12:37:18 +02002543
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002544 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002545 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002546 if (data.size()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002547 *out_uri = header +
2548 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
johan bowald642a3432018-04-01 12:37:18 +02002549 } else {
2550 // Throw error?
2551 }
2552 } else {
2553 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002554 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002555 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002556 const std::string imagefilepath = JoinPath(*basepath, *filename);
2557 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002558 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2559 fs->user_data)) {
2560 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002561 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002562 }
johan bowald642a3432018-04-01 12:37:18 +02002563 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002564 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002565 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002566 *out_uri = *filename;
johan bowald642a3432018-04-01 12:37:18 +02002567 }
2568
2569 return true;
2570}
2571#endif
2572
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002573void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002574
Harokyangfb256602019-10-30 16:13:52 +08002575#ifdef _WIN32
2576static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002577 int wstr_size =
2578 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
imallett56e10982022-10-07 10:35:16 -07002579 std::wstring wstr((size_t)wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002580 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2581 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002582 return wstr;
2583}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002584
2585static inline std::string WcharToUTF8(const std::wstring &wstr) {
2586 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
imallett56e10982022-10-07 10:35:16 -07002587 nullptr, 0, nullptr, nullptr);
2588 std::string str((size_t)str_size, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002589 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
imallett56e10982022-10-07 10:35:16 -07002590 (int)str.size(), nullptr, nullptr);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002591 return str;
2592}
Harokyangfb256602019-10-30 16:13:52 +08002593#endif
2594
Paolo Jovone6601bf2018-07-07 20:43:33 +02002595#ifndef TINYGLTF_NO_FS
2596// Default implementations of filesystem functions
2597
2598bool FileExists(const std::string &abs_filename, void *) {
2599 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002600#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002601 if (asset_manager) {
2602 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2603 AASSET_MODE_STREAMING);
2604 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002605 return false;
2606 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002607 AAsset_close(asset);
2608 ret = true;
2609 } else {
2610 return false;
2611 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002612#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002613#ifdef _WIN32
operatios1668d1e2022-09-24 22:37:14 +03002614#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
Syoyo Fujita45cac782019-11-09 20:42:55 +09002615 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002616 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002617 if (err != 0) {
2618 return false;
2619 }
2620#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002621 FILE *fp = nullptr;
2622 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2623 if (err != 0) {
2624 return false;
2625 }
2626#endif
2627
2628#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002629 FILE *fp = fopen(abs_filename.c_str(), "rb");
2630#endif
2631 if (fp) {
2632 ret = true;
2633 fclose(fp);
2634 } else {
2635 ret = false;
2636 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002637#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002638
2639 return ret;
2640}
2641
2642std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002643 // https://github.com/syoyo/tinygltf/issues/368
2644 //
2645 // No file path expansion in built-in FS function anymore, since glTF URI
2646 // should not contain tilde('~') and environment variables, and for security
2647 // reason(`wordexp`).
2648 //
2649 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2650 // `LoadBinaryFromMemory`) in expanded absolute path.
2651
2652 return filepath;
2653
2654#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002655#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002656 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002657 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002658 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002659 wchar_t *wstr = new wchar_t[wlen];
2660 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002661
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002662 std::wstring ws(wstr);
2663 delete[] wstr;
2664 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002665
Paolo Jovone6601bf2018-07-07 20:43:33 +02002666#else
2667
2668#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002669 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002670 // no expansion
2671 std::string s = filepath;
2672#else
2673 std::string s;
2674 wordexp_t p;
2675
2676 if (filepath.empty()) {
2677 return "";
2678 }
2679
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002680 // Quote the string to keep any spaces in filepath intact.
2681 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002682 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002683 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002684 if (ret) {
2685 // err
2686 s = filepath;
2687 return s;
2688 }
2689
2690 // Use first element only.
2691 if (p.we_wordv) {
2692 s = std::string(p.we_wordv[0]);
2693 wordfree(&p);
2694 } else {
2695 s = filepath;
2696 }
2697
2698#endif
2699
2700 return s;
2701#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002702#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002703}
2704
2705bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2706 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002707#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2708 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002709 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2710 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002711 if (!asset) {
2712 if (err) {
2713 (*err) += "File open error : " + filepath + "\n";
2714 }
2715 return false;
2716 }
2717 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002718 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002719 if (err) {
2720 (*err) += "Invalid file size : " + filepath +
2721 " (does the path point to a directory?)";
2722 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002723 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002724 }
2725 out->resize(size);
2726 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2727 AAsset_close(asset);
2728 return true;
2729 } else {
2730 if (err) {
2731 (*err) += "No asset manager specified : " + filepath + "\n";
2732 }
2733 return false;
2734 }
2735#else
Harokyang5cecef22019-10-30 15:16:46 +08002736#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002737#if defined(__GLIBCXX__) // mingw
2738 int file_descriptor =
2739 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002740 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2741 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002742#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002743 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2744 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002745 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002746#else
2747 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002748 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2749#endif
Harokyang5cecef22019-10-30 15:16:46 +08002750#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002751 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002752#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002753 if (!f) {
2754 if (err) {
2755 (*err) += "File open error : " + filepath + "\n";
2756 }
2757 return false;
2758 }
2759
2760 f.seekg(0, f.end);
2761 size_t sz = static_cast<size_t>(f.tellg());
2762 f.seekg(0, f.beg);
2763
Syoyo Fujitae8862472019-10-20 17:47:50 +09002764 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002765 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002766 (*err) += "Invalid file size : " + filepath +
2767 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002768 }
2769 return false;
2770 } else if (sz == 0) {
2771 if (err) {
2772 (*err) += "File is empty : " + filepath + "\n";
2773 }
2774 return false;
2775 }
2776
2777 out->resize(sz);
2778 f.read(reinterpret_cast<char *>(&out->at(0)),
2779 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002780
2781 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002782#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002783}
2784
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002785bool WriteWholeFile(std::string *err, const std::string &filepath,
2786 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002787#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002788#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002789 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2790 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2791 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2792 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002793 std::ostream f(&wfile_buf);
2794#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002795 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002796#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002797 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2798#endif
Harokyangfb256602019-10-30 16:13:52 +08002799#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002800 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002801#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002802 if (!f) {
2803 if (err) {
2804 (*err) += "File open error for writing : " + filepath + "\n";
2805 }
2806 return false;
2807 }
2808
2809 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2810 static_cast<std::streamsize>(contents.size()));
2811 if (!f) {
2812 if (err) {
2813 (*err) += "File write error: " + filepath + "\n";
2814 }
2815 return false;
2816 }
2817
Paolo Jovone6601bf2018-07-07 20:43:33 +02002818 return true;
2819}
2820
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002821#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002822
johan bowald642a3432018-04-01 12:37:18 +02002823static std::string MimeToExt(const std::string &mimeType) {
2824 if (mimeType == "image/jpeg") {
2825 return "jpg";
2826 } else if (mimeType == "image/png") {
2827 return "png";
2828 } else if (mimeType == "image/bmp") {
2829 return "bmp";
2830 } else if (mimeType == "image/gif") {
2831 return "gif";
2832 }
2833
2834 return "";
2835}
2836
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002837static void UpdateImageObject(const Image &image, std::string &baseDir,
2838 int index, bool embedImages,
2839 WriteImageDataFunction *WriteImageData,
2840 std::string *out_uri, void *user_data) {
johan bowald642a3432018-04-01 12:37:18 +02002841 std::string filename;
2842 std::string ext;
imallettd9ce9eb2022-10-07 10:37:09 -07002843 // If image has uri, use it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002844 if (image.uri.size()) {
2845 filename = GetBaseFilename(image.uri);
2846 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002847 } else if (image.bufferView != -1) {
2848 // If there's no URI and the data exists in a buffer,
2849 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002850 } else if (image.name.size()) {
2851 ext = MimeToExt(image.mimeType);
2852 // Otherwise use name as filename
2853 filename = image.name + "." + ext;
2854 } else {
2855 ext = MimeToExt(image.mimeType);
2856 // Fallback to index of image as filename
2857 filename = std::to_string(index) + "." + ext;
2858 }
2859
2860 // If callback is set, modify image data object
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002861 bool imageWritten = false;
FsiGuy00015623db855c62020-03-09 16:57:21 -05002862 if (*WriteImageData != nullptr && !filename.empty()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002863 imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
2864 out_uri, user_data);
2865 }
2866
2867 // Use the original uri if the image was not written.
2868 if (!imageWritten) {
2869 *out_uri = image.uri;
johan bowald642a3432018-04-01 12:37:18 +02002870 }
2871}
2872
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002873bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002874 std::string header = "data:application/octet-stream;base64,";
2875 if (in.find(header) == 0) {
2876 return true;
2877 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002878
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002879 header = "data:image/jpeg;base64,";
2880 if (in.find(header) == 0) {
2881 return true;
2882 }
Squareys43374632018-03-13 22:20:48 +01002883
Syoyo Fujita620eed12016-01-02 23:37:12 +09002884 header = "data:image/png;base64,";
2885 if (in.find(header) == 0) {
2886 return true;
2887 }
Squareys43374632018-03-13 22:20:48 +01002888
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002889 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002890 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002891 return true;
2892 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002893
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002894 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002895 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002896 return true;
2897 }
2898
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002899 header = "data:text/plain;base64,";
2900 if (in.find(header) == 0) {
2901 return true;
2902 }
2903
Syoyo Fujita20244e12018-03-15 11:01:05 -05002904 header = "data:application/gltf-buffer;base64,";
2905 if (in.find(header) == 0) {
2906 return true;
2907 }
2908
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002909 return false;
2910}
2911
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002912bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2913 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002914 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002915 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002916 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002917 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002918 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002919
2920 if (data.empty()) {
2921 header = "data:image/jpeg;base64,";
2922 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002923 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002924 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002925 }
2926 }
2927
2928 if (data.empty()) {
2929 header = "data:image/png;base64,";
2930 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002931 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002932 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002933 }
2934 }
Squareys43374632018-03-13 22:20:48 +01002935
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002936 if (data.empty()) {
2937 header = "data:image/bmp;base64,";
2938 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002939 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002940 data = base64_decode(in.substr(header.size())); // cut mime string.
2941 }
2942 }
2943
2944 if (data.empty()) {
2945 header = "data:image/gif;base64,";
2946 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002947 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002948 data = base64_decode(in.substr(header.size())); // cut mime string.
2949 }
2950 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002951
2952 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002953 header = "data:text/plain;base64,";
2954 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002955 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002956 data = base64_decode(in.substr(header.size()));
2957 }
2958 }
2959
2960 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002961 header = "data:application/gltf-buffer;base64,";
2962 if (in.find(header) == 0) {
2963 data = base64_decode(in.substr(header.size()));
2964 }
2965 }
2966
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002967 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002968 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002969 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002970 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002971
2972 if (checkSize) {
2973 if (data.size() != reqBytes) {
2974 return false;
2975 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002976 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002977 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002978 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002979 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002980 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002981 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002982}
2983
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002984namespace {
2985bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002986#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002987 if (!o.IsDouble()) {
2988 if (o.IsInt()) {
2989 val = o.GetInt();
2990 return true;
2991 } else if (o.IsUint()) {
2992 val = static_cast<int>(o.GetUint());
2993 return true;
2994 } else if (o.IsInt64()) {
2995 val = static_cast<int>(o.GetInt64());
2996 return true;
2997 } else if (o.IsUint64()) {
2998 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002999 return true;
3000 }
jrkoonce5cecc412019-08-29 11:45:04 -05003001 }
3002
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003003 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003004#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003005 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05003006
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003007 if ((type == json::value_t::number_integer) ||
3008 (type == json::value_t::number_unsigned)) {
3009 val = static_cast<int>(o.get<int64_t>());
3010 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05003011 }
3012
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003013 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003014#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05003015}
3016
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003017#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003018bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003019 if (o.IsDouble()) {
3020 val = o.GetDouble();
3021 return true;
3022 }
3023
3024 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003025}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003026#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003027
3028bool GetNumber(const json &o, double &val) {
3029#ifdef TINYGLTF_USE_RAPIDJSON
3030 if (o.IsNumber()) {
3031 val = o.GetDouble();
3032 return true;
3033 }
3034
3035 return false;
3036#else
3037 if (o.is_number()) {
3038 val = o.get<double>();
3039 return true;
3040 }
3041
3042 return false;
3043#endif
3044}
3045
3046bool GetString(const json &o, std::string &val) {
3047#ifdef TINYGLTF_USE_RAPIDJSON
3048 if (o.IsString()) {
3049 val = o.GetString();
3050 return true;
3051 }
3052
3053 return false;
3054#else
3055 if (o.type() == json::value_t::string) {
3056 val = o.get<std::string>();
3057 return true;
3058 }
3059
3060 return false;
3061#endif
3062}
3063
3064bool IsArray(const json &o) {
3065#ifdef TINYGLTF_USE_RAPIDJSON
3066 return o.IsArray();
3067#else
3068 return o.is_array();
3069#endif
3070}
3071
3072json_const_array_iterator ArrayBegin(const json &o) {
3073#ifdef TINYGLTF_USE_RAPIDJSON
3074 return o.Begin();
3075#else
3076 return o.begin();
3077#endif
3078}
3079
3080json_const_array_iterator ArrayEnd(const json &o) {
3081#ifdef TINYGLTF_USE_RAPIDJSON
3082 return o.End();
3083#else
3084 return o.end();
3085#endif
3086}
3087
3088bool IsObject(const json &o) {
3089#ifdef TINYGLTF_USE_RAPIDJSON
3090 return o.IsObject();
3091#else
3092 return o.is_object();
3093#endif
3094}
3095
3096json_const_iterator ObjectBegin(const json &o) {
3097#ifdef TINYGLTF_USE_RAPIDJSON
3098 return o.MemberBegin();
3099#else
3100 return o.begin();
3101#endif
3102}
3103
3104json_const_iterator ObjectEnd(const json &o) {
3105#ifdef TINYGLTF_USE_RAPIDJSON
3106 return o.MemberEnd();
3107#else
3108 return o.end();
3109#endif
3110}
3111
Rahul Sheth01d54382020-07-10 14:27:37 -04003112// Making this a const char* results in a pointer to a temporary when
3113// TINYGLTF_USE_RAPIDJSON is off.
3114std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003115#ifdef TINYGLTF_USE_RAPIDJSON
3116 return it->name.GetString();
3117#else
3118 return it.key().c_str();
3119#endif
3120}
3121
3122bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3123#ifdef TINYGLTF_USE_RAPIDJSON
3124 if (!o.IsObject()) {
3125 return false;
3126 }
3127 it = o.FindMember(member);
3128 return it != o.MemberEnd();
3129#else
3130 it = o.find(member);
3131 return it != o.end();
3132#endif
3133}
3134
3135const json &GetValue(json_const_iterator &it) {
3136#ifdef TINYGLTF_USE_RAPIDJSON
3137 return it->value;
3138#else
3139 return it.value();
3140#endif
3141}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003142
3143std::string JsonToString(const json &o, int spacing = -1) {
3144#ifdef TINYGLTF_USE_RAPIDJSON
3145 using namespace rapidjson;
3146 StringBuffer buffer;
3147 if (spacing == -1) {
3148 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003149 // TODO: Better error handling.
3150 // https://github.com/syoyo/tinygltf/issues/332
3151 if (!o.Accept(writer)) {
3152 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3153 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003154 } else {
3155 PrettyWriter<StringBuffer> writer(buffer);
3156 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003157 if (!o.Accept(writer)) {
3158 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3159 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003160 }
3161 return buffer.GetString();
3162#else
3163 return o.dump(spacing);
3164#endif
3165}
3166
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003167} // namespace
3168
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003169static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003170 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003171#ifdef TINYGLTF_USE_RAPIDJSON
3172 using rapidjson::Type;
3173 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003174 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003175 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003176 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003177 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003178 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003179 if (entry.Type() != NULL_TYPE)
3180 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003181 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003182 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003183 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003184 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003185 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003186 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003187 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003188 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003189 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003190 if (entry.Type() != NULL_TYPE)
3191 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003192 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003193 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003194 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003195 case Type::kStringType:
3196 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003197 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003198 case Type::kFalseType:
3199 case Type::kTrueType:
3200 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003201 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003202 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003203 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003204 int i = 0;
3205 GetInt(o, i);
3206 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003207 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003208 double d = 0.0;
3209 GetDouble(o, d);
3210 val = Value(d);
3211 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003212 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003213 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003214 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003215 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003216 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003217#else
3218 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003219 case json::value_t::object: {
3220 Value::Object value_object;
3221 for (auto it = o.begin(); it != o.end(); it++) {
3222 Value entry;
3223 ParseJsonAsValue(&entry, it.value());
3224 if (entry.Type() != NULL_TYPE)
3225 value_object.emplace(it.key(), std::move(entry));
3226 }
3227 if (value_object.size() > 0) val = Value(std::move(value_object));
3228 } break;
3229 case json::value_t::array: {
3230 Value::Array value_array;
3231 value_array.reserve(o.size());
3232 for (auto it = o.begin(); it != o.end(); it++) {
3233 Value entry;
3234 ParseJsonAsValue(&entry, it.value());
3235 if (entry.Type() != NULL_TYPE)
3236 value_array.emplace_back(std::move(entry));
3237 }
3238 if (value_array.size() > 0) val = Value(std::move(value_array));
3239 } break;
3240 case json::value_t::string:
3241 val = Value(o.get<std::string>());
3242 break;
3243 case json::value_t::boolean:
3244 val = Value(o.get<bool>());
3245 break;
3246 case json::value_t::number_integer:
3247 case json::value_t::number_unsigned:
3248 val = Value(static_cast<int>(o.get<int64_t>()));
3249 break;
3250 case json::value_t::number_float:
3251 val = Value(o.get<double>());
3252 break;
3253 case json::value_t::null:
3254 case json::value_t::discarded:
Christopher Sean Morrison0bfcb4f2022-02-23 10:02:49 -05003255 case json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003256 // default:
3257 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003258 }
3259#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003260 const bool isNotNull = val.Type() != NULL_TYPE;
3261
jrkooncecba5d6c2019-08-29 11:26:22 -05003262 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003263
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003264 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003265}
3266
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003267static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003268 json_const_iterator it;
3269 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003270 return false;
3271 }
3272
jrkooncecba5d6c2019-08-29 11:26:22 -05003273 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003274}
3275
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003276static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003277 const std::string &property,
3278 const bool required,
3279 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003280 json_const_iterator it;
3281 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003282 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003283 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003284 (*err) += "'" + property + "' property is missing";
3285 if (!parent_node.empty()) {
3286 (*err) += " in " + parent_node;
3287 }
3288 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003289 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003290 }
3291 return false;
3292 }
3293
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003294 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003295
3296 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003297 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003298#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003299 isBoolean = value.IsBool();
3300 if (isBoolean) {
3301 boolValue = value.GetBool();
3302 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003303#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003304 isBoolean = value.is_boolean();
3305 if (isBoolean) {
3306 boolValue = value.get<bool>();
3307 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003308#endif
3309 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003310 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003311 if (err) {
3312 (*err) += "'" + property + "' property is not a bool type.\n";
3313 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003314 }
3315 return false;
3316 }
3317
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003318 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003319 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003320 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003321
3322 return true;
3323}
3324
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003325static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3326 const std::string &property,
3327 const bool required,
3328 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003329 json_const_iterator it;
3330 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003331 if (required) {
3332 if (err) {
3333 (*err) += "'" + property + "' property is missing";
3334 if (!parent_node.empty()) {
3335 (*err) += " in " + parent_node;
3336 }
3337 (*err) += ".\n";
3338 }
3339 }
3340 return false;
3341 }
3342
jrkooncecba5d6c2019-08-29 11:26:22 -05003343 int intValue;
3344 bool isInt = GetInt(GetValue(it), intValue);
3345 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003346 if (required) {
3347 if (err) {
3348 (*err) += "'" + property + "' property is not an integer type.\n";
3349 }
3350 }
3351 return false;
3352 }
3353
3354 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003355 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003356 }
3357
3358 return true;
3359}
3360
3361static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3362 const std::string &property,
3363 const bool required,
3364 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003365 json_const_iterator it;
3366 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003367 if (required) {
3368 if (err) {
3369 (*err) += "'" + property + "' property is missing";
3370 if (!parent_node.empty()) {
3371 (*err) += " in " + parent_node;
3372 }
3373 (*err) += ".\n";
3374 }
3375 }
3376 return false;
3377 }
3378
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003379 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003380
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003381 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003382 bool isUValue;
3383#ifdef TINYGLTF_USE_RAPIDJSON
3384 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003385 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003386 uValue = value.GetUint();
3387 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003388 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003389 uValue = value.GetUint64();
3390 isUValue = true;
3391 }
3392#else
3393 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003394 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003395 uValue = value.get<size_t>();
3396 }
3397#endif
3398 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003399 if (required) {
3400 if (err) {
3401 (*err) += "'" + property + "' property is not a positive integer.\n";
3402 }
3403 }
3404 return false;
3405 }
3406
3407 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003408 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003409 }
3410
3411 return true;
3412}
3413
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003414static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003415 const std::string &property,
3416 const bool required,
3417 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003418 json_const_iterator it;
3419
3420 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003421 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003422 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003423 (*err) += "'" + property + "' property is missing";
3424 if (!parent_node.empty()) {
3425 (*err) += " in " + parent_node;
3426 }
3427 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003428 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003429 }
3430 return false;
3431 }
3432
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003433 double numberValue;
3434 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003435
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003436 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003437 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003438 if (err) {
3439 (*err) += "'" + property + "' property is not a number type.\n";
3440 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003441 }
3442 return false;
3443 }
3444
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003445 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003446 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003447 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003448
3449 return true;
3450}
3451
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003452static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003453 const json &o, const std::string &property,
3454 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003455 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003456 json_const_iterator it;
3457 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003458 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003459 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003460 (*err) += "'" + property + "' property is missing";
3461 if (!parent_node.empty()) {
3462 (*err) += " in " + parent_node;
3463 }
3464 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003465 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003466 }
3467 return false;
3468 }
3469
jrkooncecba5d6c2019-08-29 11:26:22 -05003470 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003471 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003472 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003473 (*err) += "'" + property + "' property is not an array";
3474 if (!parent_node.empty()) {
3475 (*err) += " in " + parent_node;
3476 }
3477 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003478 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003479 }
3480 return false;
3481 }
3482
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003483 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003484 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003485 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003486 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003487 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003488 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003489 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003490 if (err) {
3491 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003492 if (!parent_node.empty()) {
3493 (*err) += " in " + parent_node;
3494 }
3495 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003496 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003497 }
3498 return false;
3499 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003500 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003501 }
3502
3503 return true;
3504}
3505
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003506static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3507 const json &o,
3508 const std::string &property,
3509 bool required,
3510 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003511 json_const_iterator it;
3512 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003513 if (required) {
3514 if (err) {
3515 (*err) += "'" + property + "' property is missing";
3516 if (!parent_node.empty()) {
3517 (*err) += " in " + parent_node;
3518 }
3519 (*err) += ".\n";
3520 }
3521 }
3522 return false;
3523 }
3524
jrkooncecba5d6c2019-08-29 11:26:22 -05003525 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003526 if (required) {
3527 if (err) {
3528 (*err) += "'" + property + "' property is not an array";
3529 if (!parent_node.empty()) {
3530 (*err) += " in " + parent_node;
3531 }
3532 (*err) += ".\n";
3533 }
3534 }
3535 return false;
3536 }
3537
3538 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003539 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003540 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003541 int numberValue;
3542 bool isNumber = GetInt(*i, numberValue);
3543 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003544 if (required) {
3545 if (err) {
3546 (*err) += "'" + property + "' property is not an integer type.\n";
3547 if (!parent_node.empty()) {
3548 (*err) += " in " + parent_node;
3549 }
3550 (*err) += ".\n";
3551 }
3552 }
3553 return false;
3554 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003555 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003556 }
3557
3558 return true;
3559}
3560
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003561static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003562 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003563 const std::string &property, bool required,
3564 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003565 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003566 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003567 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003568 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003569 (*err) += "'" + property + "' property is missing";
3570 if (parent_node.empty()) {
3571 (*err) += ".\n";
3572 } else {
3573 (*err) += " in `" + parent_node + "'.\n";
3574 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003575 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003576 }
3577 return false;
3578 }
3579
jrkooncecba5d6c2019-08-29 11:26:22 -05003580 std::string strValue;
3581 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003582 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003583 if (err) {
3584 (*err) += "'" + property + "' property is not a string type.\n";
3585 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003586 }
3587 return false;
3588 }
3589
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003590 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003591 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003592 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003593
3594 return true;
3595}
3596
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003597static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3598 std::string *err, const json &o,
3599 const std::string &property,
3600 bool required,
3601 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003602 json_const_iterator it;
3603 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003604 if (required) {
3605 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003606 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003607 (*err) +=
3608 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003609 } else {
3610 (*err) += "'" + property + "' property is missing.\n";
3611 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003612 }
3613 }
3614 return false;
3615 }
3616
jrkooncecba5d6c2019-08-29 11:26:22 -05003617 const json &dict = GetValue(it);
3618
Luke San Antonio19894c72016-06-14 21:19:51 -04003619 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003620 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003621 if (required) {
3622 if (err) {
3623 (*err) += "'" + property + "' property is not an object.\n";
3624 }
3625 }
3626 return false;
3627 }
3628
3629 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003630
jrkooncecba5d6c2019-08-29 11:26:22 -05003631 json_const_iterator dictIt(ObjectBegin(dict));
3632 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003633
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003634 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003635 int intVal;
3636 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003637 if (required) {
3638 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003639 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003640 }
3641 }
3642 return false;
3643 }
3644
3645 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003646 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003647 }
3648 return true;
3649}
3650
Syoyo Fujita5b407452017-06-04 17:42:41 +09003651static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003652 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003653 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003654 json_const_iterator it;
3655 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003656 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003657 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003658 (*err) += "'" + property + "' property is missing. \n'";
3659 }
3660 }
3661 return false;
3662 }
3663
jrkooncecba5d6c2019-08-29 11:26:22 -05003664 const json &obj = GetValue(it);
3665
3666 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003667 if (required) {
3668 if (err) {
3669 (*err) += "'" + property + "' property is not a JSON object.\n";
3670 }
3671 }
3672 return false;
3673 }
3674
3675 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003676
jrkooncecba5d6c2019-08-29 11:26:22 -05003677 json_const_iterator it2(ObjectBegin(obj));
3678 json_const_iterator itEnd(ObjectEnd(obj));
3679 for (; it2 != itEnd; ++it2) {
3680 double numVal;
3681 if (GetNumber(GetValue(it2), numVal))
3682 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003683 }
3684
3685 return true;
3686}
3687
Selmar09d2ff12018-03-15 17:30:42 +01003688static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003689 const json &o, const std::string &prop,
3690 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003691 // A parameter value can either be a string or an array of either a boolean or
3692 // a number. Booleans of any kind aren't supported here. Granted, it
3693 // complicates the Parameter structure and breaks it semantically in the sense
3694 // that the client probably works off the assumption that if the string is
3695 // empty the vector is used, etc. Would a tagged union work?
3696 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3697 // Found string property.
3698 return true;
3699 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3700 false)) {
3701 // Found a number array.
3702 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003703 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02003704 param->has_number_value = true;
3705 return true;
Selmar09d2ff12018-03-15 17:30:42 +01003706 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3707 false)) {
3708 return true;
3709 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3710 return true;
3711 } else {
3712 if (required) {
3713 if (err) {
3714 (*err) += "parameter must be a string or number / number array.\n";
3715 }
3716 }
3717 return false;
3718 }
3719}
3720
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003721static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3722 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003723 (void)err;
3724
jrkooncecba5d6c2019-08-29 11:26:22 -05003725 json_const_iterator it;
3726 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003727 return false;
3728 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003729
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003730 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003731 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003732 return false;
3733 }
3734 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003735 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003736 json_const_iterator extEnd = ObjectEnd(obj);
3737 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003738 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003739 if (!IsObject(itObj)) continue;
3740 std::string key(GetKey(extIt));
3741 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003742 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003743 // create empty object so that an extension object is still of type
3744 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003745 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003746 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003747 }
Selmar09d2ff12018-03-15 17:30:42 +01003748 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003749 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003750 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003751 }
3752 return true;
3753}
3754
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003755static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3756 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003757 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3758 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3759 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003760 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003761
Selmar09d2ff12018-03-15 17:30:42 +01003762 ParseExtensionsProperty(&asset->extensions, err, o);
3763
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003764 // Unity exporter version is added as extra here
3765 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003766
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003767 if (store_original_json_for_extras_and_extensions) {
3768 {
3769 json_const_iterator it;
3770 if (FindMember(o, "extensions", it)) {
3771 asset->extensions_json_string = JsonToString(GetValue(it));
3772 }
3773 }
3774 {
3775 json_const_iterator it;
3776 if (FindMember(o, "extras", it)) {
3777 asset->extras_json_string = JsonToString(GetValue(it));
3778 }
3779 }
3780 }
3781
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003782 return true;
3783}
3784
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003785static bool ParseImage(Image *image, const int image_idx, std::string *err,
3786 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003787 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003788 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003789 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003790 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003791 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003792
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003793 // schema says oneOf [`bufferView`, `uri`]
3794 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003795 json_const_iterator it;
3796 bool hasBufferView = FindMember(o, "bufferView", it);
3797 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003798
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003799 ParseStringProperty(&image->name, err, o, "name", false);
3800
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003801 if (hasBufferView && hasURI) {
3802 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003803 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003804 (*err) +=
3805 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003806 "defined for image[" +
3807 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003808 }
3809 return false;
3810 }
3811
3812 if (!hasBufferView && !hasURI) {
3813 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003814 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3815 std::to_string(image_idx) + "] name = \"" + image->name +
3816 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003817 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003818 return false;
3819 }
3820
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003821 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003822 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003823
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003824 if (store_original_json_for_extras_and_extensions) {
3825 {
3826 json_const_iterator eit;
3827 if (FindMember(o, "extensions", eit)) {
3828 image->extensions_json_string = JsonToString(GetValue(eit));
3829 }
3830 }
3831 {
3832 json_const_iterator eit;
3833 if (FindMember(o, "extras", eit)) {
3834 image->extras_json_string = JsonToString(GetValue(eit));
3835 }
3836 }
3837 }
3838
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003839 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003840 int bufferView = -1;
3841 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003842 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003843 (*err) += "Failed to parse `bufferView` for image[" +
3844 std::to_string(image_idx) + "] name = \"" + image->name +
3845 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003846 }
3847 return false;
3848 }
3849
3850 std::string mime_type;
3851 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3852
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003853 int width = 0;
3854 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003855
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003856 int height = 0;
3857 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003858
3859 // Just only save some information here. Loading actual image data from
3860 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003861 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003862 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003863 image->width = width;
3864 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003865
3866 return true;
3867 }
3868
Syoyo Fujita246654a2018-03-21 20:32:22 +09003869 // Parse URI & Load image data.
3870
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003871 std::string uri;
3872 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003873 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3874 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003875 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3876 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003877 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003878 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003879 }
3880
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003881 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003882
Syoyo Fujita246654a2018-03-21 20:32:22 +09003883 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003884 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003885 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003886 (*err) += "Failed to decode 'uri' for image[" +
3887 std::to_string(image_idx) + "] name = [" + image->name +
3888 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003889 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003890 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003891 }
3892 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003893 // Assume external file
3894 // Keep texture path (for textures that cannot be decoded)
3895 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003896#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003897 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01003898#else
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003899 std::string decoded_uri = dlib::urldecode(uri);
3900 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3901 /* required */ false, /* required bytes */ 0,
3902 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003903 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003904 (*warn) += "Failed to load external 'uri' for image[" +
3905 std::to_string(image_idx) + "] name = [" + image->name +
3906 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003907 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003908 // If the image cannot be loaded, keep uri as image->uri.
3909 return true;
3910 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003911
Syoyo Fujita246654a2018-03-21 20:32:22 +09003912 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003913 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003914 (*warn) += "Image data is empty for image[" +
3915 std::to_string(image_idx) + "] name = [" + image->name +
3916 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003917 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003918 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003919 }
AlvaroBarua43172232022-09-11 00:41:43 +01003920#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003921 }
3922
Squareysff644d82018-03-13 22:36:18 +01003923 if (*LoadImageData == nullptr) {
3924 if (err) {
3925 (*err) += "No LoadImageData callback specified.\n";
3926 }
3927 return false;
3928 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003929 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003930 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003931}
3932
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003933static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003934 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003935 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003936 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003937 int sampler = -1;
3938 int source = -1;
3939 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003940
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003941 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003942
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003943 texture->sampler = sampler;
3944 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003945
Selmar Kokfa7022f2018-04-04 18:10:20 +02003946 ParseExtensionsProperty(&texture->extensions, err, o);
3947 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003948
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003949 if (store_original_json_for_extras_and_extensions) {
3950 {
3951 json_const_iterator it;
3952 if (FindMember(o, "extensions", it)) {
3953 texture->extensions_json_string = JsonToString(GetValue(it));
3954 }
3955 }
3956 {
3957 json_const_iterator it;
3958 if (FindMember(o, "extras", it)) {
3959 texture->extras_json_string = JsonToString(GetValue(it));
3960 }
3961 }
3962 }
3963
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003964 ParseStringProperty(&texture->name, err, o, "name", false);
3965
Syoyo Fujitabde70212016-02-07 17:38:17 +09003966 return true;
3967}
3968
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003969static bool ParseTextureInfo(
3970 TextureInfo *texinfo, std::string *err, const json &o,
3971 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003972 if (texinfo == nullptr) {
3973 return false;
3974 }
3975
3976 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3977 /* required */ true, "TextureInfo")) {
3978 return false;
3979 }
3980
3981 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3982
3983 ParseExtensionsProperty(&texinfo->extensions, err, o);
3984 ParseExtrasProperty(&texinfo->extras, o);
3985
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003986 if (store_original_json_for_extras_and_extensions) {
3987 {
3988 json_const_iterator it;
3989 if (FindMember(o, "extensions", it)) {
3990 texinfo->extensions_json_string = JsonToString(GetValue(it));
3991 }
3992 }
3993 {
3994 json_const_iterator it;
3995 if (FindMember(o, "extras", it)) {
3996 texinfo->extras_json_string = JsonToString(GetValue(it));
3997 }
3998 }
3999 }
4000
Syoyo Fujita046400b2019-07-24 19:26:48 +09004001 return true;
4002}
4003
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004004static bool ParseNormalTextureInfo(
4005 NormalTextureInfo *texinfo, std::string *err, const json &o,
4006 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004007 if (texinfo == nullptr) {
4008 return false;
4009 }
4010
4011 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4012 /* required */ true, "NormalTextureInfo")) {
4013 return false;
4014 }
4015
4016 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4017 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4018
4019 ParseExtensionsProperty(&texinfo->extensions, err, o);
4020 ParseExtrasProperty(&texinfo->extras, o);
4021
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004022 if (store_original_json_for_extras_and_extensions) {
4023 {
4024 json_const_iterator it;
4025 if (FindMember(o, "extensions", it)) {
4026 texinfo->extensions_json_string = JsonToString(GetValue(it));
4027 }
4028 }
4029 {
4030 json_const_iterator it;
4031 if (FindMember(o, "extras", it)) {
4032 texinfo->extras_json_string = JsonToString(GetValue(it));
4033 }
4034 }
4035 }
4036
Syoyo Fujita046400b2019-07-24 19:26:48 +09004037 return true;
4038}
4039
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004040static bool ParseOcclusionTextureInfo(
4041 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
4042 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004043 if (texinfo == nullptr) {
4044 return false;
4045 }
4046
4047 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4048 /* required */ true, "NormalTextureInfo")) {
4049 return false;
4050 }
4051
4052 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4053 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4054
4055 ParseExtensionsProperty(&texinfo->extensions, err, o);
4056 ParseExtrasProperty(&texinfo->extras, o);
4057
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004058 if (store_original_json_for_extras_and_extensions) {
4059 {
4060 json_const_iterator it;
4061 if (FindMember(o, "extensions", it)) {
4062 texinfo->extensions_json_string = JsonToString(GetValue(it));
4063 }
4064 }
4065 {
4066 json_const_iterator it;
4067 if (FindMember(o, "extras", it)) {
4068 texinfo->extras_json_string = JsonToString(GetValue(it));
4069 }
4070 }
4071 }
4072
Syoyo Fujita046400b2019-07-24 19:26:48 +09004073 return true;
4074}
4075
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004076static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004077 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004078 FsCallbacks *fs, const std::string &basedir,
4079 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004080 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004081 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004082 size_t byteLength;
4083 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4084 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004085 return false;
4086 }
4087
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004088 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004089 buffer->uri.clear();
4090 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004091
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004092 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004093 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004094 if (err) {
4095 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4096 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004097 }
4098
jrkooncecba5d6c2019-08-29 11:26:22 -05004099 json_const_iterator type;
4100 if (FindMember(o, "type", type)) {
4101 std::string typeStr;
4102 if (GetString(GetValue(type), typeStr)) {
4103 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004104 // buffer.type = "arraybuffer";
4105 }
4106 }
4107 }
4108
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004109 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004110 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004111 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004112 // First try embedded data URI.
4113 if (IsDataURI(buffer->uri)) {
4114 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004115 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004116 true)) {
4117 if (err) {
4118 (*err) +=
4119 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4120 }
4121 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004122 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004123 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004124 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004125 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004126 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004127 decoded_uri, basedir, /* required */ true,
4128 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004129 return false;
4130 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004131 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004132 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004133 // load data from (embedded) binary data
4134
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004135 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004136 if (err) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09004137 (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004138 }
4139 return false;
4140 }
4141
4142 if (byteLength > bin_size) {
4143 if (err) {
4144 std::stringstream ss;
4145 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004146 "`byteLength' = "
4147 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004148 (*err) += ss.str();
4149 }
4150 return false;
4151 }
4152
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004153 // Read buffer data
4154 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004155 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004156 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004157
4158 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004159 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004160 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004161 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4162 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004163 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004164 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004165 }
4166 return false;
4167 }
4168 } else {
4169 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004170 std::string decoded_uri = dlib::urldecode(buffer->uri);
4171 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4172 basedir, /* required */ true, byteLength,
4173 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004174 return false;
4175 }
4176 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004177 }
4178
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004179 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004180
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004181 ParseExtensionsProperty(&buffer->extensions, err, o);
4182 ParseExtrasProperty(&buffer->extras, o);
4183
4184 if (store_original_json_for_extras_and_extensions) {
4185 {
4186 json_const_iterator it;
4187 if (FindMember(o, "extensions", it)) {
4188 buffer->extensions_json_string = JsonToString(GetValue(it));
4189 }
4190 }
4191 {
4192 json_const_iterator it;
4193 if (FindMember(o, "extras", it)) {
4194 buffer->extras_json_string = JsonToString(GetValue(it));
4195 }
4196 }
4197 }
4198
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004199 return true;
4200}
4201
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004202static bool ParseBufferView(
4203 BufferView *bufferView, std::string *err, const json &o,
4204 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004205 int buffer = -1;
4206 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004207 return false;
4208 }
4209
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004210 size_t byteOffset = 0;
4211 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004212
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004213 size_t byteLength = 1;
4214 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4215 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004216 return false;
4217 }
4218
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004219 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004220 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004221 // Spec says: When byteStride of referenced bufferView is not defined, it
4222 // means that accessor elements are tightly packed, i.e., effective stride
4223 // equals the size of the element.
4224 // We cannot determine the actual byteStride until Accessor are parsed, thus
4225 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4226 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004227 }
4228
4229 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4230 if (err) {
4231 std::stringstream ss;
4232 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4233 "4 : "
4234 << byteStride << std::endl;
4235
4236 (*err) += ss.str();
4237 }
4238 return false;
4239 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004240
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004241 int target = 0;
4242 ParseIntegerProperty(&target, err, o, "target", false);
4243 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4244 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004245 // OK
4246 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004247 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004248 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004249 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004250
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004251 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004252
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004253 ParseExtensionsProperty(&bufferView->extensions, err, o);
4254 ParseExtrasProperty(&bufferView->extras, o);
4255
4256 if (store_original_json_for_extras_and_extensions) {
4257 {
4258 json_const_iterator it;
4259 if (FindMember(o, "extensions", it)) {
4260 bufferView->extensions_json_string = JsonToString(GetValue(it));
4261 }
4262 }
4263 {
4264 json_const_iterator it;
4265 if (FindMember(o, "extras", it)) {
4266 bufferView->extras_json_string = JsonToString(GetValue(it));
4267 }
4268 }
4269 }
4270
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004271 bufferView->buffer = buffer;
4272 bufferView->byteOffset = byteOffset;
4273 bufferView->byteLength = byteLength;
4274 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004275 return true;
4276}
4277
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004278static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4279 const json &o) {
4280 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004281
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004282 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004283 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4284 return false;
4285 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004286
jrkooncecba5d6c2019-08-29 11:26:22 -05004287 json_const_iterator indices_iterator;
4288 json_const_iterator values_iterator;
4289 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004290 (*err) = "the sparse object of this accessor doesn't have indices";
4291 return false;
4292 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004293
jrkooncecba5d6c2019-08-29 11:26:22 -05004294 if (!FindMember(o, "values", values_iterator)) {
imallettd9ce9eb2022-10-07 10:37:09 -07004295 (*err) = "the sparse object of this accessor doesn't have values";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004296 return false;
4297 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004298
jrkooncecba5d6c2019-08-29 11:26:22 -05004299 const json &indices_obj = GetValue(indices_iterator);
4300 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004301
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004302 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004303 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4304 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004305 return false;
4306 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004307 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004308 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004309 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004310 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004311 return false;
4312 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004313
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004314 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004315 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004316 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004317 return false;
4318 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004319 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004320 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004321
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004322 accessor->sparse.count = count;
4323 accessor->sparse.indices.bufferView = indices_buffer_view;
4324 accessor->sparse.indices.byteOffset = indices_byte_offset;
4325 accessor->sparse.indices.componentType = component_type;
4326 accessor->sparse.values.bufferView = values_buffer_view;
4327 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004328
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004329 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004330}
4331
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004332static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4333 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004334 int bufferView = -1;
4335 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004336
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004337 size_t byteOffset = 0;
4338 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004339
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004340 bool normalized = false;
4341 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4342
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004343 size_t componentType = 0;
4344 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4345 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004346 return false;
4347 }
4348
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004349 size_t count = 0;
4350 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004351 return false;
4352 }
4353
4354 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004355 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004356 return false;
4357 }
4358
4359 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004360 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004361 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004362 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004363 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004364 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004365 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004366 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004367 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004368 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004369 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004370 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004371 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004372 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004373 } else {
4374 std::stringstream ss;
4375 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004376 if (err) {
4377 (*err) += ss.str();
4378 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004379 return false;
4380 }
4381
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004382 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004383
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004384 accessor->minValues.clear();
4385 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004386 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4387 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004388
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004389 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4390 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004391
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004392 accessor->count = count;
4393 accessor->bufferView = bufferView;
4394 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004395 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004396 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004397 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4398 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004399 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004400 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004401 } else {
4402 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004403 ss << "Invalid `componentType` in accessor. Got " << componentType
4404 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004405 if (err) {
4406 (*err) += ss.str();
4407 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004408 return false;
4409 }
4410 }
4411
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004412 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004413 ParseExtrasProperty(&(accessor->extras), o);
4414
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004415 if (store_original_json_for_extras_and_extensions) {
4416 {
4417 json_const_iterator it;
4418 if (FindMember(o, "extensions", it)) {
4419 accessor->extensions_json_string = JsonToString(GetValue(it));
4420 }
4421 }
4422 {
4423 json_const_iterator it;
4424 if (FindMember(o, "extras", it)) {
4425 accessor->extras_json_string = JsonToString(GetValue(it));
4426 }
4427 }
4428 }
4429
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004430 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004431 json_const_iterator iterator;
4432 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004433 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004434 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004435 }
4436
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004437 return true;
4438}
4439
Alex Wood7319db72019-01-24 15:38:16 -05004440#ifdef TINYGLTF_ENABLE_DRACO
4441
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004442static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4443 std::vector<uint8_t> &outBuffer) {
4444 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004445 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004446 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4447 outBuffer.size());
4448 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004449 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004450 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4451 const draco::Mesh::Face &face = mesh->face(f);
4452 if (componentSize == 2) {
4453 uint16_t indices[3] = {(uint16_t)face[0].value(),
4454 (uint16_t)face[1].value(),
4455 (uint16_t)face[2].value()};
4456 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4457 faceStride);
4458 } else {
4459 uint8_t indices[3] = {(uint8_t)face[0].value(),
4460 (uint8_t)face[1].value(),
4461 (uint8_t)face[2].value()};
4462 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4463 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004464 }
4465 }
4466 }
4467}
4468
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004469template <typename T>
4470static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4471 const draco::PointAttribute *pAttribute,
4472 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004473 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004474 T values[4] = {0, 0, 0, 0};
4475 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004476 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004477 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4478 values))
Alex Wood7319db72019-01-24 15:38:16 -05004479 return false;
4480
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004481 memcpy(outBuffer.data() + byteOffset, &values[0],
4482 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004483 byteOffset += sizeof(T) * pAttribute->num_components();
4484 }
4485
4486 return true;
4487}
4488
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004489static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4490 const draco::PointAttribute *pAttribute,
4491 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004492 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004493 switch (componentType) {
4494 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4495 decodeResult =
4496 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4497 break;
4498 case TINYGLTF_COMPONENT_TYPE_BYTE:
4499 decodeResult =
4500 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4501 break;
4502 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4503 decodeResult =
4504 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4505 break;
4506 case TINYGLTF_COMPONENT_TYPE_SHORT:
4507 decodeResult =
4508 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4509 break;
4510 case TINYGLTF_COMPONENT_TYPE_INT:
4511 decodeResult =
4512 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4513 break;
4514 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4515 decodeResult =
4516 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4517 break;
4518 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4519 decodeResult =
4520 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4521 break;
4522 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4523 decodeResult =
4524 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4525 break;
4526 default:
4527 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004528 }
4529
4530 return decodeResult;
4531}
4532
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004533static bool ParseDracoExtension(Primitive *primitive, Model *model,
4534 std::string *err,
4535 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004536 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004537 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004538 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004539 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004540 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004541
4542 auto attributesObject = attributesValue.Get<Value::Object>();
4543 int bufferView = bufferViewValue.Get<int>();
4544
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004545 BufferView &view = model->bufferViews[bufferView];
4546 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004547 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004548 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004549 view.dracoDecoded = true;
4550
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004551 const char *bufferViewData =
4552 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004553 size_t bufferViewSize = view.byteLength;
4554
4555 // decode draco
4556 draco::DecoderBuffer decoderBuffer;
4557 decoderBuffer.Init(bufferViewData, bufferViewSize);
4558 draco::Decoder decoder;
4559 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4560 if (!decodeResult.ok()) {
4561 return false;
4562 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004563 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004564
4565 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004566 if (primitive->indices >= 0) {
4567 int32_t componentSize = GetComponentSizeInBytes(
4568 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004569 Buffer decodedIndexBuffer;
4570 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4571
4572 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4573
4574 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4575
4576 BufferView decodedIndexBufferView;
4577 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004578 decodedIndexBufferView.byteLength =
4579 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004580 decodedIndexBufferView.byteOffset = 0;
4581 decodedIndexBufferView.byteStride = 0;
4582 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4583 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4584
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004585 model->accessors[primitive->indices].bufferView =
4586 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004587 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004588 }
4589
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004590 for (const auto &attribute : attributesObject) {
4591 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004592 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004593 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004594
4595 int dracoAttributeIndex = attribute.second.Get<int>();
4596 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004597 const auto componentType =
4598 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004599
4600 // Create a new buffer for this decoded buffer
4601 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004602 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4603 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004604 decodedBuffer.data.resize(bufferSize);
4605
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004606 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4607 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004608 return false;
4609
4610 model->buffers.emplace_back(std::move(decodedBuffer));
4611
4612 BufferView decodedBufferView;
4613 decodedBufferView.buffer = int(model->buffers.size() - 1);
4614 decodedBufferView.byteLength = bufferSize;
4615 decodedBufferView.byteOffset = pAttribute->byte_offset();
4616 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004617 decodedBufferView.target = primitive->indices >= 0
4618 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4619 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004620 model->bufferViews.emplace_back(std::move(decodedBufferView));
4621
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004622 model->accessors[primitiveAttribute->second].bufferView =
4623 int(model->bufferViews.size() - 1);
4624 model->accessors[primitiveAttribute->second].count =
4625 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004626 }
4627
4628 return true;
4629}
4630#endif
4631
4632static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004633 const json &o,
4634 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004635 int material = -1;
4636 ParseIntegerProperty(&material, err, o, "material", false);
4637 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004638
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004639 int mode = TINYGLTF_MODE_TRIANGLES;
4640 ParseIntegerProperty(&mode, err, o, "mode", false);
imallettd9ce9eb2022-10-07 10:37:09 -07004641 primitive->mode = mode; // Why only triangles were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004642
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004643 int indices = -1;
4644 ParseIntegerProperty(&indices, err, o, "indices", false);
4645 primitive->indices = indices;
4646 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4647 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004648 return false;
4649 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004650
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004651 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004652 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004653 if (FindMember(o, "targets", targetsObject) &&
4654 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004655 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4656 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4657 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004658 std::map<std::string, int> targetAttribues;
4659
jrkooncecba5d6c2019-08-29 11:26:22 -05004660 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004661 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004662 json_const_iterator dictIt(ObjectBegin(dict));
4663 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004664
jrkooncecba5d6c2019-08-29 11:26:22 -05004665 for (; dictIt != dictItEnd; ++dictIt) {
4666 int iVal;
4667 if (GetInt(GetValue(dictIt), iVal))
4668 targetAttribues[GetKey(dictIt)] = iVal;
4669 }
4670 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004671 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004672 }
4673 }
4674
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004675 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004676 ParseExtensionsProperty(&primitive->extensions, err, o);
4677
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004678 if (store_original_json_for_extras_and_extensions) {
4679 {
4680 json_const_iterator it;
4681 if (FindMember(o, "extensions", it)) {
4682 primitive->extensions_json_string = JsonToString(GetValue(it));
4683 }
4684 }
4685 {
4686 json_const_iterator it;
4687 if (FindMember(o, "extras", it)) {
4688 primitive->extras_json_string = JsonToString(GetValue(it));
4689 }
4690 }
4691 }
4692
Alex Wood7319db72019-01-24 15:38:16 -05004693#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004694 auto dracoExtension =
4695 primitive->extensions.find("KHR_draco_mesh_compression");
4696 if (dracoExtension != primitive->extensions.end()) {
4697 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004698 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004699#else
4700 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004701#endif
4702
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004703 return true;
4704}
4705
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004706static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4707 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004708 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004709
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004710 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004711 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004712 if (FindMember(o, "primitives", primObject) &&
4713 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004714 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4715 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4716 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004717 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004718 if (ParsePrimitive(&primitive, model, err, *i,
4719 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004720 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004721 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004722 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004723 }
4724 }
4725
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004726 // Should probably check if has targets and if dimensions fit
4727 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4728
Selmar09d2ff12018-03-15 17:30:42 +01004729 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004730 ParseExtrasProperty(&(mesh->extras), o);
4731
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004732 if (store_original_json_for_extras_and_extensions) {
4733 {
4734 json_const_iterator it;
4735 if (FindMember(o, "extensions", it)) {
4736 mesh->extensions_json_string = JsonToString(GetValue(it));
4737 }
4738 }
4739 {
4740 json_const_iterator it;
4741 if (FindMember(o, "extras", it)) {
4742 mesh->extras_json_string = JsonToString(GetValue(it));
4743 }
4744 }
4745 }
4746
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004747 return true;
4748}
4749
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004750static bool ParseNode(Node *node, std::string *err, const json &o,
4751 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004752 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004753
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004754 int skin = -1;
4755 ParseIntegerProperty(&skin, err, o, "skin", false);
4756 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004757
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004758 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004759 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004760 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4761 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4762 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4763 }
4764
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004765 int camera = -1;
4766 ParseIntegerProperty(&camera, err, o, "camera", false);
4767 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004768
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004769 int mesh = -1;
4770 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4771 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004772
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004773 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004774 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004775
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004776 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4777
Selmar09d2ff12018-03-15 17:30:42 +01004778 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004779 ParseExtrasProperty(&(node->extras), o);
4780
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004781 if (store_original_json_for_extras_and_extensions) {
4782 {
4783 json_const_iterator it;
4784 if (FindMember(o, "extensions", it)) {
4785 node->extensions_json_string = JsonToString(GetValue(it));
4786 }
4787 }
4788 {
4789 json_const_iterator it;
4790 if (FindMember(o, "extras", it)) {
4791 node->extras_json_string = JsonToString(GetValue(it));
4792 }
4793 }
4794 }
4795
Emanuel Schrade186322b2017-11-06 11:14:41 +01004796 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004797}
4798
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004799static bool ParsePbrMetallicRoughness(
4800 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4801 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004802 if (pbr == nullptr) {
4803 return false;
4804 }
4805
4806 std::vector<double> baseColorFactor;
4807 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4808 /* required */ false)) {
4809 if (baseColorFactor.size() != 4) {
4810 if (err) {
4811 (*err) +=
4812 "Array length of `baseColorFactor` parameter in "
4813 "pbrMetallicRoughness must be 4, but got " +
4814 std::to_string(baseColorFactor.size()) + "\n";
4815 }
4816 return false;
4817 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004818 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004819 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004820
4821 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004822 json_const_iterator it;
4823 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004824 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4825 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004826 }
4827 }
4828
4829 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004830 json_const_iterator it;
4831 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004832 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4833 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004834 }
4835 }
4836
4837 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4838 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4839
4840 ParseExtensionsProperty(&pbr->extensions, err, o);
4841 ParseExtrasProperty(&pbr->extras, o);
4842
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004843 if (store_original_json_for_extras_and_extensions) {
4844 {
4845 json_const_iterator it;
4846 if (FindMember(o, "extensions", it)) {
4847 pbr->extensions_json_string = JsonToString(GetValue(it));
4848 }
4849 }
4850 {
4851 json_const_iterator it;
4852 if (FindMember(o, "extras", it)) {
4853 pbr->extras_json_string = JsonToString(GetValue(it));
4854 }
4855 }
4856 }
4857
Syoyo Fujita046400b2019-07-24 19:26:48 +09004858 return true;
4859}
4860
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004861static bool ParseMaterial(Material *material, std::string *err, const json &o,
4862 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004863 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4864
Syoyo Fujitaff515702019-08-24 16:29:14 +09004865 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4866 "emissiveFactor",
4867 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004868 if (material->emissiveFactor.size() != 3) {
4869 if (err) {
4870 (*err) +=
4871 "Array length of `emissiveFactor` parameter in "
4872 "material must be 3, but got " +
4873 std::to_string(material->emissiveFactor.size()) + "\n";
4874 }
4875 return false;
4876 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004877 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004878 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004879 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004880 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004881
4882 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4883 /* required */ false);
4884 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4885 /* required */ false);
4886 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4887 /* required */ false);
4888
4889 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004890 json_const_iterator it;
4891 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004892 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004893 GetValue(it),
4894 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004895 }
4896 }
4897
4898 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004899 json_const_iterator it;
4900 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004901 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4902 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004903 }
4904 }
4905
4906 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004907 json_const_iterator it;
4908 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004909 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4910 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004911 }
4912 }
4913
4914 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004915 json_const_iterator it;
4916 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004917 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4918 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004919 }
4920 }
4921
4922 // Old code path. For backward compatibility, we still store material values
4923 // as Parameter. This will create duplicated information for
imallettd9ce9eb2022-10-07 10:37:09 -07004924 // example(pbrMetallicRoughness), but should be negligible in terms of memory
Syoyo Fujita046400b2019-07-24 19:26:48 +09004925 // consumption.
4926 // TODO(syoyo): Remove in the next major release.
4927 material->values.clear();
4928 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004929
jrkooncecba5d6c2019-08-29 11:26:22 -05004930 json_const_iterator it(ObjectBegin(o));
4931 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004932
jrkooncecba5d6c2019-08-29 11:26:22 -05004933 for (; it != itEnd; ++it) {
4934 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004935 if (key == "pbrMetallicRoughness") {
4936 if (IsObject(GetValue(it))) {
4937 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004938
jrkoonce06c30c42019-09-03 15:56:48 -05004939 json_const_iterator itVal(ObjectBegin(values_object));
4940 json_const_iterator itValEnd(ObjectEnd(values_object));
4941
4942 for (; itVal != itValEnd; ++itVal) {
4943 Parameter param;
4944 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004945 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004946 material->values.emplace(GetKey(itVal), std::move(param));
4947 }
4948 }
4949 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004950 } else if (key == "extensions" || key == "extras") {
4951 // done later, skip, otherwise poorly parsed contents will be saved in the
4952 // parametermap and serialized again later
4953 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004954 Parameter param;
4955 if (ParseParameterProperty(&param, err, o, key, false)) {
4956 // names of materials have already been parsed. Putting it in this map
imallettd9ce9eb2022-10-07 10:37:09 -07004957 // doesn't correctly reflect the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004958 if (key != "name")
4959 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004960 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004961 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004962 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004963
Syoyo Fujita046400b2019-07-24 19:26:48 +09004964 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004965 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004966 ParseExtrasProperty(&(material->extras), o);
4967
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004968 if (store_original_json_for_extras_and_extensions) {
4969 {
4970 json_const_iterator eit;
4971 if (FindMember(o, "extensions", eit)) {
4972 material->extensions_json_string = JsonToString(GetValue(eit));
4973 }
4974 }
4975 {
4976 json_const_iterator eit;
4977 if (FindMember(o, "extras", eit)) {
4978 material->extras_json_string = JsonToString(GetValue(eit));
4979 }
4980 }
4981 }
4982
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004983 return true;
4984}
4985
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004986static bool ParseAnimationChannel(
4987 AnimationChannel *channel, std::string *err, const json &o,
4988 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004989 int samplerIndex = -1;
4990 int targetIndex = -1;
4991 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4992 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004993 if (err) {
4994 (*err) += "`sampler` field is missing in animation channels\n";
4995 }
4996 return false;
4997 }
4998
jrkooncecba5d6c2019-08-29 11:26:22 -05004999 json_const_iterator targetIt;
5000 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
5001 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005002
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005003 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005004 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09005005 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005006 }
5007 return false;
5008 }
5009
5010 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5011 true)) {
5012 if (err) {
5013 (*err) += "`path` field is missing in animation.channels.target\n";
5014 }
5015 return false;
5016 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005017 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
5018 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01005019 json_const_iterator it;
5020 if (FindMember(target_object, "extensions", it)) {
5021 channel->target_extensions_json_string = JsonToString(GetValue(it));
5022 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005023 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005024 }
5025
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005026 channel->sampler = samplerIndex;
5027 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005028
Selmar Kok4e2988e2019-08-16 14:08:08 +02005029 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005030 ParseExtrasProperty(&(channel->extras), o);
5031
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005032 if (store_original_json_for_extras_and_extensions) {
5033 {
5034 json_const_iterator it;
5035 if (FindMember(o, "extensions", it)) {
5036 channel->extensions_json_string = JsonToString(GetValue(it));
5037 }
5038 }
5039 {
5040 json_const_iterator it;
5041 if (FindMember(o, "extras", it)) {
5042 channel->extras_json_string = JsonToString(GetValue(it));
5043 }
5044 }
5045 }
5046
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005047 return true;
5048}
5049
5050static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005051 const json &o,
5052 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005053 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005054 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005055 if (FindMember(o, "channels", channelsIt) &&
5056 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005057 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
5058 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
5059 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005060 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005061 if (ParseAnimationChannel(
5062 &channel, err, *i,
5063 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005064 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005065 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005066 }
5067 }
5068 }
5069 }
5070
5071 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005072 json_const_iterator samplerIt;
5073 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5074 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005075
jrkooncecba5d6c2019-08-29 11:26:22 -05005076 json_const_array_iterator it = ArrayBegin(sampler_array);
5077 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005078
jrkooncecba5d6c2019-08-29 11:26:22 -05005079 for (; it != itEnd; ++it) {
5080 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005081
5082 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005083 int inputIndex = -1;
5084 int outputIndex = -1;
5085 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005086 if (err) {
5087 (*err) += "`input` field is missing in animation.sampler\n";
5088 }
5089 return false;
5090 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005091 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5092 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005093 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005094 if (err) {
5095 (*err) += "`output` field is missing in animation.sampler\n";
5096 }
5097 return false;
5098 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005099 sampler.input = inputIndex;
5100 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005101 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005102 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005103
5104 if (store_original_json_for_extras_and_extensions) {
5105 {
5106 json_const_iterator eit;
5107 if (FindMember(o, "extensions", eit)) {
5108 sampler.extensions_json_string = JsonToString(GetValue(eit));
5109 }
5110 }
5111 {
5112 json_const_iterator eit;
5113 if (FindMember(o, "extras", eit)) {
5114 sampler.extras_json_string = JsonToString(GetValue(eit));
5115 }
5116 }
5117 }
5118
jrkooncecba5d6c2019-08-29 11:26:22 -05005119 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005120 }
5121 }
5122 }
5123
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005124 ParseStringProperty(&animation->name, err, o, "name", false);
5125
Selmar Kok4e2988e2019-08-16 14:08:08 +02005126 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005127 ParseExtrasProperty(&(animation->extras), o);
5128
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005129 if (store_original_json_for_extras_and_extensions) {
5130 {
5131 json_const_iterator it;
5132 if (FindMember(o, "extensions", it)) {
5133 animation->extensions_json_string = JsonToString(GetValue(it));
5134 }
5135 }
5136 {
5137 json_const_iterator it;
5138 if (FindMember(o, "extras", it)) {
5139 animation->extras_json_string = JsonToString(GetValue(it));
5140 }
5141 }
5142 }
5143
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005144 return true;
5145}
5146
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005147static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5148 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005149 ParseStringProperty(&sampler->name, err, o, "name", false);
5150
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005151 int minFilter = -1;
5152 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005153 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5154 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005155 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005156 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5157 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5158 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5159 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005160 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5161 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005162
imallettd9ce9eb2022-10-07 10:37:09 -07005163 // TODO(syoyo): Check the value is allowed one.
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005164 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005165
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005166 sampler->minFilter = minFilter;
5167 sampler->magFilter = magFilter;
5168 sampler->wrapS = wrapS;
5169 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005170 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005171
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005172 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005173 ParseExtrasProperty(&(sampler->extras), o);
5174
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005175 if (store_original_json_for_extras_and_extensions) {
5176 {
5177 json_const_iterator it;
5178 if (FindMember(o, "extensions", it)) {
5179 sampler->extensions_json_string = JsonToString(GetValue(it));
5180 }
5181 }
5182 {
5183 json_const_iterator it;
5184 if (FindMember(o, "extras", it)) {
5185 sampler->extras_json_string = JsonToString(GetValue(it));
5186 }
5187 }
5188 }
5189
Syoyo Fujitac2615632016-06-19 21:56:06 +09005190 return true;
5191}
5192
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005193static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5194 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005195 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005196
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005197 std::vector<int> joints;
5198 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005199 return false;
5200 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005201 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005202
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005203 int skeleton = -1;
5204 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5205 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005206
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005207 int invBind = -1;
5208 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5209 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005210
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005211 ParseExtensionsProperty(&(skin->extensions), err, o);
5212 ParseExtrasProperty(&(skin->extras), o);
5213
5214 if (store_original_json_for_extras_and_extensions) {
5215 {
5216 json_const_iterator it;
5217 if (FindMember(o, "extensions", it)) {
5218 skin->extensions_json_string = JsonToString(GetValue(it));
5219 }
5220 }
5221 {
5222 json_const_iterator it;
5223 if (FindMember(o, "extras", it)) {
5224 skin->extras_json_string = JsonToString(GetValue(it));
5225 }
5226 }
5227 }
5228
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005229 return true;
5230}
5231
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005232static bool ParsePerspectiveCamera(
5233 PerspectiveCamera *camera, std::string *err, const json &o,
5234 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005235 double yfov = 0.0;
5236 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5237 return false;
5238 }
5239
5240 double znear = 0.0;
5241 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5242 "PerspectiveCamera")) {
5243 return false;
5244 }
5245
5246 double aspectRatio = 0.0; // = invalid
5247 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5248 "PerspectiveCamera");
5249
5250 double zfar = 0.0; // = invalid
5251 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5252
Selmar Kok31cb7f92018-10-03 15:39:05 +02005253 camera->aspectRatio = aspectRatio;
5254 camera->zfar = zfar;
5255 camera->yfov = yfov;
5256 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005257
Selmar09d2ff12018-03-15 17:30:42 +01005258 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005259 ParseExtrasProperty(&(camera->extras), o);
5260
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005261 if (store_original_json_for_extras_and_extensions) {
5262 {
5263 json_const_iterator it;
5264 if (FindMember(o, "extensions", it)) {
5265 camera->extensions_json_string = JsonToString(GetValue(it));
5266 }
5267 }
5268 {
5269 json_const_iterator it;
5270 if (FindMember(o, "extras", it)) {
5271 camera->extras_json_string = JsonToString(GetValue(it));
5272 }
5273 }
5274 }
5275
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005276 // TODO(syoyo): Validate parameter values.
5277
5278 return true;
5279}
5280
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005281static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5282 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005283 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5284 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005285
Johan Bowald52936a02019-07-17 09:06:45 +02005286 ParseExtensionsProperty(&light->extensions, err, o);
5287 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005288
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005289 if (store_original_json_for_extras_and_extensions) {
5290 {
5291 json_const_iterator it;
5292 if (FindMember(o, "extensions", it)) {
5293 light->extensions_json_string = JsonToString(GetValue(it));
5294 }
5295 }
5296 {
5297 json_const_iterator it;
5298 if (FindMember(o, "extras", it)) {
5299 light->extras_json_string = JsonToString(GetValue(it));
5300 }
5301 }
5302 }
5303
Johan Bowald52936a02019-07-17 09:06:45 +02005304 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005305
Johan Bowald52936a02019-07-17 09:06:45 +02005306 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005307}
5308
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005309static bool ParseOrthographicCamera(
5310 OrthographicCamera *camera, std::string *err, const json &o,
5311 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005312 double xmag = 0.0;
5313 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5314 return false;
5315 }
5316
5317 double ymag = 0.0;
5318 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5319 return false;
5320 }
5321
5322 double zfar = 0.0;
5323 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5324 return false;
5325 }
5326
5327 double znear = 0.0;
5328 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5329 "OrthographicCamera")) {
5330 return false;
5331 }
5332
Selmar09d2ff12018-03-15 17:30:42 +01005333 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005334 ParseExtrasProperty(&(camera->extras), o);
5335
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005336 if (store_original_json_for_extras_and_extensions) {
5337 {
5338 json_const_iterator it;
5339 if (FindMember(o, "extensions", it)) {
5340 camera->extensions_json_string = JsonToString(GetValue(it));
5341 }
5342 }
5343 {
5344 json_const_iterator it;
5345 if (FindMember(o, "extras", it)) {
5346 camera->extras_json_string = JsonToString(GetValue(it));
5347 }
5348 }
5349 }
5350
Selmar Kok31cb7f92018-10-03 15:39:05 +02005351 camera->xmag = xmag;
5352 camera->ymag = ymag;
5353 camera->zfar = zfar;
5354 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005355
5356 // TODO(syoyo): Validate parameter values.
5357
5358 return true;
5359}
5360
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005361static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5362 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005363 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5364 return false;
5365 }
5366
5367 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005368 json_const_iterator orthoIt;
5369 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005370 if (err) {
5371 std::stringstream ss;
imallettd9ce9eb2022-10-07 10:37:09 -07005372 ss << "Orthographic camera description not found." << std::endl;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005373 (*err) += ss.str();
5374 }
5375 return false;
5376 }
5377
jrkooncecba5d6c2019-08-29 11:26:22 -05005378 const json &v = GetValue(orthoIt);
5379 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005380 if (err) {
5381 std::stringstream ss;
5382 ss << "\"orthographic\" is not a JSON object." << std::endl;
5383 (*err) += ss.str();
5384 }
5385 return false;
5386 }
5387
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005388 if (!ParseOrthographicCamera(
5389 &camera->orthographic, err, v,
5390 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005391 return false;
5392 }
5393 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005394 json_const_iterator perspIt;
5395 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005396 if (err) {
5397 std::stringstream ss;
5398 ss << "Perspective camera description not found." << std::endl;
5399 (*err) += ss.str();
5400 }
5401 return false;
5402 }
5403
jrkooncecba5d6c2019-08-29 11:26:22 -05005404 const json &v = GetValue(perspIt);
5405 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005406 if (err) {
5407 std::stringstream ss;
5408 ss << "\"perspective\" is not a JSON object." << std::endl;
5409 (*err) += ss.str();
5410 }
5411 return false;
5412 }
5413
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005414 if (!ParsePerspectiveCamera(
5415 &camera->perspective, err, v,
5416 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005417 return false;
5418 }
5419 } else {
5420 if (err) {
5421 std::stringstream ss;
5422 ss << "Invalid camera type: \"" << camera->type
5423 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5424 (*err) += ss.str();
5425 }
5426 return false;
5427 }
5428
5429 ParseStringProperty(&camera->name, err, o, "name", false);
5430
Selmar09d2ff12018-03-15 17:30:42 +01005431 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005432 ParseExtrasProperty(&(camera->extras), o);
5433
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005434 if (store_original_json_for_extras_and_extensions) {
5435 {
5436 json_const_iterator it;
5437 if (FindMember(o, "extensions", it)) {
5438 camera->extensions_json_string = JsonToString(GetValue(it));
5439 }
5440 }
5441 {
5442 json_const_iterator it;
5443 if (FindMember(o, "extras", it)) {
5444 camera->extras_json_string = JsonToString(GetValue(it));
5445 }
5446 }
5447 }
5448
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005449 return true;
5450}
5451
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005452static bool ParseLight(Light *light, std::string *err, const json &o,
5453 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005454 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5455 return false;
5456 }
5457
5458 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005459 json_const_iterator spotIt;
5460 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005461 if (err) {
5462 std::stringstream ss;
5463 ss << "Spot light description not found." << std::endl;
5464 (*err) += ss.str();
5465 }
5466 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005467 }
5468
jrkooncecba5d6c2019-08-29 11:26:22 -05005469 const json &v = GetValue(spotIt);
5470 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005471 if (err) {
5472 std::stringstream ss;
5473 ss << "\"spot\" is not a JSON object." << std::endl;
5474 (*err) += ss.str();
5475 }
5476 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005477 }
5478
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005479 if (!ParseSpotLight(&light->spot, err, v,
5480 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005481 return false;
5482 }
5483 }
5484
5485 ParseStringProperty(&light->name, err, o, "name", false);
5486 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5487 ParseNumberProperty(&light->range, err, o, "range", false);
5488 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5489 ParseExtensionsProperty(&light->extensions, err, o);
5490 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005491
5492 if (store_original_json_for_extras_and_extensions) {
5493 {
5494 json_const_iterator it;
5495 if (FindMember(o, "extensions", it)) {
5496 light->extensions_json_string = JsonToString(GetValue(it));
5497 }
5498 }
5499 {
5500 json_const_iterator it;
5501 if (FindMember(o, "extras", it)) {
5502 light->extras_json_string = JsonToString(GetValue(it));
5503 }
5504 }
5505 }
5506
Johan Bowald52936a02019-07-17 09:06:45 +02005507 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005508}
5509
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005510bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005511 const char *json_str,
5512 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005513 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005514 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005515 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005516 if (err) {
5517 (*err) = "JSON string too short.\n";
5518 }
5519 return false;
5520 }
5521
jrkooncecba5d6c2019-08-29 11:26:22 -05005522 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005523
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005524#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5525 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005526 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005527 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005528 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005529
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005530 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005531 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005532 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005533 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005534 return false;
5535 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005536#else
5537 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005538 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005539
jrkooncecba5d6c2019-08-29 11:26:22 -05005540 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005541 // Assume parsing was failed.
5542 if (err) {
5543 (*err) = "Failed to parse JSON object\n";
5544 }
5545 return false;
5546 }
5547 }
5548#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005549
jrkooncecba5d6c2019-08-29 11:26:22 -05005550 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005551 // root is not an object.
5552 if (err) {
5553 (*err) = "Root element is not a JSON object\n";
5554 }
5555 return false;
5556 }
5557
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005558 {
5559 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005560 json_const_iterator it;
5561 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005562 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005563 json_const_iterator version_it;
5564 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005565 if (FindMember(itObj, "version", version_it) &&
5566 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005567 version_found = true;
5568 }
5569 }
5570 if (version_found) {
5571 // OK
5572 } else if (check_sections & REQUIRE_VERSION) {
5573 if (err) {
5574 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5575 }
5576 return false;
5577 }
5578 }
5579
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005580 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005581 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005582
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005583 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005584 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005585 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005586 };
5587
Syoyo Fujita83675312017-12-02 21:14:13 +09005588 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005589 if ((check_sections & REQUIRE_SCENES) &&
5590 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005591 if (err) {
5592 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5593 }
5594 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005595 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005596 }
5597
Syoyo Fujita83675312017-12-02 21:14:13 +09005598 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005599 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005600 if (err) {
5601 (*err) += "\"nodes\" 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_ACCESSORS) &&
5609 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005610 if (err) {
5611 (*err) += "\"accessors\" 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
Syoyo Fujita83675312017-12-02 21:14:13 +09005617 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005618 if ((check_sections & REQUIRE_BUFFERS) &&
5619 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005620 if (err) {
5621 (*err) += "\"buffers\" object not found in .gltf\n";
5622 }
5623 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005624 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005625 }
5626
Syoyo Fujita83675312017-12-02 21:14:13 +09005627 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005628 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5629 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005630 if (err) {
5631 (*err) += "\"bufferViews\" object not found in .gltf\n";
5632 }
5633 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005634 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005635 }
5636
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005637 model->buffers.clear();
5638 model->bufferViews.clear();
5639 model->accessors.clear();
5640 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005641 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005642 model->nodes.clear();
5643 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005644 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005645 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005646 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005647
Syoyo Fujita83675312017-12-02 21:14:13 +09005648 // 1. Parse Asset
5649 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005650 json_const_iterator it;
5651 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5652 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005653
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005654 ParseAsset(&model->asset, err, root,
5655 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005656 }
5657 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005658
jrkoonce51453942019-09-03 09:48:30 -05005659#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005660 auto ForEachInArray = [](const json &_v, const char *member,
5661 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005662#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005663 // The std::function<> implementation can be less efficient because it will
5664 // allocate heap when the size of the captured lambda is above 16 bytes with
5665 // clang and gcc, but it does not require C++14.
5666 auto ForEachInArray = [](const json &_v, const char *member,
5667 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005668#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005669 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005670 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005671 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005672 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005673 auto it = ArrayBegin(root);
5674 auto end = ArrayEnd(root);
5675 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005676 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005677 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005678 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005679 return true;
5680 };
5681
jrkooncecba5d6c2019-08-29 11:26:22 -05005682 // 2. Parse extensionUsed
5683 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005684 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005685 std::string str;
5686 GetString(o, str);
5687 model->extensionsUsed.emplace_back(std::move(str));
5688 return true;
5689 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005690 }
5691
Syoyo Fujita83675312017-12-02 21:14:13 +09005692 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005693 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005694 std::string str;
5695 GetString(o, str);
5696 model->extensionsRequired.emplace_back(std::move(str));
5697 return true;
5698 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005699 }
5700
Syoyo Fujita83675312017-12-02 21:14:13 +09005701 // 3. Parse Buffer
5702 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005703 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005704 if (!IsObject(o)) {
5705 if (err) {
5706 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005707 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005708 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005709 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005710 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005711 if (!ParseBuffer(&buffer, err, o,
5712 store_original_json_for_extras_and_extensions_, &fs,
5713 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005714 return false;
5715 }
5716
5717 model->buffers.emplace_back(std::move(buffer));
5718 return true;
5719 });
5720
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005721 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005722 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005723 }
5724 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005725 // 4. Parse BufferView
5726 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005727 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005728 if (!IsObject(o)) {
5729 if (err) {
5730 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005731 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005732 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005733 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005734 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005735 if (!ParseBufferView(&bufferView, err, o,
5736 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005737 return false;
5738 }
5739
5740 model->bufferViews.emplace_back(std::move(bufferView));
5741 return true;
5742 });
5743
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005744 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005745 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005746 }
5747 }
5748
Syoyo Fujita83675312017-12-02 21:14:13 +09005749 // 5. Parse Accessor
5750 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005751 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005752 if (!IsObject(o)) {
5753 if (err) {
5754 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005755 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005756 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005757 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005758 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005759 if (!ParseAccessor(&accessor, err, o,
5760 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005761 return false;
5762 }
5763
5764 model->accessors.emplace_back(std::move(accessor));
5765 return true;
5766 });
5767
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005768 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005769 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005770 }
5771 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005772
Syoyo Fujita83675312017-12-02 21:14:13 +09005773 // 6. Parse Mesh
5774 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005775 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005776 if (!IsObject(o)) {
5777 if (err) {
5778 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005779 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005780 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005781 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005782 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005783 if (!ParseMesh(&mesh, model, err, o,
5784 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005785 return false;
5786 }
5787
5788 model->meshes.emplace_back(std::move(mesh));
5789 return true;
5790 });
5791
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005792 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005793 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005794 }
5795 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005796
viperscape9df05802018-12-05 14:11:01 -05005797 // Assign missing bufferView target types
5798 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005799 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005800 for (auto &mesh : model->meshes) {
5801 for (auto &primitive : mesh.primitives) {
5802 if (primitive.indices >
5803 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005804 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005805 if (size_t(primitive.indices) >= model->accessors.size()) {
5806 if (err) {
5807 (*err) += "primitive indices accessor out of bounds";
5808 }
5809 return false;
5810 }
5811
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005812 auto bufferView =
5813 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005814 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5815 if (err) {
5816 (*err) += "accessor[" + std::to_string(primitive.indices) +
5817 "] invalid bufferView";
5818 }
5819 return false;
5820 }
5821
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005822 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005823 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
imallettd9ce9eb2022-10-07 10:37:09 -07005824 // we could optionally check if accessors' bufferView type is Scalar, as
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005825 // it should be
viperscape9df05802018-12-05 14:11:01 -05005826 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005827
5828 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07005829 const auto accessorsIndex = size_t(attribute.second);
5830 if (accessorsIndex < model->accessors.size()) {
5831 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5832 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005833 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005834 model->bufferViews[size_t(bufferView)].target =
5835 TINYGLTF_TARGET_ARRAY_BUFFER;
5836 }
5837 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005838 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005839
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005840 for (auto &target : primitive.targets) {
5841 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07005842 const auto accessorsIndex = size_t(attribute.second);
5843 if (accessorsIndex < model->accessors.size()) {
5844 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5845 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005846 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005847 model->bufferViews[size_t(bufferView)].target =
5848 TINYGLTF_TARGET_ARRAY_BUFFER;
5849 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04005850 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005851 }
5852 }
viperscape9df05802018-12-05 14:11:01 -05005853 }
5854 }
5855
Syoyo Fujita83675312017-12-02 21:14:13 +09005856 // 7. Parse Node
5857 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005858 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005859 if (!IsObject(o)) {
5860 if (err) {
5861 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005862 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005863 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005864 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005865 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005866 if (!ParseNode(&node, err, o,
5867 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005868 return false;
5869 }
5870
5871 model->nodes.emplace_back(std::move(node));
5872 return true;
5873 });
5874
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005875 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005876 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005877 }
5878 }
5879
5880 // 8. Parse scenes.
5881 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005882 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005883 if (!IsObject(o)) {
5884 if (err) {
5885 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005886 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005887 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005888 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005889 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005890 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005891
5892 Scene scene;
5893 scene.nodes = std::move(nodes);
5894
5895 ParseStringProperty(&scene.name, err, o, "name", false);
5896
5897 ParseExtensionsProperty(&scene.extensions, err, o);
5898 ParseExtrasProperty(&scene.extras, o);
5899
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005900 if (store_original_json_for_extras_and_extensions_) {
5901 {
5902 json_const_iterator it;
5903 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005904 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005905 }
5906 }
5907 {
5908 json_const_iterator it;
5909 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005910 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005911 }
5912 }
5913 }
5914
jrkooncecba5d6c2019-08-29 11:26:22 -05005915 model->scenes.emplace_back(std::move(scene));
5916 return true;
5917 });
5918
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005919 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005920 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005921 }
5922 }
5923
5924 // 9. Parse default scenes.
5925 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005926 json_const_iterator rootIt;
5927 int iVal;
5928 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5929 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005930 }
5931 }
5932
5933 // 10. Parse Material
5934 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005935 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005936 if (!IsObject(o)) {
5937 if (err) {
5938 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005939 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005940 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005941 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005942 Material material;
5943 ParseStringProperty(&material.name, err, o, "name", false);
5944
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005945 if (!ParseMaterial(&material, err, o,
5946 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005947 return false;
5948 }
5949
5950 model->materials.emplace_back(std::move(material));
5951 return true;
5952 });
5953
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005954 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005955 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005956 }
5957 }
5958
5959 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005960 void *load_image_user_data{nullptr};
5961
5962 LoadImageDataOption load_image_option;
5963
5964 if (user_image_loader_) {
5965 // Use user supplied pointer
5966 load_image_user_data = load_image_user_data_;
5967 } else {
5968 load_image_option.preserve_channels = preserve_image_channels_;
5969 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5970 }
5971
Syoyo Fujita83675312017-12-02 21:14:13 +09005972 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005973 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005974 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005975 if (!IsObject(o)) {
5976 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005977 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005978 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005979 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005980 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005981 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005982 if (!ParseImage(&image, idx, err, warn, o,
5983 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005984 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005985 return false;
5986 }
5987
5988 if (image.bufferView != -1) {
5989 // Load image from the buffer view.
5990 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5991 if (err) {
5992 std::stringstream ss;
5993 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005994 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005995 (*err) += ss.str();
5996 }
5997 return false;
5998 }
5999
6000 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006001 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05006002 if (size_t(bufferView.buffer) >= model->buffers.size()) {
6003 if (err) {
6004 std::stringstream ss;
6005 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006006 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05006007 (*err) += ss.str();
6008 }
6009 return false;
6010 }
6011 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6012
6013 if (*LoadImageData == nullptr) {
6014 if (err) {
6015 (*err) += "No LoadImageData callback specified.\n";
6016 }
6017 return false;
6018 }
6019 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006020 &image, idx, err, warn, image.width, image.height,
6021 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006022 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05006023 if (!ret) {
6024 return false;
6025 }
6026 }
6027
6028 model->images.emplace_back(std::move(image));
6029 ++idx;
6030 return true;
6031 });
6032
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006033 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006034 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006035 }
6036 }
6037
6038 // 12. Parse Texture
6039 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006040 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006041 if (!IsObject(o)) {
6042 if (err) {
6043 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006044 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006045 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006046 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006047 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006048 if (!ParseTexture(&texture, err, o,
6049 store_original_json_for_extras_and_extensions_,
6050 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006051 return false;
6052 }
6053
6054 model->textures.emplace_back(std::move(texture));
6055 return true;
6056 });
6057
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006058 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006059 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006060 }
6061 }
6062
6063 // 13. Parse Animation
6064 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006065 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006066 if (!IsObject(o)) {
6067 if (err) {
6068 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006069 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006070 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006071 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006072 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006073 if (!ParseAnimation(&animation, err, o,
6074 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006075 return false;
6076 }
6077
6078 model->animations.emplace_back(std::move(animation));
6079 return true;
6080 });
6081
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006082 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006083 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006084 }
6085 }
6086
6087 // 14. Parse Skin
6088 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006089 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006090 if (!IsObject(o)) {
6091 if (err) {
6092 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006093 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006094 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006095 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006096 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006097 if (!ParseSkin(&skin, err, o,
6098 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006099 return false;
6100 }
6101
6102 model->skins.emplace_back(std::move(skin));
6103 return true;
6104 });
6105
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006106 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006107 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006108 }
6109 }
6110
6111 // 15. Parse Sampler
6112 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006113 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006114 if (!IsObject(o)) {
6115 if (err) {
6116 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006117 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006118 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006119 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006120 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006121 if (!ParseSampler(&sampler, err, o,
6122 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006123 return false;
6124 }
6125
6126 model->samplers.emplace_back(std::move(sampler));
6127 return true;
6128 });
6129
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006130 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006131 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006132 }
6133 }
6134
6135 // 16. Parse Camera
6136 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006137 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006138 if (!IsObject(o)) {
6139 if (err) {
6140 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006141 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006142 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006143 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006144 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006145 if (!ParseCamera(&camera, err, o,
6146 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006147 return false;
6148 }
6149
6150 model->cameras.emplace_back(std::move(camera));
6151 return true;
6152 });
6153
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006154 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006155 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006156 }
6157 }
6158
6159 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006160 ParseExtensionsProperty(&model->extensions, err, v);
6161
6162 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006163 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006164 json_const_iterator rootIt;
6165 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6166 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006167
jrkooncecba5d6c2019-08-29 11:26:22 -05006168 json_const_iterator it(ObjectBegin(root));
6169 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006170 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006171 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006172 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006173 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006174 const json &object = GetValue(it);
6175 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006176 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006177 const json &lights = GetValue(itLight);
6178 if (!IsArray(lights)) {
6179 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006180 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006181
6182 auto arrayIt(ArrayBegin(lights));
6183 auto arrayItEnd(ArrayEnd(lights));
6184 for (; arrayIt != arrayItEnd; ++arrayIt) {
6185 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006186 if (!ParseLight(&light, err, *arrayIt,
6187 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006188 return false;
6189 }
6190 model->lights.emplace_back(std::move(light));
6191 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006192 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006193 }
6194 }
6195 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006196 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006197
mynzc0d4d1c2018-06-28 23:06:00 +09006198 // 19. Parse Extras
6199 ParseExtrasProperty(&model->extras, v);
6200
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006201 if (store_original_json_for_extras_and_extensions_) {
6202 model->extras_json_string = JsonToString(v["extras"]);
6203 model->extensions_json_string = JsonToString(v["extensions"]);
6204 }
6205
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006206 return true;
6207}
6208
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006209bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006210 std::string *warn, const char *str,
6211 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006212 const std::string &base_dir,
6213 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006214 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006215 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006216 bin_size_ = 0;
6217
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006218 return LoadFromString(model, err, warn, str, length, base_dir,
6219 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006220}
6221
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006222bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006223 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006224 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006225 std::stringstream ss;
6226
Paolo Jovone6601bf2018-07-07 20:43:33 +02006227 if (fs.ReadWholeFile == nullptr) {
6228 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006229 ss << "Failed to read file: " << filename
6230 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006231 if (err) {
6232 (*err) = ss.str();
6233 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006234 return false;
6235 }
6236
Paolo Jovone6601bf2018-07-07 20:43:33 +02006237 std::vector<unsigned char> data;
6238 std::string fileerr;
6239 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006240 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006241 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6242 if (err) {
6243 (*err) = ss.str();
6244 }
6245 return false;
6246 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006247
Paolo Jovone6601bf2018-07-07 20:43:33 +02006248 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006249 if (sz == 0) {
6250 if (err) {
6251 (*err) = "Empty file.";
6252 }
6253 return false;
6254 }
6255
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006256 std::string basedir = GetBaseDir(filename);
6257
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006258 bool ret = LoadASCIIFromString(
6259 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6260 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006261
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006262 return ret;
6263}
6264
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006265bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006266 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006267 const unsigned char *bytes,
6268 unsigned int size,
6269 const std::string &base_dir,
6270 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006271 if (size < 20) {
6272 if (err) {
6273 (*err) = "Too short data size for glTF Binary.";
6274 }
6275 return false;
6276 }
6277
Syoyo Fujitabeded612016-05-01 20:03:43 +09006278 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6279 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006280 // ok
6281 } else {
6282 if (err) {
6283 (*err) = "Invalid magic.";
6284 }
6285 return false;
6286 }
6287
Syoyo Fujitabeded612016-05-01 20:03:43 +09006288 unsigned int version; // 4 bytes
6289 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006290 unsigned int chunk0_length; // 4 bytes
6291 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006292
Syoyo Fujitabeded612016-05-01 20:03:43 +09006293 memcpy(&version, bytes + 4, 4);
6294 swap4(&version);
6295 memcpy(&length, bytes + 8, 4);
6296 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006297 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6298 swap4(&chunk0_length);
6299 memcpy(&chunk0_format, bytes + 16, 4);
6300 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006301
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006302 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6303 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006304 // In case the Bin buffer is not present, the size is exactly 20 + size of
6305 // JSON contents,
6306 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006307 //
6308 // https://github.com/syoyo/tinygltf/issues/372
6309 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006310 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006311
Syoyo Fujitac670f082022-09-17 19:52:25 +09006312 if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006313 // Do not allow 4GB or more GLB data.
6314 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6315 }
6316
Syoyo Fujitac670f082022-09-17 19:52:25 +09006317 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6318 (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006319 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006320 if (err) {
6321 (*err) = "Invalid glTF binary.";
6322 }
6323 return false;
6324 }
6325
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006326 // Padding check
6327 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6328 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006329 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006330 if (err) {
6331 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6332 }
6333 }
6334
Syoyo Fujita612e5782022-09-18 21:01:39 +09006335 //std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6336 //std::cout << "length = " << length << "\n";
6337
Syoyo Fujitac670f082022-09-17 19:52:25 +09006338 // Chunk1(BIN) data
6339 // 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 +09006340 // So when header + JSON data == binary size, Chunk1 is omitted.
6341 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006342
Syoyo Fujitac670f082022-09-17 19:52:25 +09006343 bin_data_ = nullptr;
6344 bin_size_ = 0;
6345 } else {
6346 // Read Chunk1 info(BIN data)
imallettd9ce9eb2022-10-07 10:37:09 -07006347 // 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 +09006348 if ((header_and_json_size + 12ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006349 if (err) {
Syoyo Fujita612e5782022-09-18 21:01:39 +09006350 (*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 +09006351 }
6352 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006353 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006354
Syoyo Fujitac670f082022-09-17 19:52:25 +09006355 unsigned int chunk1_length; // 4 bytes
6356 unsigned int chunk1_format; // 4 bytes;
6357 memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
6358 swap4(&chunk1_length);
6359 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6360 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006361
Syoyo Fujita612e5782022-09-18 21:01:39 +09006362 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6363
Syoyo Fujitac670f082022-09-17 19:52:25 +09006364 if (chunk1_length < 4) {
6365 if (err) {
6366 (*err) = "Insufficient Chunk1(BIN) data size.";
6367 }
6368 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006369 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006370
Syoyo Fujitac670f082022-09-17 19:52:25 +09006371 if ((chunk1_length % 4) != 0) {
6372 if (err) {
6373 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6374 }
6375 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006376 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006377
Syoyo Fujitac670f082022-09-17 19:52:25 +09006378 if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
6379 if (err) {
6380 (*err) = "BIN Chunk data length exceeds the GLB size.";
6381 }
6382 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006383 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006384
Syoyo Fujitac670f082022-09-17 19:52:25 +09006385 if (chunk1_format != 0x004e4942) {
6386 if (err) {
imallettd9ce9eb2022-10-07 10:37:09 -07006387 (*err) = "Invalid type for chunk1 data.";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006388 }
6389 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006390 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006391
Syoyo Fujita612e5782022-09-18 21:01:39 +09006392 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6393
Syoyo Fujitac670f082022-09-17 19:52:25 +09006394 bin_data_ = bytes + header_and_json_size +
6395 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6396
6397 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006398 }
6399
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006400 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006401 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006402 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006403
6404 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006405
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006406 bool ret = LoadFromString(model, err, warn,
6407 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006408 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006409 if (!ret) {
6410 return ret;
6411 }
6412
6413 return true;
6414}
6415
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006416bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006417 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006418 const std::string &filename,
6419 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006420 std::stringstream ss;
6421
Paolo Jovone6601bf2018-07-07 20:43:33 +02006422 if (fs.ReadWholeFile == nullptr) {
6423 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006424 ss << "Failed to read file: " << filename
6425 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006426 if (err) {
6427 (*err) = ss.str();
6428 }
6429 return false;
6430 }
6431
Paolo Jovone6601bf2018-07-07 20:43:33 +02006432 std::vector<unsigned char> data;
6433 std::string fileerr;
6434 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006435 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006436 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6437 if (err) {
6438 (*err) = ss.str();
6439 }
6440 return false;
6441 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006442
Syoyo Fujitabeded612016-05-01 20:03:43 +09006443 std::string basedir = GetBaseDir(filename);
6444
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006445 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6446 static_cast<unsigned int>(data.size()),
6447 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006448
6449 return ret;
6450}
6451
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006452///////////////////////
6453// GLTF Serialization
6454///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006455namespace {
6456json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006457#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006458 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006459#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006460 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006461#endif
jrkoonce63419a12019-09-03 17:06:41 -05006462}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006463
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006464void JsonAssign(json &dest, const json &src) {
6465#ifdef TINYGLTF_USE_RAPIDJSON
6466 dest.CopyFrom(src, GetAllocator());
6467#else
6468 dest = src;
6469#endif
6470}
6471
6472void JsonAddMember(json &o, const char *key, json &&value) {
6473#ifdef TINYGLTF_USE_RAPIDJSON
6474 if (!o.IsObject()) {
6475 o.SetObject();
6476 }
6477 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6478#else
6479 o[key] = std::move(value);
6480#endif
6481}
6482
6483void JsonPushBack(json &o, json &&value) {
6484#ifdef TINYGLTF_USE_RAPIDJSON
6485 o.PushBack(std::move(value), GetAllocator());
6486#else
6487 o.push_back(std::move(value));
6488#endif
6489}
6490
6491bool JsonIsNull(const json &o) {
6492#ifdef TINYGLTF_USE_RAPIDJSON
6493 return o.IsNull();
6494#else
6495 return o.is_null();
6496#endif
6497}
6498
6499void JsonSetObject(json &o) {
6500#ifdef TINYGLTF_USE_RAPIDJSON
6501 o.SetObject();
6502#else
6503 o = o.object({});
6504#endif
6505}
6506
6507void JsonReserveArray(json &o, size_t s) {
6508#ifdef TINYGLTF_USE_RAPIDJSON
6509 o.SetArray();
6510 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6511#endif
6512 (void)(o);
6513 (void)(s);
6514}
6515} // namespace
6516
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006517// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006518
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006519template <typename T>
6520static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006521 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006522 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006523 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006524 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006525 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006526}
6527
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006528#ifdef TINYGLTF_USE_RAPIDJSON
6529template <>
6530void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6531 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6532}
6533#endif
6534
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006535template <typename T>
6536static void SerializeNumberArrayProperty(const std::string &key,
6537 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006538 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006539 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006540
jrkooncecba5d6c2019-08-29 11:26:22 -05006541 json ary;
6542 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006543 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006544 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006545 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006546 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006547}
6548
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006549static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006550 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006551 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006552}
6553
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006554static void SerializeStringArrayProperty(const std::string &key,
6555 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006556 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006557 json ary;
6558 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006559 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006560 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006561 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006562 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006563}
6564
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006565static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006566 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006567#ifdef TINYGLTF_USE_RAPIDJSON
6568 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006569 case REAL_TYPE:
6570 obj.SetDouble(value.Get<double>());
6571 break;
6572 case INT_TYPE:
6573 obj.SetInt(value.Get<int>());
6574 break;
6575 case BOOL_TYPE:
6576 obj.SetBool(value.Get<bool>());
6577 break;
6578 case STRING_TYPE:
6579 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6580 break;
6581 case ARRAY_TYPE: {
6582 obj.SetArray();
6583 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6584 GetAllocator());
6585 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6586 Value elementValue = value.Get(int(i));
6587 json elementJson;
6588 if (ValueToJson(value.Get(int(i)), &elementJson))
6589 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006590 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006591 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006592 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006593 case BINARY_TYPE:
6594 // TODO
6595 // obj = json(value.Get<std::vector<unsigned char>>());
6596 return false;
6597 break;
6598 case OBJECT_TYPE: {
6599 obj.SetObject();
6600 Value::Object objMap = value.Get<Value::Object>();
6601 for (auto &it : objMap) {
6602 json elementJson;
6603 if (ValueToJson(it.second, &elementJson)) {
6604 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6605 std::move(elementJson), GetAllocator());
6606 }
6607 }
6608 break;
6609 }
6610 case NULL_TYPE:
6611 default:
6612 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006613 }
6614#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006615 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006616 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006617 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006618 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006619 case INT_TYPE:
6620 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006621 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006622 case BOOL_TYPE:
6623 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006624 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006625 case STRING_TYPE:
6626 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006627 break;
6628 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006629 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6630 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006631 json elementJson;
6632 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006633 obj.push_back(elementJson);
6634 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006635 break;
6636 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006637 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006638 // TODO
6639 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006640 return false;
6641 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006642 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006643 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006644 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006645 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006646 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006647 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006648 break;
6649 }
6650 case NULL_TYPE:
6651 default:
6652 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006653 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006654#endif
6655 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006656 return true;
6657}
6658
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006659static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006660 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006661 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006662 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006663 JsonAddMember(obj, key.c_str(), std::move(ret));
6664 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006665}
6666
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006667static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006668 json &o) {
6669 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006670 if (data.size() > 0) {
6671 std::string encodedData =
6672 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6673 SerializeStringProperty("uri", header + encodedData, o);
6674 } else {
6675 // Issue #229
imallettd9ce9eb2022-10-07 10:37:09 -07006676 // size 0 is allowed. Just emit mime header.
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006677 SerializeStringProperty("uri", header, o);
6678 }
johan bowald30c53472018-03-30 11:49:36 +02006679}
6680
Selmar Koke4677492018-10-25 16:45:49 +02006681static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006682 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006683#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006684#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006685 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6686 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6687 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6688 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006689 std::ostream output(&wfile_buf);
6690 if (!wfile_buf.is_open()) return false;
6691#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006692 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006693 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006694#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006695 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006696 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006697#endif
6698#else
6699 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6700 if (!output.is_open()) return false;
6701#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006702 if (data.size() > 0) {
6703 output.write(reinterpret_cast<const char *>(&data[0]),
6704 std::streamsize(data.size()));
6705 } else {
6706 // Issue #229
6707 // size 0 will be still valid buffer data.
6708 // write empty file.
6709 }
Selmar Koke4677492018-10-25 16:45:49 +02006710 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006711}
6712
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006713#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006714static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006715 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6716 ++paramIt) {
6717 if (paramIt->second.number_array.size()) {
6718 SerializeNumberArrayProperty<double>(paramIt->first,
6719 paramIt->second.number_array, o);
6720 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006721 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006722 for (std::map<std::string, double>::iterator it =
6723 paramIt->second.json_double_value.begin();
6724 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006725 if (it->first == "index") {
6726 json_double_value[it->first] = paramIt->second.TextureIndex();
6727 } else {
6728 json_double_value[it->first] = it->second;
6729 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006730 }
6731
Syoyo Fujita83675312017-12-02 21:14:13 +09006732 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006733 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006734 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006735 } else if (paramIt->second.has_number_value) {
6736 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006737 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006738 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006739 }
6740 }
6741}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006742#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006743
Selmar Kok81b672b2019-10-18 16:08:44 +02006744static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006745 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006746
6747 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006748 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006749 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006750 // Allow an empty object for extension(#97)
6751 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006752 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006753 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006754 isNull = JsonIsNull(ret);
6755 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006756 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006757 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006758 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6759 // create empty object so that an extension name is still included in
6760 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006761 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006762 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006763 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006764 }
6765 }
Selmar09d2ff12018-03-15 17:30:42 +01006766 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006767 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006768}
6769
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006770static void SerializeGltfAccessor(const Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006771 if (accessor.bufferView >= 0)
6772 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006773
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006774 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006775 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006776
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006777 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6778 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006779
6780 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6781 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6782 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6783 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6784 } else {
6785 // Issue #301. Serialize as integer.
6786 // Assume int value is within [-2**31-1, 2**31-1]
6787 {
6788 std::vector<int> values;
6789 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6790 std::back_inserter(values),
6791 [](double v) { return static_cast<int>(v); });
6792
6793 SerializeNumberArrayProperty<int>("min", values, o);
6794 }
6795
6796 {
6797 std::vector<int> values;
6798 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6799 std::back_inserter(values),
6800 [](double v) { return static_cast<int>(v); });
6801
6802 SerializeNumberArrayProperty<int>("max", values, o);
6803 }
6804 }
6805
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006806 if (accessor.normalized)
6807 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006808 std::string type;
6809 switch (accessor.type) {
6810 case TINYGLTF_TYPE_SCALAR:
6811 type = "SCALAR";
6812 break;
6813 case TINYGLTF_TYPE_VEC2:
6814 type = "VEC2";
6815 break;
6816 case TINYGLTF_TYPE_VEC3:
6817 type = "VEC3";
6818 break;
6819 case TINYGLTF_TYPE_VEC4:
6820 type = "VEC4";
6821 break;
6822 case TINYGLTF_TYPE_MAT2:
6823 type = "MAT2";
6824 break;
6825 case TINYGLTF_TYPE_MAT3:
6826 type = "MAT3";
6827 break;
6828 case TINYGLTF_TYPE_MAT4:
6829 type = "MAT4";
6830 break;
6831 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006832
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006833 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006834 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006835
6836 if (accessor.extras.Type() != NULL_TYPE) {
6837 SerializeValue("extras", accessor.extras, o);
6838 }
feiy0b315432022-08-13 10:08:17 +08006839
6840 // sparse
6841 if (accessor.sparse.isSparse)
6842 {
6843 json sparse;
6844 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
6845 {
6846 json indices;
6847 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
6848 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
6849 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
6850 JsonAddMember(sparse, "indices", std::move(indices));
6851 }
6852 {
6853 json values;
6854 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08006855 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
feiy0b315432022-08-13 10:08:17 +08006856 JsonAddMember(sparse, "values", std::move(values));
6857 }
6858 JsonAddMember(o, "sparse", std::move(sparse));
6859 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006860}
6861
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006862static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
6863 json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006864 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006865 {
6866 json target;
6867 SerializeNumberProperty("node", channel.target_node, target);
6868 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006869
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006870 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006871
jrkooncecba5d6c2019-08-29 11:26:22 -05006872 JsonAddMember(o, "target", std::move(target));
6873 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006874
6875 if (channel.extras.Type() != NULL_TYPE) {
6876 SerializeValue("extras", channel.extras, o);
6877 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006878
Selmar Kok4e2988e2019-08-16 14:08:08 +02006879 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006880}
6881
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006882static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
6883 json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006884 SerializeNumberProperty("input", sampler.input, o);
6885 SerializeNumberProperty("output", sampler.output, o);
6886 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006887
6888 if (sampler.extras.Type() != NULL_TYPE) {
6889 SerializeValue("extras", sampler.extras, o);
6890 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006891}
6892
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006893static void SerializeGltfAnimation(const Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006894 if (!animation.name.empty())
6895 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006896
jrkooncecba5d6c2019-08-29 11:26:22 -05006897 {
6898 json channels;
6899 JsonReserveArray(channels, animation.channels.size());
6900 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6901 json channel;
6902 AnimationChannel gltfChannel = animation.channels[i];
6903 SerializeGltfAnimationChannel(gltfChannel, channel);
6904 JsonPushBack(channels, std::move(channel));
6905 }
6906
6907 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006908 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006909
jrkooncecba5d6c2019-08-29 11:26:22 -05006910 {
6911 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006912 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006913 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6914 json sampler;
6915 AnimationSampler gltfSampler = animation.samplers[i];
6916 SerializeGltfAnimationSampler(gltfSampler, sampler);
6917 JsonPushBack(samplers, std::move(sampler));
6918 }
6919 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006920 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006921
Jens Olssonb3af2f12018-06-04 10:17:49 +02006922 if (animation.extras.Type() != NULL_TYPE) {
6923 SerializeValue("extras", animation.extras, o);
6924 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006925
Selmar Kok4e2988e2019-08-16 14:08:08 +02006926 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006927}
6928
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006929static void SerializeGltfAsset(const Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006930 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006931 SerializeStringProperty("generator", asset.generator, o);
6932 }
6933
Christophe820ede82019-07-04 15:21:21 +09006934 if (!asset.copyright.empty()) {
6935 SerializeStringProperty("copyright", asset.copyright, o);
6936 }
6937
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006938 auto version = asset.version;
6939 if (version.empty()) {
Syoyo Fujitab702de72021-03-02 19:08:29 +09006940 // Just in case
6941 // `version` must be defined
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006942 version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006943 }
6944
Syoyo Fujitab702de72021-03-02 19:08:29 +09006945 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006946 SerializeStringProperty("version", version, o);
Syoyo Fujitab702de72021-03-02 19:08:29 +09006947
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006948 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006949 SerializeValue("extras", asset.extras, o);
6950 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006951
Selmar09d2ff12018-03-15 17:30:42 +01006952 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006953}
6954
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006955static void SerializeGltfBufferBin(const Buffer &buffer, json &o,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006956 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006957 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006958 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006959
6960 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6961
6962 if (buffer.extras.Type() != NULL_TYPE) {
6963 SerializeValue("extras", buffer.extras, o);
6964 }
6965}
6966
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006967static void SerializeGltfBuffer(const Buffer &buffer, json &o) {
johan bowald30c53472018-03-30 11:49:36 +02006968 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6969 SerializeGltfBufferData(buffer.data, o);
6970
6971 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006972
6973 if (buffer.extras.Type() != NULL_TYPE) {
6974 SerializeValue("extras", buffer.extras, o);
6975 }
johan bowald30c53472018-03-30 11:49:36 +02006976}
6977
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006978static bool SerializeGltfBuffer(const Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006979 const std::string &binFilename,
6980 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006981 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006982 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006983 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006984
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006985 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006986
6987 if (buffer.extras.Type() != NULL_TYPE) {
6988 SerializeValue("extras", buffer.extras, o);
6989 }
Selmar Koke4677492018-10-25 16:45:49 +02006990 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006991}
6992
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006993static void SerializeGltfBufferView(const BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006994 SerializeNumberProperty("buffer", bufferView.buffer, o);
6995 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006996
Johan Bowaldfaa27222018-03-28 14:44:45 +02006997 // byteStride is optional, minimum allowed is 4
6998 if (bufferView.byteStride >= 4) {
6999 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
7000 }
7001 // byteOffset is optional, default is 0
7002 if (bufferView.byteOffset > 0) {
7003 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
7004 }
7005 // Target is optional, check if it contains a valid value
7006 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
7007 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
7008 SerializeNumberProperty("target", bufferView.target, o);
7009 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007010 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007011 SerializeStringProperty("name", bufferView.name, o);
7012 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007013
7014 if (bufferView.extras.Type() != NULL_TYPE) {
7015 SerializeValue("extras", bufferView.extras, o);
7016 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007017}
7018
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007019static void SerializeGltfImage(const Image &image, const std::string uri,
7020 json &o) {
Syoyo Fujita584f1df2022-12-29 21:05:53 +09007021 // From 2.7.0, we look for `uri` parameter, not `Image.uri`
7022 // if uri is empty, the mimeType and bufferview should be set
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007023 if (uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007024 SerializeStringProperty("mimeType", image.mimeType, o);
7025 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7026 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007027 // TODO(syoyo): dlib::urilencode?
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007028 SerializeStringProperty("uri", uri, o);
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007029 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007030
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007031 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007032 SerializeStringProperty("name", image.name, o);
7033 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007034
7035 if (image.extras.Type() != NULL_TYPE) {
7036 SerializeValue("extras", image.extras, o);
7037 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09007038
7039 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007040}
7041
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007042static void SerializeGltfTextureInfo(const TextureInfo &texinfo, json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007043 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007044
Syoyo Fujita046400b2019-07-24 19:26:48 +09007045 if (texinfo.texCoord != 0) {
7046 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7047 }
7048
7049 if (texinfo.extras.Type() != NULL_TYPE) {
7050 SerializeValue("extras", texinfo.extras, o);
7051 }
7052
7053 SerializeExtensionMap(texinfo.extensions, o);
7054}
7055
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007056static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
Syoyo Fujita046400b2019-07-24 19:26:48 +09007057 json &o) {
7058 SerializeNumberProperty("index", texinfo.index, o);
7059
7060 if (texinfo.texCoord != 0) {
7061 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7062 }
7063
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007064 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007065 SerializeNumberProperty("scale", texinfo.scale, o);
7066 }
7067
7068 if (texinfo.extras.Type() != NULL_TYPE) {
7069 SerializeValue("extras", texinfo.extras, o);
7070 }
7071
7072 SerializeExtensionMap(texinfo.extensions, o);
7073}
7074
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007075static void SerializeGltfOcclusionTextureInfo(
7076 const OcclusionTextureInfo &texinfo, json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007077 SerializeNumberProperty("index", texinfo.index, o);
7078
7079 if (texinfo.texCoord != 0) {
7080 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7081 }
7082
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007083 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007084 SerializeNumberProperty("strength", texinfo.strength, o);
7085 }
7086
7087 if (texinfo.extras.Type() != NULL_TYPE) {
7088 SerializeValue("extras", texinfo.extras, o);
7089 }
7090
7091 SerializeExtensionMap(texinfo.extensions, o);
7092}
7093
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007094static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
Syoyo Fujita046400b2019-07-24 19:26:48 +09007095 json &o) {
7096 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7097 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7098 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7099 o);
7100 }
7101
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007102 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007103 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7104 }
7105
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007106 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007107 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7108 }
7109
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007110 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007111 json texinfo;
7112 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007113 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007114 }
7115
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007116 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007117 json texinfo;
7118 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007119 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007120 }
7121
7122 SerializeExtensionMap(pbr.extensions, o);
7123
7124 if (pbr.extras.Type() != NULL_TYPE) {
7125 SerializeValue("extras", pbr.extras, o);
7126 }
7127}
7128
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007129static void SerializeGltfMaterial(const Material &material, json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007130 if (material.name.size()) {
7131 SerializeStringProperty("name", material.name, o);
7132 }
7133
7134 // QUESTION(syoyo): Write material parameters regardless of its default value?
7135
7136 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7137 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7138 }
7139
Patrick Härtld9a468b2019-08-14 14:14:07 +02007140 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007141 SerializeStringProperty("alphaMode", material.alphaMode, o);
7142 }
7143
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007144 if (material.doubleSided != false)
7145 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007146
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007147 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007148 json texinfo;
7149 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007150 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007151 }
7152
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007153 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007154 json texinfo;
7155 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007156 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007157 }
7158
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007159 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007160 json texinfo;
7161 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007162 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007163 }
7164
7165 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7166 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7167 SerializeNumberArrayProperty<double>("emissiveFactor",
7168 material.emissiveFactor, o);
7169 }
7170
7171 {
7172 json pbrMetallicRoughness;
7173 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7174 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007175 // Issue 204
7176 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7177 // default values(json is null). Otherwise it will serialize to
7178 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
imallettd9ce9eb2022-10-07 10:37:09 -07007179 // importers (and validators).
Syoyo Fujita7e009042019-09-13 15:32:22 +09007180 //
7181 if (!JsonIsNull(pbrMetallicRoughness)) {
7182 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7183 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007184 }
7185
7186#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007187 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007188 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007189 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007190 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007191 }
7192
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007193 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007194#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007195
Syoyo Fujita046400b2019-07-24 19:26:48 +09007196#endif
7197
7198 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007199
7200 if (material.extras.Type() != NULL_TYPE) {
7201 SerializeValue("extras", material.extras, o);
7202 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007203}
7204
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007205static void SerializeGltfMesh(const Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007206 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007207 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007208 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007209 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007210 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007211 {
7212 json attributes;
7213 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007214 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007215 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7216 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007217
jrkooncecba5d6c2019-08-29 11:26:22 -05007218 JsonAddMember(primitive, "attributes", std::move(attributes));
7219 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007220
imallettd9ce9eb2022-10-07 10:37:09 -07007221 // Indices is optional
Johan Bowaldfaa27222018-03-28 14:44:45 +02007222 if (gltfPrimitive.indices > -1) {
7223 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7224 }
7225 // Material is optional
7226 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007227 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7228 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007229 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007230 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007231
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007232 // Morph targets
7233 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007234 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007235 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007236 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007237 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007238 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7239 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7240 attrIt != targetData.end(); ++attrIt) {
7241 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7242 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007243 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007244 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007245 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007246 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007247 }
7248
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007249 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007250
Jens Olssonb3af2f12018-06-04 10:17:49 +02007251 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7252 SerializeValue("extras", gltfPrimitive.extras, primitive);
7253 }
7254
jrkooncecba5d6c2019-08-29 11:26:22 -05007255 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007256 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007257
jrkooncecba5d6c2019-08-29 11:26:22 -05007258 JsonAddMember(o, "primitives", std::move(primitives));
7259
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007260 if (mesh.weights.size()) {
7261 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7262 }
7263
7264 if (mesh.name.size()) {
7265 SerializeStringProperty("name", mesh.name, o);
7266 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007267
Selmar Kok81b672b2019-10-18 16:08:44 +02007268 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007269 if (mesh.extras.Type() != NULL_TYPE) {
7270 SerializeValue("extras", mesh.extras, o);
7271 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007272}
7273
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007274static void SerializeSpotLight(const SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007275 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7276 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7277 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007278 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007279 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007280 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007281}
7282
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007283static void SerializeGltfLight(const Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007284 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007285 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007286 if (light.range > 0.0) {
7287 SerializeNumberProperty("range", light.range, o);
7288 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007289 SerializeNumberArrayProperty("color", light.color, o);
7290 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007291 if (light.type == "spot") {
7292 json spot;
7293 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007294 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007295 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007296 SerializeExtensionMap(light.extensions, o);
7297 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007298 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007299 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007300}
7301
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007302static void SerializeGltfNode(const Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007303 if (node.translation.size() > 0) {
7304 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7305 }
7306 if (node.rotation.size() > 0) {
7307 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7308 }
7309 if (node.scale.size() > 0) {
7310 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7311 }
7312 if (node.matrix.size() > 0) {
7313 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7314 }
7315 if (node.mesh != -1) {
7316 SerializeNumberProperty<int>("mesh", node.mesh, o);
7317 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007318
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007319 if (node.skin != -1) {
7320 SerializeNumberProperty<int>("skin", node.skin, o);
7321 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007322
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007323 if (node.camera != -1) {
7324 SerializeNumberProperty<int>("camera", node.camera, o);
7325 }
7326
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007327 if (node.weights.size() > 0) {
7328 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7329 }
7330
Jens Olssona9718662018-05-24 15:48:49 +02007331 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007332 SerializeValue("extras", node.extras, o);
7333 }
7334
Selmar09d2ff12018-03-15 17:30:42 +01007335 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007336 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007337 SerializeNumberArrayProperty<int>("children", node.children, o);
7338}
7339
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007340static void SerializeGltfSampler(const Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007341 if (sampler.magFilter != -1) {
7342 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7343 }
7344 if (sampler.minFilter != -1) {
7345 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7346 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007347 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007348 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7349 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007350
7351 if (sampler.extras.Type() != NULL_TYPE) {
7352 SerializeValue("extras", sampler.extras, o);
7353 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007354}
7355
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007356static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007357 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007358 SerializeNumberProperty("zfar", camera.zfar, o);
7359 SerializeNumberProperty("znear", camera.znear, o);
7360 SerializeNumberProperty("xmag", camera.xmag, o);
7361 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007362
7363 if (camera.extras.Type() != NULL_TYPE) {
7364 SerializeValue("extras", camera.extras, o);
7365 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007366}
7367
7368static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007369 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007370 SerializeNumberProperty("zfar", camera.zfar, o);
7371 SerializeNumberProperty("znear", camera.znear, o);
7372 if (camera.aspectRatio > 0) {
7373 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7374 }
7375
7376 if (camera.yfov > 0) {
7377 SerializeNumberProperty("yfov", camera.yfov, o);
7378 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007379
7380 if (camera.extras.Type() != NULL_TYPE) {
7381 SerializeValue("extras", camera.extras, o);
7382 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007383}
7384
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007385static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007386 SerializeStringProperty("type", camera.type, o);
7387 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007388 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007389 }
7390
7391 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007392 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007393 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007394 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007395 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007396 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007397 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007398 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007399 } else {
7400 // ???
7401 }
Syoyofe77cc52020-05-09 02:41:07 +09007402
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007403 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007404 SerializeValue("extras", camera.extras, o);
7405 }
7406 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007407}
7408
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007409static void SerializeGltfScene(const Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007410 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7411
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007412 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007413 SerializeStringProperty("name", scene.name, o);
7414 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007415 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007416 SerializeValue("extras", scene.extras, o);
7417 }
7418 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007419}
7420
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007421static void SerializeGltfSkin(const Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007422 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007423 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007424
7425 if (skin.inverseBindMatrices >= 0) {
7426 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7427 }
7428
7429 if (skin.skeleton >= 0) {
7430 SerializeNumberProperty("skeleton", skin.skeleton, o);
7431 }
7432
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007433 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007434 SerializeStringProperty("name", skin.name, o);
7435 }
7436}
7437
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007438static void SerializeGltfTexture(const Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007439 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007440 SerializeNumberProperty("sampler", texture.sampler, o);
7441 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007442 if (texture.source > -1) {
7443 SerializeNumberProperty("source", texture.source, o);
7444 }
Christophe820ede82019-07-04 15:21:21 +09007445 if (texture.name.size()) {
7446 SerializeStringProperty("name", texture.name, o);
7447 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007448 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007449 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007450 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007451 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007452}
7453
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007454///
7455/// Serialize all properties except buffers and images.
7456///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007457static void SerializeGltfModel(const Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007458 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007459 if (model->accessors.size()) {
7460 json accessors;
7461 JsonReserveArray(accessors, model->accessors.size());
7462 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7463 json accessor;
7464 SerializeGltfAccessor(model->accessors[i], accessor);
7465 JsonPushBack(accessors, std::move(accessor));
7466 }
7467 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007468 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007469
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007470 // ANIMATIONS
7471 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007472 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007473 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007474 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7475 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007476 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007477 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007478 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007479 }
7480 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007481
jrkooncecba5d6c2019-08-29 11:26:22 -05007482 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007483 }
7484
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007485 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007486 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007487 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007488 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007489
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007490 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007491 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007492 json bufferViews;
7493 JsonReserveArray(bufferViews, model->bufferViews.size());
7494 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7495 json bufferView;
7496 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7497 JsonPushBack(bufferViews, std::move(bufferView));
7498 }
7499 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007500 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007501
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007502 // Extensions required
7503 if (model->extensionsRequired.size()) {
7504 SerializeStringArrayProperty("extensionsRequired",
7505 model->extensionsRequired, o);
7506 }
7507
7508 // MATERIALS
7509 if (model->materials.size()) {
7510 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007511 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007512 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7513 json material;
7514 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007515
7516 if (JsonIsNull(material)) {
7517 // Issue 294.
7518 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007519 // so the result may be null(unmodified) when all material parameters
7520 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007521 //
7522 // null is not allowed thus we create an empty JSON object.
7523 JsonSetObject(material);
7524 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007525 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007526 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007527 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007528 }
7529
7530 // MESHES
7531 if (model->meshes.size()) {
7532 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007533 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007534 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7535 json mesh;
7536 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007537 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007538 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007539 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007540 }
7541
7542 // NODES
7543 if (model->nodes.size()) {
7544 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007545 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007546 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7547 json node;
7548 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007549 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007550 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007551 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007552 }
7553
7554 // SCENE
7555 if (model->defaultScene > -1) {
7556 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7557 }
7558
7559 // SCENES
7560 if (model->scenes.size()) {
7561 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007562 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007563 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7564 json currentScene;
7565 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007566 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007567 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007568 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007569 }
7570
7571 // SKINS
7572 if (model->skins.size()) {
7573 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007574 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007575 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7576 json skin;
7577 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007578 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007579 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007580 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007581 }
7582
7583 // TEXTURES
7584 if (model->textures.size()) {
7585 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007586 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007587 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7588 json texture;
7589 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007590 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007591 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007592 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007593 }
7594
7595 // SAMPLERS
7596 if (model->samplers.size()) {
7597 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007598 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007599 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7600 json sampler;
7601 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007602 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007603 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007604 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007605 }
7606
7607 // CAMERAS
7608 if (model->cameras.size()) {
7609 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007610 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007611 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7612 json camera;
7613 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007614 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007615 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007616 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007617 }
7618
7619 // EXTENSIONS
7620 SerializeExtensionMap(model->extensions, o);
7621
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007622 auto extensionsUsed = model->extensionsUsed;
7623
7624 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007625 if (model->lights.size()) {
7626 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007627 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007628 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7629 json light;
7630 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007631 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007632 }
7633 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007634 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007635 json ext_j;
7636
jrkooncecba5d6c2019-08-29 11:26:22 -05007637 {
7638 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007639 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007640 JsonAssign(ext_j, GetValue(it));
7641 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007642 }
7643
jrkooncecba5d6c2019-08-29 11:26:22 -05007644 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007645
jrkooncecba5d6c2019-08-29 11:26:22 -05007646 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007647
7648 // Also add "KHR_lights_punctual" to `extensionsUsed`
7649 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007650 auto has_khr_lights_punctual =
7651 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7652 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007653 return (s.compare("KHR_lights_punctual") == 0);
7654 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007655
7656 if (has_khr_lights_punctual == extensionsUsed.end()) {
7657 extensionsUsed.push_back("KHR_lights_punctual");
7658 }
7659 }
7660 }
7661
7662 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007663 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007664 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007665 }
7666
7667 // EXTRAS
7668 if (model->extras.Type() != NULL_TYPE) {
7669 SerializeValue("extras", model->extras, o);
7670 }
7671}
7672
Johan Bowald52936a02019-07-17 09:06:45 +02007673static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007674 stream << content << std::endl;
7675 return true;
7676}
7677
7678static bool WriteGltfFile(const std::string &output,
7679 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007680#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007681#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007682 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007683#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007684 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7685 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7686 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7687 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007688 std::ostream gltfFile(&wfile_buf);
7689 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007690#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007691 std::ofstream gltfFile(output.c_str());
7692 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007693#endif
7694#else
7695 std::ofstream gltfFile(output.c_str());
7696 if (!gltfFile.is_open()) return false;
7697#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007698 return WriteGltfStream(gltfFile, content);
7699}
7700
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007701static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007702 const std::string &content,
7703 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007704 const std::string header = "glTF";
7705 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007706
Alexander Wood190382a2021-10-08 12:19:13 -04007707 const uint32_t content_size = uint32_t(content.size());
7708 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7709 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007710 const uint32_t content_padding_size =
7711 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7712 const uint32_t bin_padding_size =
7713 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007714
7715 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007716 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007717 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007718 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007719 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007720
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007721 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007722 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7723 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7724
7725 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007726 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007727 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007728 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007729 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007730 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007731 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007732 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007733
7734 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007735 if (content_padding_size > 0) {
7736 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007737 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007738 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007739 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007740 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007741 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7742 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007743 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007744 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007745 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007746 sizeof(bin_format));
7747 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7748 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007749 // Chunksize must be multiplies of 4, so pad with zeroes
7750 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007751 const std::vector<unsigned char> padding =
7752 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7753 stream.write(reinterpret_cast<const char *>(padding.data()),
7754 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007755 }
7756 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007757
7758 // TODO: Check error on stream.write
7759 return true;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007760}
7761
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007762static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007763 const std::string &content,
7764 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007765#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007766#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007767 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007768#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007769 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7770 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7771 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7772 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007773 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007774#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007775 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007776#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007777#else
7778 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007779#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007780 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007781}
7782
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007783bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007784 bool prettyPrint = true,
7785 bool writeBinary = false) {
7786 JsonDocument output;
7787
7788 /// Serialize all properties except buffers and images.
7789 SerializeGltfModel(model, output);
7790
7791 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007792 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007793 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007794 json buffers;
7795 JsonReserveArray(buffers, model->buffers.size());
7796 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7797 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007798 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7799 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007800 } else {
7801 SerializeGltfBuffer(model->buffers[i], buffer);
7802 }
7803 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007804 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007805 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007806 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007807
7808 // IMAGES
7809 if (model->images.size()) {
7810 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007811 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007812 for (unsigned int i = 0; i < model->images.size(); ++i) {
7813 json image;
7814
7815 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007816 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7817 // enabled, since we won't write separate images when writing to a stream
7818 // we
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007819 std::string uri;
Hendrik Schwanekamp41e11022022-06-29 12:22:40 +02007820 UpdateImageObject(model->images[i], dummystring, int(i), true,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007821 &this->WriteImageData, &uri,
7822 this->write_image_user_data_);
7823 SerializeGltfImage(model->images[i], uri, image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007824 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007825 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007826 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007827 }
7828
7829 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007830 return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007831 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007832 return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007833 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007834}
7835
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007836bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
7837 const std::string &filename,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007838 bool embedImages = false,
7839 bool embedBuffers = false,
7840 bool prettyPrint = true,
7841 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007842 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007843 std::string defaultBinFilename = GetBaseFilename(filename);
7844 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007845 std::string::size_type pos =
7846 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007847
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007848 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007849 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007850 }
johan bowald642a3432018-04-01 12:37:18 +02007851 std::string baseDir = GetBaseDir(filename);
7852 if (baseDir.empty()) {
7853 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007854 }
Johan Bowald52936a02019-07-17 09:06:45 +02007855 /// Serialize all properties except buffers and images.
7856 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007857
Selmar Kok7cb31e42018-10-05 16:02:29 +02007858 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007859 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007860 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007861 if (model->buffers.size()) {
7862 json buffers;
7863 JsonReserveArray(buffers, model->buffers.size());
7864 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7865 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007866 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7867 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007868 } else if (embedBuffers) {
7869 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007870 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007871 std::string binSavePath;
7872 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007873 if (!model->buffers[i].uri.empty() &&
7874 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007875 binUri = model->buffers[i].uri;
7876 } else {
7877 binUri = defaultBinFilename + defaultBinFileExt;
7878 bool inUse = true;
7879 int numUsed = 0;
7880 while (inUse) {
7881 inUse = false;
7882 for (const std::string &usedName : usedUris) {
7883 if (binUri.compare(usedName) != 0) continue;
7884 inUse = true;
7885 binUri = defaultBinFilename + std::to_string(numUsed++) +
7886 defaultBinFileExt;
7887 break;
7888 }
Selmar Kokc884e582018-10-05 16:25:54 +02007889 }
7890 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007891 usedUris.push_back(binUri);
7892 binSavePath = JoinPath(baseDir, binUri);
7893 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7894 binUri)) {
7895 return false;
7896 }
Selmar Kokc884e582018-10-05 16:25:54 +02007897 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007898 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007899 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007900 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007901 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007902
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007903 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007904 if (model->images.size()) {
7905 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007906 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007907 for (unsigned int i = 0; i < model->images.size(); ++i) {
7908 json image;
johan bowald642a3432018-04-01 12:37:18 +02007909
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007910 std::string uri;
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007911 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007912 &this->WriteImageData, &uri,
7913 this->write_image_user_data_);
7914 SerializeGltfImage(model->images[i], uri, image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007915 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007916 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007917 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007918 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007919
David Harmonda9eac22018-08-30 08:06:05 -04007920 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007921 return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007922 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007923 return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007924 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007925}
7926
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007927} // namespace tinygltf
7928
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007929#ifdef __clang__
7930#pragma clang diagnostic pop
7931#endif
7932
Syoyo Fujita612e5782022-09-18 21:01:39 +09007933#endif // TINYGLTF_IMPLEMENTATION