blob: 5d1407fd98fd4c8b8f522fb97321619ac20c3405 [file] [log] [blame]
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001//
Syoyo Fujita9a1ea7e2017-06-20 02:17:28 +09002// Header-only tiny glTF 2.0 loader and serializer.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003//
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005// The MIT License (MIT)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006//
Syoyo Fujitab702de72021-03-02 19:08:29 +09007// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many
Syoyo Fujita5b407452017-06-04 17:42:41 +09008// contributors.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09009//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090010// Permission is hereby granted, free of charge, to any person obtaining a copy
11// of this software and associated documentation files (the "Software"), to deal
12// in the Software without restriction, including without limitation the rights
13// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14// copies of the Software, and to permit persons to whom the Software is
15// furnished to do so, subject to the following conditions:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090016//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090017// The above copyright notice and this permission notice shall be included in
18// all copies or substantial portions of the Software.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090019//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090020// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26// THE SOFTWARE.
27
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090028// Version:
Syoyo Fujitae0b62552022-09-19 03:36:58 +090029// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383.
Syoyo Fujitaeec4c982022-09-16 17:27:20 +090030// - v2.6.2 Fix out-of-bounds access of accessors. PR#379.
Syoyo Fujita4581d372022-09-06 22:02:31 +090031// - v2.6.1 Better GLB validation check when loading.
Syoyo Fujita3bddc092022-08-19 18:21:24 +090032// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
33// Disable expanding file path for security(no use of awkward `wordexp` anymore).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +090034// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
imallettd9ce9eb2022-10-07 10:37:09 -070035// - v2.4.3 Fix null object output when material has all default
Syoyo Fujita7905a5b2021-01-21 20:33:11 +090036// parameters.
Syoyo Fujitac4166e42020-01-08 02:38:01 +090037// - v2.4.2 Decode percent-encoded URI.
Syoyo Fujita6e08b172019-10-30 17:25:38 +090038// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
39// `extras` property.
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +090040// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
Syoyo Fujitaee179b22019-08-16 13:11:30 +090041// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
Syoyo Fujita046400b2019-07-24 19:26:48 +090042// - v2.3.0 Modified Material representation according to glTF 2.0 schema
43// (and introduced TextureInfo class)
Syoyo Fujita150f2432019-07-25 19:22:44 +090044// Change the behavior of `Value::IsNumber`. It return true either the
45// value is int or real.
Syoyo Fujitaca56f722019-03-07 21:04:25 +090046// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
47// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090048// - v2.1.0 Add draco compression.
imallettd9ce9eb2022-10-07 10:37:09 -070049// - v2.0.1 Add comparison feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090050// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090051//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090052// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090053//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090054// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090055// - base64: base64 decode/encode library.
56// - stb_image: Image loading library.
57//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090058#ifndef TINY_GLTF_H_
59#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090060
Syoyo Fujitad42767e2018-03-15 21:52:00 -050061#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090062#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090063#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050064#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020065#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090066#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090067#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090068#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090069#include <string>
70#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090071
zbendefy69eeea12022-09-05 23:54:57 +020072//Auto-detect C++14 standard version
73#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
74#define TINYGLTF_USE_CPP14
75#endif
76
jrkoonce51453942019-09-03 09:48:30 -050077#ifndef TINYGLTF_USE_CPP14
78#include <functional>
79#endif
80
Sascha Willems5f9cb242018-12-28 20:53:41 +010081#ifdef __ANDROID__
82#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
83#include <android/asset_manager.h>
84#endif
85#endif
86
Selmar Kok79e3df22019-10-29 16:22:07 +010087#ifdef __GNUC__
88#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010089#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010090#else
91#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010092#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010093#else
94#define TINYGLTF_NOEXCEPT noexcept
95#endif
96
Syoyo Fujita6e08b172019-10-30 17:25:38 +090097#define DEFAULT_METHODS(x) \
98 ~x() = default; \
99 x(const x &) = default; \
100 x(x &&) TINYGLTF_NOEXCEPT = default; \
101 x &operator=(const x &) = default; \
102 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100103
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900104namespace tinygltf {
105
106#define TINYGLTF_MODE_POINTS (0)
107#define TINYGLTF_MODE_LINE (1)
108#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +0100109#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900110#define TINYGLTF_MODE_TRIANGLES (4)
111#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
112#define TINYGLTF_MODE_TRIANGLE_FAN (6)
113
114#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
115#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
116#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
117#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
118#define TINYGLTF_COMPONENT_TYPE_INT (5124)
119#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
120#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900121#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
122 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
123 // support double type even the schema seems allow any value of
124 // integer:
125 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900126
Syoyo Fujitac2615632016-06-19 21:56:06 +0900127#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
128#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
129#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
130#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
131#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
132#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
133
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100134#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900135#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400136#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900137
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400138// Redeclarations of the above for technique.parameters.
139#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
140#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
141#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
142#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
143#define TINYGLTF_PARAMETER_TYPE_INT (5124)
144#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
145#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
146
147#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
148#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
149#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
150
151#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
152#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
153#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
154
155#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
156#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
157#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
158#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
159
160#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
161#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
162#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
163
164#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
165
166// End parameter types
167
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900168#define TINYGLTF_TYPE_VEC2 (2)
169#define TINYGLTF_TYPE_VEC3 (3)
170#define TINYGLTF_TYPE_VEC4 (4)
171#define TINYGLTF_TYPE_MAT2 (32 + 2)
172#define TINYGLTF_TYPE_MAT3 (32 + 3)
173#define TINYGLTF_TYPE_MAT4 (32 + 4)
174#define TINYGLTF_TYPE_SCALAR (64 + 1)
175#define TINYGLTF_TYPE_VECTOR (64 + 4)
176#define TINYGLTF_TYPE_MATRIX (64 + 16)
177
178#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
179#define TINYGLTF_IMAGE_FORMAT_PNG (1)
180#define TINYGLTF_IMAGE_FORMAT_BMP (2)
181#define TINYGLTF_IMAGE_FORMAT_GIF (3)
182
Luke San Antonio6d616f52016-06-23 14:09:23 -0400183#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
184#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900185#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400186#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
187#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
188
Syoyo Fujitabde70212016-02-07 17:38:17 +0900189#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
190#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
191
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900192#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
193#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
194
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400195#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
196#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
197
Selmar Kok31cb7f92018-10-03 15:39:05 +0200198#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900199#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200200
Sascha Willems5f9cb242018-12-28 20:53:41 +0100201#ifdef __ANDROID__
202#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000203#ifdef TINYGLTF_IMPLEMENTATION
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000204AAssetManager *asset_manager = nullptr;
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000205#else
206extern AAssetManager *asset_manager;
207#endif
Sascha Willems5f9cb242018-12-28 20:53:41 +0100208#endif
209#endif
210
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900211typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100212 NULL_TYPE,
213 REAL_TYPE,
214 INT_TYPE,
215 BOOL_TYPE,
216 STRING_TYPE,
217 ARRAY_TYPE,
218 BINARY_TYPE,
219 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900220} Type;
221
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500222static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900223 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
224 return 1;
225 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
226 return 1;
227 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
228 return 2;
229 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
230 return 2;
231 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
232 return 4;
233 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
234 return 4;
235 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
236 return 4;
237 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
238 return 8;
239 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700240 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900241 return -1;
242 }
243}
244
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400245static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900246 if (ty == TINYGLTF_TYPE_SCALAR) {
247 return 1;
248 } else if (ty == TINYGLTF_TYPE_VEC2) {
249 return 2;
250 } else if (ty == TINYGLTF_TYPE_VEC3) {
251 return 3;
252 } else if (ty == TINYGLTF_TYPE_VEC4) {
253 return 4;
254 } else if (ty == TINYGLTF_TYPE_MAT2) {
255 return 4;
256 } else if (ty == TINYGLTF_TYPE_MAT3) {
257 return 9;
258 } else if (ty == TINYGLTF_TYPE_MAT4) {
259 return 16;
260 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700261 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900262 return -1;
263 }
264}
265
Syoyo Fujita150f2432019-07-25 19:22:44 +0900266// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200267bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900268bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
269 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200270
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900271#ifdef __clang__
272#pragma clang diagnostic push
273// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900274#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900275#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900276#endif
277
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900278// Simple class to represent JSON object
279class Value {
280 public:
281 typedef std::vector<Value> Array;
282 typedef std::map<std::string, Value> Object;
283
Syoyo Fujita046400b2019-07-24 19:26:48 +0900284 Value()
285 : type_(NULL_TYPE),
286 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900287 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900288 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900289
290 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900291 explicit Value(int i) : type_(INT_TYPE) {
292 int_value_ = i;
293 real_value_ = i;
294 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900295 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900296 explicit Value(const std::string &s) : type_(STRING_TYPE) {
297 string_value_ = s;
298 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900299 explicit Value(std::string &&s)
300 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900301 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900302 binary_value_.resize(n);
303 memcpy(binary_value_.data(), p, n);
304 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900305 explicit Value(std::vector<unsigned char> &&v) noexcept
306 : type_(BINARY_TYPE),
307 binary_value_(std::move(v)) {}
308 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
309 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
310 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500311
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900312 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
313 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
314 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100315
316 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900317
Hill Mad1e32862021-02-20 22:30:44 -0800318 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900319
320 bool IsBool() const { return (type_ == BOOL_TYPE); }
321
322 bool IsInt() const { return (type_ == INT_TYPE); }
323
Syoyo Fujita150f2432019-07-25 19:22:44 +0900324 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900325
Syoyo Fujita150f2432019-07-25 19:22:44 +0900326 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900327
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900328 bool IsString() const { return (type_ == STRING_TYPE); }
329
330 bool IsBinary() const { return (type_ == BINARY_TYPE); }
331
332 bool IsArray() const { return (type_ == ARRAY_TYPE); }
333
334 bool IsObject() const { return (type_ == OBJECT_TYPE); }
335
Syoyo Fujita150f2432019-07-25 19:22:44 +0900336 // Use this function if you want to have number value as double.
337 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900338 if (type_ == INT_TYPE) {
339 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900340 } else {
341 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900342 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900343 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900344
Syoyo Fujita150f2432019-07-25 19:22:44 +0900345 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900346 // TODO(syoyo): Support int value larger than 32 bits
347 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900348 if (type_ == REAL_TYPE) {
349 return int(real_value_);
350 } else {
351 return int_value_;
352 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900353 }
354
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900355 // Accessor
356 template <typename T>
357 const T &Get() const;
358 template <typename T>
359 T &Get();
360
361 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900362 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900363 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900364 assert(IsArray());
365 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900366 return (static_cast<size_t>(idx) < array_value_.size())
367 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900368 : null_value;
369 }
370
371 // Lookup value from a key-value pair
372 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900373 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900374 assert(IsObject());
375 Object::const_iterator it = object_value_.find(key);
376 return (it != object_value_.end()) ? it->second : null_value;
377 }
378
379 size_t ArrayLen() const {
380 if (!IsArray()) return 0;
381 return array_value_.size();
382 }
383
384 // Valid only for object type.
385 bool Has(const std::string &key) const {
386 if (!IsObject()) return false;
387 Object::const_iterator it = object_value_.find(key);
388 return (it != object_value_.end()) ? true : false;
389 }
390
391 // List keys
392 std::vector<std::string> Keys() const {
393 std::vector<std::string> keys;
394 if (!IsObject()) return keys; // empty
395
396 for (Object::const_iterator it = object_value_.begin();
397 it != object_value_.end(); ++it) {
398 keys.push_back(it->first);
399 }
400
401 return keys;
402 }
403
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900404 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900405
406 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000407
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900408 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900409 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900410
Syoyo Fujita046400b2019-07-24 19:26:48 +0900411 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900412 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900413 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900414 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900415 Array array_value_;
416 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900417 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900418};
419
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900420#ifdef __clang__
421#pragma clang diagnostic pop
422#endif
423
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900424#define TINYGLTF_VALUE_GET(ctype, var) \
425 template <> \
426 inline const ctype &Value::Get<ctype>() const { \
427 return var; \
428 } \
429 template <> \
430 inline ctype &Value::Get<ctype>() { \
431 return var; \
432 }
433TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900434TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900435TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900436TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900437TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900438TINYGLTF_VALUE_GET(Value::Array, array_value_)
439TINYGLTF_VALUE_GET(Value::Object, object_value_)
440#undef TINYGLTF_VALUE_GET
441
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900442#ifdef __clang__
443#pragma clang diagnostic push
444#pragma clang diagnostic ignored "-Wc++98-compat"
445#pragma clang diagnostic ignored "-Wpadded"
446#endif
447
imallettd9ce9eb2022-10-07 10:37:09 -0700448/// Aggregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100449using ColorValue = std::array<double, 4>;
450
Syoyo Fujita046400b2019-07-24 19:26:48 +0900451// === legacy interface ====
452// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500453struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200454 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700455 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900456 std::string string_value;
457 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000458 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200459 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900460
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500461 // context sensitive methods. depending the type of the Parameter you are
462 // accessing, these are either valid or not
463 // If this parameter represent a texture map in a material, will return the
464 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100465
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500466 /// Return the index of a texture if this Parameter is a texture map.
467 /// Returned value is only valid if the parameter represent a texture from a
468 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100469 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100470 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500471 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100472 return int(it->second);
473 }
474 return -1;
475 }
476
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000477 /// Return the index of a texture coordinate set if this Parameter is a
478 /// texture map. Returned value is only valid if the parameter represent a
479 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100480 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000481 const auto it = json_double_value.find("texCoord");
482 if (it != std::end(json_double_value)) {
483 return int(it->second);
484 }
imallettd9ce9eb2022-10-07 10:37:09 -0700485 // As per the spec, if texCoord is omitted, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000486 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100487 }
488
Christophe820ede82019-07-04 15:21:21 +0900489 /// Return the scale of a texture if this Parameter is a normal texture map.
490 /// Returned value is only valid if the parameter represent a normal texture
491 /// from a material
492 double TextureScale() const {
493 const auto it = json_double_value.find("scale");
494 if (it != std::end(json_double_value)) {
495 return it->second;
496 }
imallettd9ce9eb2022-10-07 10:37:09 -0700497 // As per the spec, if scale is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200498 return 1;
499 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200500
Arthur Brainville8a98d982019-07-05 00:26:02 +0200501 /// Return the strength of a texture if this Parameter is a an occlusion map.
502 /// Returned value is only valid if the parameter represent an occlusion map
503 /// from a material
504 double TextureStrength() const {
505 const auto it = json_double_value.find("strength");
506 if (it != std::end(json_double_value)) {
507 return it->second;
508 }
imallettd9ce9eb2022-10-07 10:37:09 -0700509 // As per the spec, if strength is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200510 return 1;
Christophe820ede82019-07-04 15:21:21 +0900511 }
512
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500513 /// Material factor, like the roughness or metalness of a material
514 /// Returned value is only valid if the parameter represent a texture from a
515 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700516 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100517
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500518 /// Return the color of a material
519 /// Returned value is only valid if the parameter represent a texture from a
520 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100521 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100522 return {
imallettd9ce9eb2022-10-07 10:37:09 -0700523 {// this aggregate initialize the std::array object, and uses C++11 RVO.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500524 number_array[0], number_array[1], number_array[2],
525 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100526 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200527
Selmar Kokff2b1f92019-10-21 17:58:09 +0200528 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100529 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900530 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100531};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900532
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900533#ifdef __clang__
534#pragma clang diagnostic pop
535#endif
536
537#ifdef __clang__
538#pragma clang diagnostic push
539#pragma clang diagnostic ignored "-Wpadded"
540#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900541
Syoyo Fujitabde70212016-02-07 17:38:17 +0900542typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200543typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900544
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000545struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900546 int sampler; // required
547 int target_node; // required (index of the node to target)
548 std::string target_path; // required in ["translation", "rotation", "scale",
549 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900550 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200551 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100552 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900553
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900554 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
555 std::string extras_json_string;
556 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100557 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900558
Syoyo Fujita5b407452017-06-04 17:42:41 +0900559 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100560 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900561 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000562};
563
564struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900565 int input; // required
566 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200567 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
568 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200569 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900570 ExtensionMap extensions;
571
572 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
573 std::string extras_json_string;
574 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000575
Syoyo Fujita5b407452017-06-04 17:42:41 +0900576 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100577 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900578 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000579};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900580
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900581struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900582 std::string name;
583 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000584 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900585 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200586 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200587
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900588 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
589 std::string extras_json_string;
590 std::string extensions_json_string;
591
Selmar Kokff2b1f92019-10-21 17:58:09 +0200592 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100593 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900594 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900595};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900596
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000597struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900598 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900599 int inverseBindMatrices; // required here but not in the spec
600 int skeleton; // The index of the node used as a skeleton root
601 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000602
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900603 Value extras;
604 ExtensionMap extensions;
605
606 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
607 std::string extras_json_string;
608 std::string extensions_json_string;
609
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900610 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000611 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000612 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000613 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100614 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900615 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000616};
617
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000618struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900619 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900620 // glTF 2.0 spec does not define default value for `minFilter` and
621 // `magFilter`. Set -1 in TinyGLTF(issue #186)
622 int minFilter =
623 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800624 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900625 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
626 int magFilter =
627 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
628 int wrapS =
629 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
630 // "REPEAT"], default "REPEAT"
631 int wrapT =
632 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
633 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900634 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
635 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900636
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900637 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900638 ExtensionMap extensions;
639
640 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
641 std::string extras_json_string;
642 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900643
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000644 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900645 : minFilter(-1),
646 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800647 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900648 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100649 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900650 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000651};
652
Syoyo Fujita5b407452017-06-04 17:42:41 +0900653struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900654 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900655 int width;
656 int height;
657 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000658 int bits; // bit depth per channel. 8(byte), 16 or 32.
659 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
660 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900661 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900662 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500663 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
664 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900665 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
666 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900667 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900668 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900669
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900670 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
671 std::string extras_json_string;
672 std::string extensions_json_string;
673
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900674 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
675 // compressed for "image/jpeg" mime) This feature is good if you use custom
676 // image loader function. (e.g. delayed decoding of images for faster glTF
677 // parsing) Default parser for Image does not provide as-is loading feature at
678 // the moment. (You can manipulate this by providing your own LoadImageData
679 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200680 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900681
682 Image() : as_is(false) {
683 bufferView = -1;
684 width = -1;
685 height = -1;
686 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900687 bits = -1;
688 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900689 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100690 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500691
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900692 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000693};
694
695struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200696 std::string name;
697
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000698 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200699 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900700 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200701 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900702
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900703 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
704 std::string extras_json_string;
705 std::string extensions_json_string;
706
Syoyo Fujita5b407452017-06-04 17:42:41 +0900707 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100708 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500709
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900710 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000711};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900712
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900713struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900714 int index = -1; // required.
715 int texCoord; // The set index of texture's TEXCOORD attribute used for
716 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900717
718 Value extras;
719 ExtensionMap extensions;
720
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900721 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
722 std::string extras_json_string;
723 std::string extensions_json_string;
724
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900725 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100726 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900727 bool operator==(const TextureInfo &) const;
728};
729
730struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900731 int index = -1; // required
732 int texCoord; // The set index of texture's TEXCOORD attribute used for
733 // texture coordinate mapping.
734 double scale; // scaledNormal = normalize((<sampled normal texture value>
735 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900736
737 Value extras;
738 ExtensionMap extensions;
739
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900740 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
741 std::string extras_json_string;
742 std::string extensions_json_string;
743
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900744 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100745 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900746 bool operator==(const NormalTextureInfo &) const;
747};
748
749struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900750 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900751 int texCoord; // The set index of texture's TEXCOORD attribute used for
752 // texture coordinate mapping.
753 double strength; // occludedColor = lerp(color, color * <sampled occlusion
754 // texture value>, <occlusion strength>)
755
756 Value extras;
757 ExtensionMap extensions;
758
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900759 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
760 std::string extras_json_string;
761 std::string extensions_json_string;
762
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900763 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100764 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900765 bool operator==(const OcclusionTextureInfo &) const;
766};
767
768// pbrMetallicRoughness class defined in glTF 2.0 spec.
769struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900770 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900771 TextureInfo baseColorTexture;
772 double metallicFactor; // default 1
773 double roughnessFactor; // default 1
774 TextureInfo metallicRoughnessTexture;
775
776 Value extras;
777 ExtensionMap extensions;
778
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900779 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
780 std::string extras_json_string;
781 std::string extensions_json_string;
782
783 PbrMetallicRoughness()
784 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
785 metallicFactor(1.0),
786 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100787 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900788 bool operator==(const PbrMetallicRoughness &) const;
789};
790
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000791// Each extension should be stored in a ParameterMap.
792// members not in the values could be included in the ParameterMap
793// to keep a single material model
794struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900795 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900796
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900797 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900798 std::string alphaMode; // default "OPAQUE"
799 double alphaCutoff; // default 0.5
800 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900801
802 PbrMetallicRoughness pbrMetallicRoughness;
803
804 NormalTextureInfo normalTexture;
805 OcclusionTextureInfo occlusionTexture;
806 TextureInfo emissiveTexture;
807
Syoyo Fujita046400b2019-07-24 19:26:48 +0900808 // For backward compatibility
809 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
810 ParameterMap values;
811 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100812
813 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900814 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200815
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900816 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
817 std::string extras_json_string;
818 std::string extensions_json_string;
819
Syoyo Fujita046400b2019-07-24 19:26:48 +0900820 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100821 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900822
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900823 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000824};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900825
Syoyo Fujita5b407452017-06-04 17:42:41 +0900826struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900827 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900828 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900829 size_t byteOffset{0}; // minimum 0, default 0
830 size_t byteLength{0}; // required, minimum 1. 0 = invalid
831 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900832 // understood to be tightly packed
833 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
imallettd9ce9eb2022-10-07 10:37:09 -0700834 // or attribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900835 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900836 ExtensionMap extensions;
837
838 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
839 std::string extras_json_string;
840 std::string extensions_json_string;
841
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900842 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900843
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900844 BufferView()
845 : buffer(-1),
846 byteOffset(0),
847 byteLength(0),
848 byteStride(0),
849 target(0),
850 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100851 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900852 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000853};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900854
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000855struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900856 int bufferView; // optional in spec but required here since sparse accessor
857 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900858 std::string name;
859 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100860 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000861 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900862 size_t count; // required
863 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900864 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900865 ExtensionMap extensions;
866
867 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
868 std::string extras_json_string;
869 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000870
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900871 std::vector<double>
872 minValues; // optional. integer value is promoted to double
873 std::vector<double>
874 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900875
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100876 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000877 int count;
878 bool isSparse;
879 struct {
880 int byteOffset;
881 int bufferView;
882 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
883 } indices;
884 struct {
885 int bufferView;
886 int byteOffset;
887 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100888 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000889
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900890 ///
891 /// Utility function to compute byteStride for a given bufferView object.
892 /// Returns -1 upon invalid glTF value or parameter configuration.
893 ///
894 int ByteStride(const BufferView &bufferViewObject) const {
895 if (bufferViewObject.byteStride == 0) {
896 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500897 int componentSizeInBytes =
898 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900899 if (componentSizeInBytes <= 0) {
900 return -1;
901 }
902
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900903 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
904 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900905 return -1;
906 }
907
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900908 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900909 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700910 // Check if byteStride is a multiple of the size of the accessor's component
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500911 // type.
912 int componentSizeInBytes =
913 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900914 if (componentSizeInBytes <= 0) {
915 return -1;
916 }
917
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900918 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900919 return -1;
920 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100921 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900922 }
923
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900924 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900925 }
926
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900927 Accessor()
928 : bufferView(-1),
929 byteOffset(0),
930 normalized(false),
931 componentType(-1),
932 count(0),
933 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000934 sparse.isSparse = false;
935 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100936 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900937 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000938};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900939
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900940struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200941 double aspectRatio; // min > 0
942 double yfov; // required. min > 0
943 double zfar; // min > 0
944 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900945
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900946 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900947 : aspectRatio(0.0),
948 yfov(0.0),
imallettd9ce9eb2022-10-07 10:37:09 -0700949 zfar(0.0) // 0 = use infinite projection matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900950 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900951 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100952 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900953 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900954
Selmar09d2ff12018-03-15 17:30:42 +0100955 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900956 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900957
958 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
959 std::string extras_json_string;
960 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900961};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000962
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900963struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200964 double xmag; // required. must not be zero.
965 double ymag; // required. must not be zero.
966 double zfar; // required. `zfar` must be greater than `znear`.
967 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000968
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900969 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100970 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900971 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900972
Selmar09d2ff12018-03-15 17:30:42 +0100973 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900974 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900975
976 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
977 std::string extras_json_string;
978 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900979};
980
981struct Camera {
982 std::string type; // required. "perspective" or "orthographic"
983 std::string name;
984
985 PerspectiveCamera perspective;
986 OrthographicCamera orthographic;
987
988 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100989 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900990 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900991
Selmar09d2ff12018-03-15 17:30:42 +0100992 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000993 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900994
995 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
996 std::string extras_json_string;
997 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900998};
999
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001000struct Primitive {
1001 std::map<std::string, int> attributes; // (required) A dictionary object of
1002 // integer, where each integer
1003 // is the index of the accessor
1004 // containing an attribute.
1005 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +09001006 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001007 int indices; // The index of the accessor that contains the indices.
1008 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001009 std::vector<std::map<std::string, int> > targets; // array of morph targets,
imallettd9ce9eb2022-10-07 10:37:09 -07001010 // where each target is a dict with attributes in ["POSITION, "NORMAL",
Syoyo Fujita5b407452017-06-04 17:42:41 +09001011 // "TANGENT"] pointing
1012 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -05001013 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001014 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001015
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001016 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1017 std::string extras_json_string;
1018 std::string extensions_json_string;
1019
Syoyo Fujita5b407452017-06-04 17:42:41 +09001020 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001021 material = -1;
1022 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001023 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001024 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001025 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001026 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001027};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001028
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001029struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001030 std::string name;
1031 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001032 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001033 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001034 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001035
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001036 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1037 std::string extras_json_string;
1038 std::string extensions_json_string;
1039
jrkoonced1e14722019-08-27 11:51:02 -05001040 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001041 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001042 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001043};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001044
1045class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001046 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001047 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001048
Selmar Kokb74fade2019-10-29 16:09:32 +01001049 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001050
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001051 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001052
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001053 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001054
1055 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001056 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001057 int mesh;
1058 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001059 std::vector<double> rotation; // length must be 0 or 4
1060 std::vector<double> scale; // length must be 0 or 3
1061 std::vector<double> translation; // length must be 0 or 3
1062 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001063 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001064
Selmar09d2ff12018-03-15 17:30:42 +01001065 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001066 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001067
1068 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1069 std::string extras_json_string;
1070 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001071};
1072
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001073struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001074 std::string name;
1075 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001076 std::string
1077 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001078 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001079 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001080 ExtensionMap extensions;
1081
1082 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1083 std::string extras_json_string;
1084 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001085
Selmar Kokb74fade2019-10-29 16:09:32 +01001086 Buffer() = default;
1087 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001088 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001089};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001090
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001091struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001092 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001093 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001094 std::string minVersion;
1095 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001096 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001097 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001098
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001099 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1100 std::string extras_json_string;
1101 std::string extensions_json_string;
1102
jrkoonced1e14722019-08-27 11:51:02 -05001103 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001104 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001105 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001106};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001107
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001108struct Scene {
1109 std::string name;
1110 std::vector<int> nodes;
1111
Selmar09d2ff12018-03-15 17:30:42 +01001112 ExtensionMap extensions;
1113 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001114
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001115 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1116 std::string extras_json_string;
1117 std::string extensions_json_string;
1118
jrkoonced1e14722019-08-27 11:51:02 -05001119 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001120 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001121 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001122};
1123
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001124struct SpotLight {
1125 double innerConeAngle;
1126 double outerConeAngle;
1127
Johan Bowald52936a02019-07-17 09:06:45 +02001128 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001129 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001130 bool operator==(const SpotLight &) const;
1131
1132 ExtensionMap extensions;
1133 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001134
1135 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1136 std::string extras_json_string;
1137 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001138};
1139
Emanuel Schrade186322b2017-11-06 11:14:41 +01001140struct Light {
1141 std::string name;
1142 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001143 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001144 std::string type;
imallettd9ce9eb2022-10-07 10:37:09 -07001145 double range{0.0}; // 0.0 = infinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001146 SpotLight spot;
1147
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001148 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001149 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001150
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001151 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001152
1153 ExtensionMap extensions;
1154 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001155
1156 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1157 std::string extras_json_string;
1158 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001159};
1160
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001161class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001162 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001163 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001164 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001165
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001166 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001167
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001168 std::vector<Accessor> accessors;
1169 std::vector<Animation> animations;
1170 std::vector<Buffer> buffers;
1171 std::vector<BufferView> bufferViews;
1172 std::vector<Material> materials;
1173 std::vector<Mesh> meshes;
1174 std::vector<Node> nodes;
1175 std::vector<Texture> textures;
1176 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001177 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001178 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001179 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001180 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001181 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001182
sammyKhana0a62bd2020-01-17 13:41:16 +01001183 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001184 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001185 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001186
1187 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001188
1189 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001190 ExtensionMap extensions;
1191
1192 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1193 std::string extras_json_string;
1194 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001195};
1196
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001197enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001198 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001199 REQUIRE_VERSION = 0x01,
1200 REQUIRE_SCENE = 0x02,
1201 REQUIRE_SCENES = 0x04,
1202 REQUIRE_NODES = 0x08,
1203 REQUIRE_ACCESSORS = 0x10,
1204 REQUIRE_BUFFERS = 0x20,
1205 REQUIRE_BUFFER_VIEWS = 0x40,
1206 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001207};
1208
Squareysff644d82018-03-13 22:36:18 +01001209///
1210/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1211///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001212typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1213 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001214 const unsigned char *, int,
1215 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001216
johan bowald642a3432018-04-01 12:37:18 +02001217///
1218/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1219///
1220typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1221 Image *, bool, void *);
1222
Squareys2d3594d2018-03-13 22:40:53 +01001223#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001224// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001225bool LoadImageData(Image *image, const int image_idx, std::string *err,
1226 std::string *warn, int req_width, int req_height,
1227 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001228#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001229
johan bowald642a3432018-04-01 12:37:18 +02001230#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1231// Declaration of default image writer callback
1232bool WriteImageData(const std::string *basepath, const std::string *filename,
1233 Image *image, bool embedImages, void *);
1234#endif
1235
Paolo Jovone6601bf2018-07-07 20:43:33 +02001236///
1237/// FilExistsFunction type. Signature for custom filesystem callbacks.
1238///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001239typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001240
1241///
1242/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1243///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001244typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001245
1246///
1247/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1248///
1249typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001250 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001251 void *);
1252
1253///
1254/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1255///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001256typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001257 const std::vector<unsigned char> &,
1258 void *);
1259
1260///
1261/// A structure containing all required filesystem callbacks and a pointer to
1262/// their user data.
1263///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001264struct FsCallbacks {
1265 FileExistsFunction FileExists;
1266 ExpandFilePathFunction ExpandFilePath;
1267 ReadWholeFileFunction ReadWholeFile;
1268 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001269
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001270 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001271};
1272
1273#ifndef TINYGLTF_NO_FS
1274// Declaration of default filesystem callbacks
1275
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001276bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001277
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001278///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001279/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001280/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001281///
1282/// @param[in] filepath File path string. Assume UTF-8
1283/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1284///
1285std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001286
1287bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001288 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001289
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001290bool WriteWholeFile(std::string *err, const std::string &filepath,
1291 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001292#endif
1293
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001294///
imallettd9ce9eb2022-10-07 10:37:09 -07001295/// glTF Parser/Serializer context.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001296///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001297class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001298 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001299#ifdef __clang__
1300#pragma clang diagnostic push
1301#pragma clang diagnostic ignored "-Wc++98-compat"
1302#endif
1303
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001304 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001305
1306#ifdef __clang__
1307#pragma clang diagnostic pop
1308#endif
1309
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001310 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001311
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001312 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001313 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001314 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001315 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001316 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001317 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001318 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001319 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001320
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001321 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001322 /// Loads glTF ASCII asset from string(memory).
1323 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001324 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1325 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1326 /// message to `warn` for example it fails to load asserts. Returns false and
1327 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001328 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001329 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1330 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001331 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001332 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001333
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001334 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001335 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001336 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001337 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001338 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001339 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001340 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001341 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001342
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001343 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001344 /// Loads glTF binary asset from memory.
1345 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001346 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1347 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001348 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001349 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001350 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001351 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001352 const unsigned char *bytes,
1353 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001354 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001355 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001356
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001357 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001358 /// Write glTF to stream, buffers and images will be embedded
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001359 ///
1360 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1361 bool prettyPrint, bool writeBinary);
1362
1363 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001364 /// Write glTF to file.
1365 ///
johan bowald642a3432018-04-01 12:37:18 +02001366 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001367 bool embedImages, bool embedBuffers,
1368 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001369
Squareysff644d82018-03-13 22:36:18 +01001370 ///
1371 /// Set callback to use for loading image data
1372 ///
1373 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1374
johan bowald642a3432018-04-01 12:37:18 +02001375 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001376 /// Unset(remove) callback of loading image data
1377 ///
1378 void RemoveImageLoader();
1379
1380 ///
johan bowald642a3432018-04-01 12:37:18 +02001381 /// Set callback to use for writing image data
1382 ///
1383 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1384
Paolo Jovone6601bf2018-07-07 20:43:33 +02001385 ///
1386 /// Set callbacks to use for filesystem (fs) access and their user data
1387 ///
1388 void SetFsCallbacks(FsCallbacks callbacks);
1389
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001390 ///
1391 /// Set serializing default values(default = false).
1392 /// When true, default values are force serialized to .glTF.
imallettd9ce9eb2022-10-07 10:37:09 -07001393 /// This may be helpful if you want to serialize a full description of glTF
Syoyo Fujitaff515702019-08-24 16:29:14 +09001394 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001395 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001396 /// TODO(LTE): Supply parsing option as function arguments to
1397 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001398 ///
1399 void SetSerializeDefaultValues(const bool enabled) {
1400 serialize_default_values_ = enabled;
1401 }
1402
Syoyo Fujitaff515702019-08-24 16:29:14 +09001403 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001404
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001405 ///
1406 /// Store original JSON string for `extras` and `extensions`.
1407 /// This feature will be useful when the user want to reconstruct custom data
1408 /// structure from JSON string.
1409 ///
1410 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1411 store_original_json_for_extras_and_extensions_ = enabled;
1412 }
1413
1414 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1415 return store_original_json_for_extras_and_extensions_;
1416 }
1417
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001418 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001419 /// Specify whether preserve image channels when loading images or not.
1420 /// (Not effective when the user supplies their own LoadImageData callbacks)
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001421 ///
1422 void SetPreserveImageChannels(bool onoff) {
1423 preserve_image_channels_ = onoff;
1424 }
1425
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001426 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001427
Syoyo Fujitabeded612016-05-01 20:03:43 +09001428 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001429 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001430 /// Loads glTF asset from string(memory).
1431 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001432 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001433 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001434 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001435 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1436 const char *str, const unsigned int length,
1437 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001438
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001439 const unsigned char *bin_data_ = nullptr;
1440 size_t bin_size_ = 0;
1441 bool is_binary_ = false;
1442
Syoyo Fujitaff515702019-08-24 16:29:14 +09001443 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001444
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001445 bool store_original_json_for_extras_and_extensions_ = false;
1446
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001447 bool preserve_image_channels_ = false; /// Default false(expand channels to
1448 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001449
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001450 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001451 std::string warn_;
1452 std::string err_;
1453
Paolo Jovone6601bf2018-07-07 20:43:33 +02001454 FsCallbacks fs = {
1455#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001456 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1457 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001458
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001459 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001460#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001461 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001462
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001463 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001464#endif
1465 };
1466
Squareysff644d82018-03-13 22:36:18 +01001467 LoadImageDataFunction LoadImageData =
1468#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001469 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001470#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001471 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001472#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001473 void *load_image_user_data_{nullptr};
1474 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001475
1476 WriteImageDataFunction WriteImageData =
1477#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001478 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001479#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001480 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001481#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001482 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001483};
1484
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001485#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001486#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001487#endif
1488
Syoyo Fujita7c877972016-03-08 01:31:49 +09001489} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001490
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001491#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001492
Selmar Kok31cb7f92018-10-03 15:39:05 +02001493#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001494#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001495//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001496#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001497#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001498#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001499#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001500#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001501
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001502#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001503// Disable some warnings for external files.
1504#pragma clang diagnostic push
1505#pragma clang diagnostic ignored "-Wfloat-equal"
1506#pragma clang diagnostic ignored "-Wexit-time-destructors"
1507#pragma clang diagnostic ignored "-Wconversion"
1508#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001509#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001510#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001511#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001512#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001513#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1514#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001515#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001516#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001517#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1518#pragma clang diagnostic ignored "-Wswitch-enum"
1519#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001520#pragma clang diagnostic ignored "-Wweak-vtables"
1521#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001522#if __has_warning("-Wdouble-promotion")
1523#pragma clang diagnostic ignored "-Wdouble-promotion"
1524#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001525#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001526#pragma clang diagnostic ignored "-Wcomma"
1527#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001528#if __has_warning("-Wzero-as-null-pointer-constant")
1529#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1530#endif
1531#if __has_warning("-Wcast-qual")
1532#pragma clang diagnostic ignored "-Wcast-qual"
1533#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001534#if __has_warning("-Wmissing-variable-declarations")
1535#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1536#endif
1537#if __has_warning("-Wmissing-prototypes")
1538#pragma clang diagnostic ignored "-Wmissing-prototypes"
1539#endif
1540#if __has_warning("-Wcast-align")
1541#pragma clang diagnostic ignored "-Wcast-align"
1542#endif
1543#if __has_warning("-Wnewline-eof")
1544#pragma clang diagnostic ignored "-Wnewline-eof"
1545#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001546#if __has_warning("-Wunused-parameter")
1547#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001548#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001549#if __has_warning("-Wmismatched-tags")
1550#pragma clang diagnostic ignored "-Wmismatched-tags"
1551#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001552#if __has_warning("-Wextra-semi-stmt")
1553#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1554#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001555#endif
1556
imallettd9ce9eb2022-10-07 10:37:09 -07001557// Disable GCC warnings
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001558#ifdef __GNUC__
1559#pragma GCC diagnostic push
1560#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001561#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001562
krokofc0116b2019-03-03 08:28:49 +02001563#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001564#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001565#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001566#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001567#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001568#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001569#include "prettywriter.h"
1570#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001571#include "stringbuffer.h"
1572#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001573#endif
krokof4b6d112019-03-03 01:11:31 +02001574#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001575#endif
Alex Wood7319db72019-01-24 15:38:16 -05001576
1577#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001578#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001579#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001580#endif
Squareys2d3594d2018-03-13 22:40:53 +01001581
1582#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001583#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001584#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001585#endif
krokof4b6d112019-03-03 01:11:31 +02001586#endif
Squareys2d3594d2018-03-13 22:40:53 +01001587
johan bowald642a3432018-04-01 12:37:18 +02001588#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001589#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001590#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001591#endif
krokof4b6d112019-03-03 01:11:31 +02001592#endif
johan bowald642a3432018-04-01 12:37:18 +02001593
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001594#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001595#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001596#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001597
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001598#ifdef __GNUC__
1599#pragma GCC diagnostic pop
1600#endif
1601
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001602#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001603
1604// issue 143.
1605// Define NOMINMAX to avoid min/max defines,
imallett3a295882022-10-07 11:20:39 -07001606// but undef it after included Windows.h
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001607#ifndef NOMINMAX
1608#define TINYGLTF_INTERNAL_NOMINMAX
1609#define NOMINMAX
1610#endif
1611
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001612#ifndef WIN32_LEAN_AND_MEAN
1613#define WIN32_LEAN_AND_MEAN
1614#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1615#endif
imallett3a295882022-10-07 11:20:39 -07001616#ifndef __MINGW32__
imallett56e10982022-10-07 10:35:16 -07001617#include <Windows.h> // include API for expanding a file path
imallett3a295882022-10-07 11:20:39 -07001618#else
1619#include <windows.h>
1620#endif
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001621
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001622#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1623#undef WIN32_LEAN_AND_MEAN
1624#endif
1625
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001626#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1627#undef NOMINMAX
1628#endif
1629
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001630#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001631
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001632#include <fcntl.h> // _O_RDONLY
1633
1634#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001635
1636#endif
1637
Julian Smith0598a202021-08-25 12:06:08 +01001638#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001639//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001640#endif
1641
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001642#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001643// Big endian
1644#else
1645#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1646#define TINYGLTF_LITTLE_ENDIAN 1
1647#endif
1648#endif
1649
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001650namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001651#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001652
1653#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001654// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1655// documents may be active at once.
1656using json =
1657 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1658using json_const_iterator = json::ConstMemberIterator;
1659using json_const_array_iterator = json const *;
1660using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001661 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001662rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1663rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001664#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001665// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1666// not thread safe. Only a single JsonDocument may be active at any one time,
1667// meaning only a single gltf load/save can be active any one time.
1668using json = rapidjson::Value;
1669using json_const_iterator = json::ConstMemberIterator;
1670using json_const_array_iterator = json const *;
1671rapidjson::Document *s_pActiveDocument = nullptr;
1672rapidjson::Document::AllocatorType &GetAllocator() {
1673 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1674 return s_pActiveDocument->GetAllocator();
1675}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001676
1677#ifdef __clang__
1678#pragma clang diagnostic push
1679// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1680#pragma clang diagnostic ignored "-Wunused-member-function"
1681#endif
1682
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001683struct JsonDocument : public rapidjson::Document {
1684 JsonDocument() {
1685 assert(s_pActiveDocument ==
1686 nullptr); // When using default allocator, only one document can be
1687 // active at a time, if you need multiple active at once,
1688 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1689 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001690 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001691 JsonDocument(const JsonDocument &) = delete;
1692 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001693 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001694 s_pActiveDocument = this;
1695 rhs.isNil = true;
1696 }
1697 ~JsonDocument() {
1698 if (!isNil) {
1699 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001700 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001701 }
jrkooncece7fa742019-09-04 13:31:44 -05001702
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001703 private:
1704 bool isNil = false;
1705};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001706
1707#ifdef __clang__
1708#pragma clang diagnostic pop
1709#endif
1710
jrkooncece7fa742019-09-04 13:31:44 -05001711#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001712
jrkooncecba5d6c2019-08-29 11:26:22 -05001713#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001714using nlohmann::json;
1715using json_const_iterator = json::const_iterator;
1716using json_const_array_iterator = json_const_iterator;
1717using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001718#endif
1719
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001720void JsonParse(JsonDocument &doc, const char *str, size_t length,
1721 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001722#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001723 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001724 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001725#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001726 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001727#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001728}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001729} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001730
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001731#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001732#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001733#endif
1734
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001735#ifdef __clang__
1736#pragma clang diagnostic push
1737#pragma clang diagnostic ignored "-Wc++98-compat"
1738#endif
1739
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001740namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001741
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001742///
1743/// Internal LoadImageDataOption struct.
1744/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001745/// The struct is not passed when the user supply their own LoadImageData
1746/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001747///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001748struct LoadImageDataOption {
1749 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1750 // channels) default `false`(channels are expanded to RGBA for backward
imallettd9ce9eb2022-10-07 10:37:09 -07001751 // compatibility).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001752 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001753};
1754
Selmar Kok31cb7f92018-10-03 15:39:05 +02001755// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001756static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1757 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001758
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001759 switch (one.Type()) {
1760 case NULL_TYPE:
1761 return true;
1762 case BOOL_TYPE:
1763 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001764 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001765 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1766 case INT_TYPE:
1767 return one.Get<int>() == other.Get<int>();
1768 case OBJECT_TYPE: {
1769 auto oneObj = one.Get<tinygltf::Value::Object>();
1770 auto otherObj = other.Get<tinygltf::Value::Object>();
1771 if (oneObj.size() != otherObj.size()) return false;
1772 for (auto &it : oneObj) {
1773 auto otherIt = otherObj.find(it.first);
1774 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001775
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001776 if (!Equals(it.second, otherIt->second)) return false;
1777 }
1778 return true;
1779 }
1780 case ARRAY_TYPE: {
1781 if (one.Size() != other.Size()) return false;
1782 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001783 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001784 return true;
1785 }
1786 case STRING_TYPE:
1787 return one.Get<std::string>() == other.Get<std::string>();
1788 case BINARY_TYPE:
1789 return one.Get<std::vector<unsigned char> >() ==
1790 other.Get<std::vector<unsigned char> >();
1791 default: {
1792 // unhandled type
1793 return false;
1794 }
1795 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001796}
1797
1798// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001799static bool Equals(const std::vector<double> &one,
1800 const std::vector<double> &other) {
1801 if (one.size() != other.size()) return false;
1802 for (int i = 0; i < int(one.size()); ++i) {
1803 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1804 }
1805 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001806}
1807
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001808bool Accessor::operator==(const Accessor &other) const {
1809 return this->bufferView == other.bufferView &&
1810 this->byteOffset == other.byteOffset &&
1811 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001812 this->count == other.count && this->extensions == other.extensions &&
1813 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001814 Equals(this->maxValues, other.maxValues) &&
1815 Equals(this->minValues, other.minValues) && this->name == other.name &&
1816 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001817}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001818bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001819 return this->channels == other.channels &&
1820 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001821 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001822}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001823bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001824 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001825 this->target_node == other.target_node &&
1826 this->target_path == other.target_path &&
1827 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001828}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001829bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001830 return this->extras == other.extras && this->extensions == other.extensions &&
1831 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001832 this->interpolation == other.interpolation &&
1833 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001834}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001835bool Asset::operator==(const Asset &other) const {
1836 return this->copyright == other.copyright &&
1837 this->extensions == other.extensions && this->extras == other.extras &&
1838 this->generator == other.generator &&
1839 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001840}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001841bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001842 return this->data == other.data && this->extensions == other.extensions &&
1843 this->extras == other.extras && this->name == other.name &&
1844 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001845}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001846bool BufferView::operator==(const BufferView &other) const {
1847 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1848 this->byteOffset == other.byteOffset &&
1849 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001850 this->target == other.target && this->extensions == other.extensions &&
1851 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001852 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001853}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001854bool Camera::operator==(const Camera &other) const {
1855 return this->name == other.name && this->extensions == other.extensions &&
1856 this->extras == other.extras &&
1857 this->orthographic == other.orthographic &&
1858 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001859}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001860bool Image::operator==(const Image &other) const {
1861 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001862 this->component == other.component &&
1863 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001864 this->height == other.height && this->image == other.image &&
1865 this->mimeType == other.mimeType && this->name == other.name &&
1866 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001867}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001868bool Light::operator==(const Light &other) const {
1869 return Equals(this->color, other.color) && this->name == other.name &&
1870 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001871}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001872bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001873 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1874 (this->normalTexture == other.normalTexture) &&
1875 (this->occlusionTexture == other.occlusionTexture) &&
1876 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001877 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001878 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001879 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001880 (this->doubleSided == other.doubleSided) &&
1881 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001882 (this->extras == other.extras) && (this->values == other.values) &&
1883 (this->additionalValues == other.additionalValues) &&
1884 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001885}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001886bool Mesh::operator==(const Mesh &other) const {
1887 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001888 this->name == other.name && Equals(this->weights, other.weights) &&
1889 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001890}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001891bool Model::operator==(const Model &other) const {
1892 return this->accessors == other.accessors &&
1893 this->animations == other.animations && this->asset == other.asset &&
1894 this->buffers == other.buffers &&
1895 this->bufferViews == other.bufferViews &&
1896 this->cameras == other.cameras &&
1897 this->defaultScene == other.defaultScene &&
1898 this->extensions == other.extensions &&
1899 this->extensionsRequired == other.extensionsRequired &&
1900 this->extensionsUsed == other.extensionsUsed &&
1901 this->extras == other.extras && this->images == other.images &&
1902 this->lights == other.lights && this->materials == other.materials &&
1903 this->meshes == other.meshes && this->nodes == other.nodes &&
1904 this->samplers == other.samplers && this->scenes == other.scenes &&
1905 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001906}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001907bool Node::operator==(const Node &other) const {
1908 return this->camera == other.camera && this->children == other.children &&
1909 this->extensions == other.extensions && this->extras == other.extras &&
1910 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1911 this->name == other.name && Equals(this->rotation, other.rotation) &&
1912 Equals(this->scale, other.scale) && this->skin == other.skin &&
1913 Equals(this->translation, other.translation) &&
1914 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001915}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001916bool SpotLight::operator==(const SpotLight &other) const {
1917 return this->extensions == other.extensions && this->extras == other.extras &&
1918 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1919 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1920}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001921bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1922 return this->extensions == other.extensions && this->extras == other.extras &&
1923 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1924 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1925 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1926 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001927}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001928bool Parameter::operator==(const Parameter &other) const {
1929 if (this->bool_value != other.bool_value ||
1930 this->has_number_value != other.has_number_value)
1931 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001932
Selmar Kok2bda71c2018-10-05 14:36:05 +02001933 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1934 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001935
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001936 if (this->json_double_value.size() != other.json_double_value.size())
1937 return false;
1938 for (auto &it : this->json_double_value) {
1939 auto otherIt = other.json_double_value.find(it.first);
1940 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001941
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001942 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1943 }
1944
1945 if (!Equals(this->number_array, other.number_array)) return false;
1946
1947 if (this->string_value != other.string_value) return false;
1948
1949 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001950}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001951bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1952 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1953 this->extensions == other.extensions && this->extras == other.extras &&
1954 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1955 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1956 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001957}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001958bool Primitive::operator==(const Primitive &other) const {
1959 return this->attributes == other.attributes && this->extras == other.extras &&
1960 this->indices == other.indices && this->material == other.material &&
1961 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001962}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001963bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001964 return this->extensions == other.extensions && this->extras == other.extras &&
1965 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001966 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001967 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001968
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001969 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02001970}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001971bool Scene::operator==(const Scene &other) const {
1972 return this->extensions == other.extensions && this->extras == other.extras &&
1973 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001974}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001975bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001976 return this->extensions == other.extensions && this->extras == other.extras &&
1977 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001978 this->joints == other.joints && this->name == other.name &&
1979 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001980}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001981bool Texture::operator==(const Texture &other) const {
1982 return this->extensions == other.extensions && this->extras == other.extras &&
1983 this->name == other.name && this->sampler == other.sampler &&
1984 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001985}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001986bool TextureInfo::operator==(const TextureInfo &other) const {
1987 return this->extensions == other.extensions && this->extras == other.extras &&
1988 this->index == other.index && this->texCoord == other.texCoord;
1989}
1990bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1991 return this->extensions == other.extensions && this->extras == other.extras &&
1992 this->index == other.index && this->texCoord == other.texCoord &&
1993 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
1994}
1995bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
1996 return this->extensions == other.extensions && this->extras == other.extras &&
1997 this->index == other.index && this->texCoord == other.texCoord &&
1998 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
1999}
2000bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2001 return this->extensions == other.extensions && this->extras == other.extras &&
2002 (this->baseColorTexture == other.baseColorTexture) &&
2003 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002004 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002005 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2006 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2007}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002008bool Value::operator==(const Value &other) const {
2009 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002010}
2011
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002012static void swap4(unsigned int *val) {
2013#ifdef TINYGLTF_LITTLE_ENDIAN
2014 (void)val;
2015#else
2016 unsigned int tmp = *val;
2017 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2018 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2019
2020 dst[0] = src[3];
2021 dst[1] = src[2];
2022 dst[2] = src[1];
2023 dst[3] = src[0];
2024#endif
2025}
2026
Syoyo Fujitabeded612016-05-01 20:03:43 +09002027static std::string JoinPath(const std::string &path0,
2028 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002029 if (path0.empty()) {
2030 return path1;
2031 } else {
2032 // check '/'
2033 char lastChar = *path0.rbegin();
2034 if (lastChar != '/') {
2035 return path0 + std::string("/") + path1;
2036 } else {
2037 return path0 + path1;
2038 }
2039 }
2040}
2041
Syoyo Fujita643ce102016-05-01 17:19:37 +09002042static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002043 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002044 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2045 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002046 // Error, fs callback[s] missing
2047 return std::string();
2048 }
2049
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002050 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002051 std::string absPath =
2052 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002053 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002054 return absPath;
2055 }
2056 }
2057
2058 return std::string();
2059}
2060
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002061static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002062 if (FileName.find_last_of(".") != std::string::npos)
2063 return FileName.substr(FileName.find_last_of(".") + 1);
2064 return "";
2065}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002066
Syoyo Fujita643ce102016-05-01 17:19:37 +09002067static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002068 if (filepath.find_last_of("/\\") != std::string::npos)
2069 return filepath.substr(0, filepath.find_last_of("/\\"));
2070 return "";
2071}
2072
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002073static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002074 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002075 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002076 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002077}
2078
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002079std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002080std::string base64_decode(std::string const &s);
2081
2082/*
2083 base64.cpp and base64.h
2084
2085 Copyright (C) 2004-2008 René Nyffenegger
2086
2087 This source code is provided 'as-is', without any express or implied
2088 warranty. In no event will the author be held liable for any damages
2089 arising from the use of this software.
2090
2091 Permission is granted to anyone to use this software for any purpose,
2092 including commercial applications, and to alter it and redistribute it
2093 freely, subject to the following restrictions:
2094
2095 1. The origin of this source code must not be misrepresented; you must not
2096 claim that you wrote the original source code. If you use this source code
2097 in a product, an acknowledgment in the product documentation would be
2098 appreciated but is not required.
2099
2100 2. Altered source versions must be plainly marked as such, and must not be
2101 misrepresented as being the original source code.
2102
2103 3. This notice may not be removed or altered from any source distribution.
2104
2105 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2106
2107*/
2108
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002109#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002110#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002111#pragma clang diagnostic ignored "-Wsign-conversion"
2112#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002113#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002114
2115static inline bool is_base64(unsigned char c) {
2116 return (isalnum(c) || (c == '+') || (c == '/'));
2117}
2118
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002119std::string base64_encode(unsigned char const *bytes_to_encode,
2120 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002121 std::string ret;
2122 int i = 0;
2123 int j = 0;
2124 unsigned char char_array_3[3];
2125 unsigned char char_array_4[4];
2126
Syoyo Fujitaff515702019-08-24 16:29:14 +09002127 const char *base64_chars =
2128 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2129 "abcdefghijklmnopqrstuvwxyz"
2130 "0123456789+/";
2131
johan bowald30c53472018-03-30 11:49:36 +02002132 while (in_len--) {
2133 char_array_3[i++] = *(bytes_to_encode++);
2134 if (i == 3) {
2135 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002136 char_array_4[1] =
2137 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2138 char_array_4[2] =
2139 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002140 char_array_4[3] = char_array_3[2] & 0x3f;
2141
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002142 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002143 i = 0;
2144 }
2145 }
2146
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002147 if (i) {
2148 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002149
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002150 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2151 char_array_4[1] =
2152 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2153 char_array_4[2] =
2154 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002155
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002156 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002157
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002158 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002159 }
2160
2161 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002162}
2163
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002164std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002165 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002166 int i = 0;
2167 int j = 0;
2168 int in_ = 0;
2169 unsigned char char_array_4[4], char_array_3[3];
2170 std::string ret;
2171
Syoyo Fujitaff515702019-08-24 16:29:14 +09002172 const std::string base64_chars =
2173 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2174 "abcdefghijklmnopqrstuvwxyz"
2175 "0123456789+/";
2176
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002177 while (in_len-- && (encoded_string[in_] != '=') &&
2178 is_base64(encoded_string[in_])) {
2179 char_array_4[i++] = encoded_string[in_];
2180 in_++;
2181 if (i == 4) {
2182 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002183 char_array_4[i] =
2184 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002185
2186 char_array_3[0] =
2187 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2188 char_array_3[1] =
2189 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2190 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2191
Syoyo Fujita7c877972016-03-08 01:31:49 +09002192 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002193 i = 0;
2194 }
2195 }
2196
2197 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002198 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002199
2200 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002201 char_array_4[j] =
2202 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002203
2204 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2205 char_array_3[1] =
2206 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2207 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2208
Syoyo Fujita7c877972016-03-08 01:31:49 +09002209 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002210 }
2211
2212 return ret;
2213}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002214#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002215#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002216#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002217
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002218// https://github.com/syoyo/tinygltf/issues/228
2219// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2220// decoding?
2221//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002222// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002223// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002224// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002225// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002226// License: Boost Software License
2227// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002228
Alexander Woode4bc6c72021-10-14 08:54:59 -04002229// Permission is hereby granted, free of charge, to any person or organization
2230// obtaining a copy of the software and accompanying documentation covered by
2231// this license (the "Software") to use, reproduce, display, distribute,
2232// execute, and transmit the Software, and to prepare derivative works of the
2233// Software, and to permit third-parties to whom the Software is furnished to
2234// do so, all subject to the following:
2235// The copyright notices in the Software and this entire statement, including
2236// the above license grant, this restriction and the following disclaimer,
2237// must be included in all copies of the Software, in whole or in part, and
2238// all derivative works of the Software, unless such copies or derivative
2239// works are solely in the form of machine-executable object code generated by
2240// a source language processor.
2241// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2242// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2243// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2244// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2245// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2246// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2247// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002248//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002249namespace dlib {
2250
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002251inline unsigned char from_hex(unsigned char ch) {
2252 if (ch <= '9' && ch >= '0')
2253 ch -= '0';
2254 else if (ch <= 'f' && ch >= 'a')
2255 ch -= 'a' - 10;
2256 else if (ch <= 'F' && ch >= 'A')
2257 ch -= 'A' - 10;
2258 else
2259 ch = 0;
2260 return ch;
2261}
2262
2263static const std::string urldecode(const std::string &str) {
2264 using namespace std;
2265 string result;
2266 string::size_type i;
2267 for (i = 0; i < str.size(); ++i) {
2268 if (str[i] == '+') {
2269 result += ' ';
2270 } else if (str[i] == '%' && str.size() > i + 2) {
2271 const unsigned char ch1 =
2272 from_hex(static_cast<unsigned char>(str[i + 1]));
2273 const unsigned char ch2 =
2274 from_hex(static_cast<unsigned char>(str[i + 2]));
2275 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2276 result += static_cast<char>(ch);
2277 i += 2;
2278 } else {
2279 result += str[i];
2280 }
2281 }
2282 return result;
2283}
2284
2285} // namespace dlib
2286// --- dlib end --------------------------------------------------------------
2287
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002288static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002289 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002290 const std::string &basedir, bool required,
2291 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002292 if (fs == nullptr || fs->FileExists == nullptr ||
2293 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002294 // This is a developer error, assert() ?
2295 if (err) {
2296 (*err) += "FS callback[s] not set\n";
2297 }
2298 return false;
2299 }
2300
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002301 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002302
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002303 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002304
2305 std::vector<std::string> paths;
2306 paths.push_back(basedir);
2307 paths.push_back(".");
2308
Paolo Jovone6601bf2018-07-07 20:43:33 +02002309 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002310 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002311 if (failMsgOut) {
2312 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002313 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002314 return false;
2315 }
2316
Paolo Jovone6601bf2018-07-07 20:43:33 +02002317 std::vector<unsigned char> buf;
2318 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002319 bool fileRead =
2320 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002321 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002322 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002323 (*failMsgOut) +=
2324 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002325 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002326 return false;
2327 }
2328
Paolo Jovone6601bf2018-07-07 20:43:33 +02002329 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002330 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002331 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002332 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002333 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002334 return false;
2335 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002336
2337 if (checkSize) {
2338 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002339 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002340 return true;
2341 } else {
2342 std::stringstream ss;
2343 ss << "File size mismatch : " << filepath << ", requestedBytes "
2344 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002345 if (failMsgOut) {
2346 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002347 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002348 return false;
2349 }
2350 }
2351
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002352 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002353 return true;
2354}
2355
Squareysff644d82018-03-13 22:36:18 +01002356void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002357 LoadImageData = func;
2358 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002359 user_image_loader_ = true;
2360}
2361
2362void TinyGLTF::RemoveImageLoader() {
2363 LoadImageData =
2364#ifndef TINYGLTF_NO_STB_IMAGE
2365 &tinygltf::LoadImageData;
2366#else
2367 nullptr;
2368#endif
2369
2370 load_image_user_data_ = nullptr;
2371 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002372}
2373
Squareys2d3594d2018-03-13 22:40:53 +01002374#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002375bool LoadImageData(Image *image, const int image_idx, std::string *err,
2376 std::string *warn, int req_width, int req_height,
2377 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002378 (void)warn;
2379
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002380 LoadImageDataOption option;
2381 if (user_data) {
2382 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2383 }
2384
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002385 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002386
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002387 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002388
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002389 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002390 // false: force 32-bit textures for common Vulkan compatibility. It appears
2391 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002392 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002393 int bits = 8;
2394 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002395
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002396 // It is possible that the image we want to load is a 16bit per channel image
2397 // We are going to attempt to load it as 16bit per channel, and if it worked,
imallettd9ce9eb2022-10-07 10:37:09 -07002398 // set the image data accordingly. We are casting the returned pointer into
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002399 // unsigned char, because we are representing "bytes". But we are updating
2400 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2401 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002402 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002403 data = reinterpret_cast<unsigned char *>(
2404 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002405 if (data) {
2406 bits = 16;
2407 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2408 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002409 }
2410
2411 // at this point, if data is still NULL, it means that the image wasn't
2412 // 16bit per channel, we are going to load it as a normal 8bit per channel
imallettd9ce9eb2022-10-07 10:37:09 -07002413 // image as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002414 // if image cannot be decoded, ignore parsing and keep it by its path
2415 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002416 // FIXME we should only enter this function if the image is embedded. If
2417 // image->uri references
2418 // an image file, it should be left as it is. Image loading should not be
2419 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002420 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002421 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002422 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002423 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002424 (*err) +=
2425 "Unknown image format. STB cannot decode image data for image[" +
2426 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002427 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002428 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002429 }
2430
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002431 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002432 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002433 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002434 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2435 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002436 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002437 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002438 }
2439
2440 if (req_width > 0) {
2441 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002442 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002443 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002444 (*err) += "Image width mismatch for image[" +
2445 std::to_string(image_idx) + "] name = \"" + image->name +
2446 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002447 }
2448 return false;
2449 }
2450 }
2451
2452 if (req_height > 0) {
2453 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002454 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002455 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002456 (*err) += "Image height mismatch. for image[" +
2457 std::to_string(image_idx) + "] name = \"" + image->name +
2458 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002459 }
2460 return false;
2461 }
2462 }
2463
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002464 if (req_comp != 0) {
2465 // loaded data has `req_comp` channels(components)
2466 comp = req_comp;
2467 }
2468
Syoyo Fujitabeded612016-05-01 20:03:43 +09002469 image->width = w;
2470 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002471 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002472 image->bits = bits;
2473 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002474 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2475 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002476 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002477
Syoyo Fujitabeded612016-05-01 20:03:43 +09002478 return true;
2479}
Squareys2d3594d2018-03-13 22:40:53 +01002480#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002481
johan bowald642a3432018-04-01 12:37:18 +02002482void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2483 WriteImageData = func;
2484 write_image_user_data_ = user_data;
2485}
2486
2487#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2488static void WriteToMemory_stbi(void *context, void *data, int size) {
2489 std::vector<unsigned char> *buffer =
2490 reinterpret_cast<std::vector<unsigned char> *>(context);
2491
2492 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2493
2494 buffer->insert(buffer->end(), pData, pData + size);
2495}
2496
2497bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002498 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002499 const std::string ext = GetFilePathExtension(*filename);
2500
Paolo Jovone6601bf2018-07-07 20:43:33 +02002501 // Write image to temporary buffer
2502 std::string header;
2503 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002504
Paolo Jovone6601bf2018-07-07 20:43:33 +02002505 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002506 if ((image->bits != 8) ||
2507 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002508 // Unsupported pixel format
2509 return false;
2510 }
2511
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002512 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002513 image->height, image->component,
2514 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002515 return false;
2516 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002517 header = "data:image/png;base64,";
2518 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002519 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002520 image->height, image->component,
2521 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002522 return false;
2523 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002524 header = "data:image/jpeg;base64,";
2525 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002526 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002527 image->height, image->component,
2528 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002529 return false;
2530 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002531 header = "data:image/bmp;base64,";
2532 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002533 // Error: can't output requested format to file
2534 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002535 }
johan bowald642a3432018-04-01 12:37:18 +02002536
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002537 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002538 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002539 if (data.size()) {
2540 image->uri =
2541 header +
2542 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
2543 } else {
2544 // Throw error?
2545 }
2546 } else {
2547 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002548 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002549 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002550 const std::string imagefilepath = JoinPath(*basepath, *filename);
2551 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002552 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2553 fs->user_data)) {
2554 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002555 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002556 }
johan bowald642a3432018-04-01 12:37:18 +02002557 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002558 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002559 }
2560 image->uri = *filename;
2561 }
2562
2563 return true;
2564}
2565#endif
2566
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002567void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002568
Harokyangfb256602019-10-30 16:13:52 +08002569#ifdef _WIN32
2570static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002571 int wstr_size =
2572 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
imallett56e10982022-10-07 10:35:16 -07002573 std::wstring wstr((size_t)wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002574 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2575 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002576 return wstr;
2577}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002578
2579static inline std::string WcharToUTF8(const std::wstring &wstr) {
2580 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
imallett56e10982022-10-07 10:35:16 -07002581 nullptr, 0, nullptr, nullptr);
2582 std::string str((size_t)str_size, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002583 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
imallett56e10982022-10-07 10:35:16 -07002584 (int)str.size(), nullptr, nullptr);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002585 return str;
2586}
Harokyangfb256602019-10-30 16:13:52 +08002587#endif
2588
Paolo Jovone6601bf2018-07-07 20:43:33 +02002589#ifndef TINYGLTF_NO_FS
2590// Default implementations of filesystem functions
2591
2592bool FileExists(const std::string &abs_filename, void *) {
2593 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002594#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002595 if (asset_manager) {
2596 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2597 AASSET_MODE_STREAMING);
2598 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002599 return false;
2600 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002601 AAsset_close(asset);
2602 ret = true;
2603 } else {
2604 return false;
2605 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002606#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002607#ifdef _WIN32
operatios1668d1e2022-09-24 22:37:14 +03002608#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
Syoyo Fujita45cac782019-11-09 20:42:55 +09002609 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002610 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002611 if (err != 0) {
2612 return false;
2613 }
2614#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002615 FILE *fp = nullptr;
2616 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2617 if (err != 0) {
2618 return false;
2619 }
2620#endif
2621
2622#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002623 FILE *fp = fopen(abs_filename.c_str(), "rb");
2624#endif
2625 if (fp) {
2626 ret = true;
2627 fclose(fp);
2628 } else {
2629 ret = false;
2630 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002631#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002632
2633 return ret;
2634}
2635
2636std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002637 // https://github.com/syoyo/tinygltf/issues/368
2638 //
2639 // No file path expansion in built-in FS function anymore, since glTF URI
2640 // should not contain tilde('~') and environment variables, and for security
2641 // reason(`wordexp`).
2642 //
2643 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2644 // `LoadBinaryFromMemory`) in expanded absolute path.
2645
2646 return filepath;
2647
2648#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002649#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002650 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002651 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002652 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002653 wchar_t *wstr = new wchar_t[wlen];
2654 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002655
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002656 std::wstring ws(wstr);
2657 delete[] wstr;
2658 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002659
Paolo Jovone6601bf2018-07-07 20:43:33 +02002660#else
2661
2662#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002663 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002664 // no expansion
2665 std::string s = filepath;
2666#else
2667 std::string s;
2668 wordexp_t p;
2669
2670 if (filepath.empty()) {
2671 return "";
2672 }
2673
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002674 // Quote the string to keep any spaces in filepath intact.
2675 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002676 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002677 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002678 if (ret) {
2679 // err
2680 s = filepath;
2681 return s;
2682 }
2683
2684 // Use first element only.
2685 if (p.we_wordv) {
2686 s = std::string(p.we_wordv[0]);
2687 wordfree(&p);
2688 } else {
2689 s = filepath;
2690 }
2691
2692#endif
2693
2694 return s;
2695#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002696#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002697}
2698
2699bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2700 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002701#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2702 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002703 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2704 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002705 if (!asset) {
2706 if (err) {
2707 (*err) += "File open error : " + filepath + "\n";
2708 }
2709 return false;
2710 }
2711 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002712 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002713 if (err) {
2714 (*err) += "Invalid file size : " + filepath +
2715 " (does the path point to a directory?)";
2716 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002717 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002718 }
2719 out->resize(size);
2720 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2721 AAsset_close(asset);
2722 return true;
2723 } else {
2724 if (err) {
2725 (*err) += "No asset manager specified : " + filepath + "\n";
2726 }
2727 return false;
2728 }
2729#else
Harokyang5cecef22019-10-30 15:16:46 +08002730#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002731#if defined(__GLIBCXX__) // mingw
2732 int file_descriptor =
2733 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002734 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2735 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002736#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002737 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2738 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002739 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002740#else
2741 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002742 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2743#endif
Harokyang5cecef22019-10-30 15:16:46 +08002744#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002745 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002746#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002747 if (!f) {
2748 if (err) {
2749 (*err) += "File open error : " + filepath + "\n";
2750 }
2751 return false;
2752 }
2753
2754 f.seekg(0, f.end);
2755 size_t sz = static_cast<size_t>(f.tellg());
2756 f.seekg(0, f.beg);
2757
Syoyo Fujitae8862472019-10-20 17:47:50 +09002758 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002759 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002760 (*err) += "Invalid file size : " + filepath +
2761 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002762 }
2763 return false;
2764 } else if (sz == 0) {
2765 if (err) {
2766 (*err) += "File is empty : " + filepath + "\n";
2767 }
2768 return false;
2769 }
2770
2771 out->resize(sz);
2772 f.read(reinterpret_cast<char *>(&out->at(0)),
2773 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002774
2775 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002776#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002777}
2778
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002779bool WriteWholeFile(std::string *err, const std::string &filepath,
2780 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002781#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002782#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002783 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2784 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2785 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2786 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002787 std::ostream f(&wfile_buf);
2788#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002789 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002790#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002791 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2792#endif
Harokyangfb256602019-10-30 16:13:52 +08002793#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002794 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002795#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002796 if (!f) {
2797 if (err) {
2798 (*err) += "File open error for writing : " + filepath + "\n";
2799 }
2800 return false;
2801 }
2802
2803 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2804 static_cast<std::streamsize>(contents.size()));
2805 if (!f) {
2806 if (err) {
2807 (*err) += "File write error: " + filepath + "\n";
2808 }
2809 return false;
2810 }
2811
Paolo Jovone6601bf2018-07-07 20:43:33 +02002812 return true;
2813}
2814
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002815#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002816
johan bowald642a3432018-04-01 12:37:18 +02002817static std::string MimeToExt(const std::string &mimeType) {
2818 if (mimeType == "image/jpeg") {
2819 return "jpg";
2820 } else if (mimeType == "image/png") {
2821 return "png";
2822 } else if (mimeType == "image/bmp") {
2823 return "bmp";
2824 } else if (mimeType == "image/gif") {
2825 return "gif";
2826 }
2827
2828 return "";
2829}
2830
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002831static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002832 bool embedImages,
2833 WriteImageDataFunction *WriteImageData = nullptr,
2834 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002835 std::string filename;
2836 std::string ext;
imallettd9ce9eb2022-10-07 10:37:09 -07002837 // If image has uri, use it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002838 if (image.uri.size()) {
2839 filename = GetBaseFilename(image.uri);
2840 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002841 } else if (image.bufferView != -1) {
2842 // If there's no URI and the data exists in a buffer,
2843 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002844 } else if (image.name.size()) {
2845 ext = MimeToExt(image.mimeType);
2846 // Otherwise use name as filename
2847 filename = image.name + "." + ext;
2848 } else {
2849 ext = MimeToExt(image.mimeType);
2850 // Fallback to index of image as filename
2851 filename = std::to_string(index) + "." + ext;
2852 }
2853
2854 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002855 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002856 std::string uri;
2857 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2858 }
2859}
2860
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002861bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002862 std::string header = "data:application/octet-stream;base64,";
2863 if (in.find(header) == 0) {
2864 return true;
2865 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002866
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002867 header = "data:image/jpeg;base64,";
2868 if (in.find(header) == 0) {
2869 return true;
2870 }
Squareys43374632018-03-13 22:20:48 +01002871
Syoyo Fujita620eed12016-01-02 23:37:12 +09002872 header = "data:image/png;base64,";
2873 if (in.find(header) == 0) {
2874 return true;
2875 }
Squareys43374632018-03-13 22:20:48 +01002876
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002877 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002878 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002879 return true;
2880 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002881
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002882 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002883 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002884 return true;
2885 }
2886
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002887 header = "data:text/plain;base64,";
2888 if (in.find(header) == 0) {
2889 return true;
2890 }
2891
Syoyo Fujita20244e12018-03-15 11:01:05 -05002892 header = "data:application/gltf-buffer;base64,";
2893 if (in.find(header) == 0) {
2894 return true;
2895 }
2896
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002897 return false;
2898}
2899
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002900bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2901 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002902 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002903 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002904 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002905 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002906 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002907
2908 if (data.empty()) {
2909 header = "data:image/jpeg;base64,";
2910 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002911 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002912 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002913 }
2914 }
2915
2916 if (data.empty()) {
2917 header = "data:image/png;base64,";
2918 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002919 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002920 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002921 }
2922 }
Squareys43374632018-03-13 22:20:48 +01002923
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002924 if (data.empty()) {
2925 header = "data:image/bmp;base64,";
2926 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002927 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002928 data = base64_decode(in.substr(header.size())); // cut mime string.
2929 }
2930 }
2931
2932 if (data.empty()) {
2933 header = "data:image/gif;base64,";
2934 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002935 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002936 data = base64_decode(in.substr(header.size())); // cut mime string.
2937 }
2938 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002939
2940 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002941 header = "data:text/plain;base64,";
2942 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002943 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002944 data = base64_decode(in.substr(header.size()));
2945 }
2946 }
2947
2948 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002949 header = "data:application/gltf-buffer;base64,";
2950 if (in.find(header) == 0) {
2951 data = base64_decode(in.substr(header.size()));
2952 }
2953 }
2954
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002955 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002956 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002957 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002958 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002959
2960 if (checkSize) {
2961 if (data.size() != reqBytes) {
2962 return false;
2963 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002964 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002965 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002966 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002967 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002968 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002969 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002970}
2971
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002972namespace {
2973bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002974#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002975 if (!o.IsDouble()) {
2976 if (o.IsInt()) {
2977 val = o.GetInt();
2978 return true;
2979 } else if (o.IsUint()) {
2980 val = static_cast<int>(o.GetUint());
2981 return true;
2982 } else if (o.IsInt64()) {
2983 val = static_cast<int>(o.GetInt64());
2984 return true;
2985 } else if (o.IsUint64()) {
2986 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002987 return true;
2988 }
jrkoonce5cecc412019-08-29 11:45:04 -05002989 }
2990
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002991 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002992#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002993 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002994
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002995 if ((type == json::value_t::number_integer) ||
2996 (type == json::value_t::number_unsigned)) {
2997 val = static_cast<int>(o.get<int64_t>());
2998 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002999 }
3000
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003001 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003002#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05003003}
3004
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003005#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003006bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003007 if (o.IsDouble()) {
3008 val = o.GetDouble();
3009 return true;
3010 }
3011
3012 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003013}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003014#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003015
3016bool GetNumber(const json &o, double &val) {
3017#ifdef TINYGLTF_USE_RAPIDJSON
3018 if (o.IsNumber()) {
3019 val = o.GetDouble();
3020 return true;
3021 }
3022
3023 return false;
3024#else
3025 if (o.is_number()) {
3026 val = o.get<double>();
3027 return true;
3028 }
3029
3030 return false;
3031#endif
3032}
3033
3034bool GetString(const json &o, std::string &val) {
3035#ifdef TINYGLTF_USE_RAPIDJSON
3036 if (o.IsString()) {
3037 val = o.GetString();
3038 return true;
3039 }
3040
3041 return false;
3042#else
3043 if (o.type() == json::value_t::string) {
3044 val = o.get<std::string>();
3045 return true;
3046 }
3047
3048 return false;
3049#endif
3050}
3051
3052bool IsArray(const json &o) {
3053#ifdef TINYGLTF_USE_RAPIDJSON
3054 return o.IsArray();
3055#else
3056 return o.is_array();
3057#endif
3058}
3059
3060json_const_array_iterator ArrayBegin(const json &o) {
3061#ifdef TINYGLTF_USE_RAPIDJSON
3062 return o.Begin();
3063#else
3064 return o.begin();
3065#endif
3066}
3067
3068json_const_array_iterator ArrayEnd(const json &o) {
3069#ifdef TINYGLTF_USE_RAPIDJSON
3070 return o.End();
3071#else
3072 return o.end();
3073#endif
3074}
3075
3076bool IsObject(const json &o) {
3077#ifdef TINYGLTF_USE_RAPIDJSON
3078 return o.IsObject();
3079#else
3080 return o.is_object();
3081#endif
3082}
3083
3084json_const_iterator ObjectBegin(const json &o) {
3085#ifdef TINYGLTF_USE_RAPIDJSON
3086 return o.MemberBegin();
3087#else
3088 return o.begin();
3089#endif
3090}
3091
3092json_const_iterator ObjectEnd(const json &o) {
3093#ifdef TINYGLTF_USE_RAPIDJSON
3094 return o.MemberEnd();
3095#else
3096 return o.end();
3097#endif
3098}
3099
Rahul Sheth01d54382020-07-10 14:27:37 -04003100// Making this a const char* results in a pointer to a temporary when
3101// TINYGLTF_USE_RAPIDJSON is off.
3102std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003103#ifdef TINYGLTF_USE_RAPIDJSON
3104 return it->name.GetString();
3105#else
3106 return it.key().c_str();
3107#endif
3108}
3109
3110bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3111#ifdef TINYGLTF_USE_RAPIDJSON
3112 if (!o.IsObject()) {
3113 return false;
3114 }
3115 it = o.FindMember(member);
3116 return it != o.MemberEnd();
3117#else
3118 it = o.find(member);
3119 return it != o.end();
3120#endif
3121}
3122
3123const json &GetValue(json_const_iterator &it) {
3124#ifdef TINYGLTF_USE_RAPIDJSON
3125 return it->value;
3126#else
3127 return it.value();
3128#endif
3129}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003130
3131std::string JsonToString(const json &o, int spacing = -1) {
3132#ifdef TINYGLTF_USE_RAPIDJSON
3133 using namespace rapidjson;
3134 StringBuffer buffer;
3135 if (spacing == -1) {
3136 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003137 // TODO: Better error handling.
3138 // https://github.com/syoyo/tinygltf/issues/332
3139 if (!o.Accept(writer)) {
3140 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3141 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003142 } else {
3143 PrettyWriter<StringBuffer> writer(buffer);
3144 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003145 if (!o.Accept(writer)) {
3146 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3147 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003148 }
3149 return buffer.GetString();
3150#else
3151 return o.dump(spacing);
3152#endif
3153}
3154
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003155} // namespace
3156
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003157static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003158 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003159#ifdef TINYGLTF_USE_RAPIDJSON
3160 using rapidjson::Type;
3161 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003162 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003163 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003164 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003165 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003166 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003167 if (entry.Type() != NULL_TYPE)
3168 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003169 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003170 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003171 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003172 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003173 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003174 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003175 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003176 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003177 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003178 if (entry.Type() != NULL_TYPE)
3179 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003180 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003181 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003182 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003183 case Type::kStringType:
3184 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003185 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003186 case Type::kFalseType:
3187 case Type::kTrueType:
3188 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003189 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003190 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003191 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003192 int i = 0;
3193 GetInt(o, i);
3194 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003195 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003196 double d = 0.0;
3197 GetDouble(o, d);
3198 val = Value(d);
3199 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003200 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003201 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003202 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003203 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003204 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003205#else
3206 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003207 case json::value_t::object: {
3208 Value::Object value_object;
3209 for (auto it = o.begin(); it != o.end(); it++) {
3210 Value entry;
3211 ParseJsonAsValue(&entry, it.value());
3212 if (entry.Type() != NULL_TYPE)
3213 value_object.emplace(it.key(), std::move(entry));
3214 }
3215 if (value_object.size() > 0) val = Value(std::move(value_object));
3216 } break;
3217 case json::value_t::array: {
3218 Value::Array value_array;
3219 value_array.reserve(o.size());
3220 for (auto it = o.begin(); it != o.end(); it++) {
3221 Value entry;
3222 ParseJsonAsValue(&entry, it.value());
3223 if (entry.Type() != NULL_TYPE)
3224 value_array.emplace_back(std::move(entry));
3225 }
3226 if (value_array.size() > 0) val = Value(std::move(value_array));
3227 } break;
3228 case json::value_t::string:
3229 val = Value(o.get<std::string>());
3230 break;
3231 case json::value_t::boolean:
3232 val = Value(o.get<bool>());
3233 break;
3234 case json::value_t::number_integer:
3235 case json::value_t::number_unsigned:
3236 val = Value(static_cast<int>(o.get<int64_t>()));
3237 break;
3238 case json::value_t::number_float:
3239 val = Value(o.get<double>());
3240 break;
3241 case json::value_t::null:
3242 case json::value_t::discarded:
Christopher Sean Morrison0bfcb4f2022-02-23 10:02:49 -05003243 case json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003244 // default:
3245 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003246 }
3247#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003248 const bool isNotNull = val.Type() != NULL_TYPE;
3249
jrkooncecba5d6c2019-08-29 11:26:22 -05003250 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003251
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003252 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003253}
3254
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003255static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003256 json_const_iterator it;
3257 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003258 return false;
3259 }
3260
jrkooncecba5d6c2019-08-29 11:26:22 -05003261 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003262}
3263
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003264static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003265 const std::string &property,
3266 const bool required,
3267 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003268 json_const_iterator it;
3269 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003270 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003271 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003272 (*err) += "'" + property + "' property is missing";
3273 if (!parent_node.empty()) {
3274 (*err) += " in " + parent_node;
3275 }
3276 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003277 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003278 }
3279 return false;
3280 }
3281
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003282 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003283
3284 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003285 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003286#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003287 isBoolean = value.IsBool();
3288 if (isBoolean) {
3289 boolValue = value.GetBool();
3290 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003291#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003292 isBoolean = value.is_boolean();
3293 if (isBoolean) {
3294 boolValue = value.get<bool>();
3295 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003296#endif
3297 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003298 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003299 if (err) {
3300 (*err) += "'" + property + "' property is not a bool type.\n";
3301 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003302 }
3303 return false;
3304 }
3305
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003306 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003307 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003308 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003309
3310 return true;
3311}
3312
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003313static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3314 const std::string &property,
3315 const bool required,
3316 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003317 json_const_iterator it;
3318 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003319 if (required) {
3320 if (err) {
3321 (*err) += "'" + property + "' property is missing";
3322 if (!parent_node.empty()) {
3323 (*err) += " in " + parent_node;
3324 }
3325 (*err) += ".\n";
3326 }
3327 }
3328 return false;
3329 }
3330
jrkooncecba5d6c2019-08-29 11:26:22 -05003331 int intValue;
3332 bool isInt = GetInt(GetValue(it), intValue);
3333 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003334 if (required) {
3335 if (err) {
3336 (*err) += "'" + property + "' property is not an integer type.\n";
3337 }
3338 }
3339 return false;
3340 }
3341
3342 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003343 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003344 }
3345
3346 return true;
3347}
3348
3349static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3350 const std::string &property,
3351 const bool required,
3352 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003353 json_const_iterator it;
3354 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003355 if (required) {
3356 if (err) {
3357 (*err) += "'" + property + "' property is missing";
3358 if (!parent_node.empty()) {
3359 (*err) += " in " + parent_node;
3360 }
3361 (*err) += ".\n";
3362 }
3363 }
3364 return false;
3365 }
3366
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003367 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003368
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003369 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003370 bool isUValue;
3371#ifdef TINYGLTF_USE_RAPIDJSON
3372 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003373 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003374 uValue = value.GetUint();
3375 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003376 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003377 uValue = value.GetUint64();
3378 isUValue = true;
3379 }
3380#else
3381 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003382 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003383 uValue = value.get<size_t>();
3384 }
3385#endif
3386 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003387 if (required) {
3388 if (err) {
3389 (*err) += "'" + property + "' property is not a positive integer.\n";
3390 }
3391 }
3392 return false;
3393 }
3394
3395 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003396 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003397 }
3398
3399 return true;
3400}
3401
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003402static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003403 const std::string &property,
3404 const bool required,
3405 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003406 json_const_iterator it;
3407
3408 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003409 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003410 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003411 (*err) += "'" + property + "' property is missing";
3412 if (!parent_node.empty()) {
3413 (*err) += " in " + parent_node;
3414 }
3415 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003416 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003417 }
3418 return false;
3419 }
3420
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003421 double numberValue;
3422 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003423
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003424 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003425 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003426 if (err) {
3427 (*err) += "'" + property + "' property is not a number type.\n";
3428 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003429 }
3430 return false;
3431 }
3432
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003433 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003434 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003435 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003436
3437 return true;
3438}
3439
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003440static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003441 const json &o, const std::string &property,
3442 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003443 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003444 json_const_iterator it;
3445 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003446 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003447 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003448 (*err) += "'" + property + "' property is missing";
3449 if (!parent_node.empty()) {
3450 (*err) += " in " + parent_node;
3451 }
3452 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003453 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003454 }
3455 return false;
3456 }
3457
jrkooncecba5d6c2019-08-29 11:26:22 -05003458 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003459 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003460 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003461 (*err) += "'" + property + "' property is not an array";
3462 if (!parent_node.empty()) {
3463 (*err) += " in " + parent_node;
3464 }
3465 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003466 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003467 }
3468 return false;
3469 }
3470
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003471 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003472 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003473 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003474 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003475 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003476 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003477 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003478 if (err) {
3479 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003480 if (!parent_node.empty()) {
3481 (*err) += " in " + parent_node;
3482 }
3483 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003484 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003485 }
3486 return false;
3487 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003488 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003489 }
3490
3491 return true;
3492}
3493
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003494static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3495 const json &o,
3496 const std::string &property,
3497 bool required,
3498 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003499 json_const_iterator it;
3500 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003501 if (required) {
3502 if (err) {
3503 (*err) += "'" + property + "' property is missing";
3504 if (!parent_node.empty()) {
3505 (*err) += " in " + parent_node;
3506 }
3507 (*err) += ".\n";
3508 }
3509 }
3510 return false;
3511 }
3512
jrkooncecba5d6c2019-08-29 11:26:22 -05003513 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003514 if (required) {
3515 if (err) {
3516 (*err) += "'" + property + "' property is not an array";
3517 if (!parent_node.empty()) {
3518 (*err) += " in " + parent_node;
3519 }
3520 (*err) += ".\n";
3521 }
3522 }
3523 return false;
3524 }
3525
3526 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003527 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003528 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003529 int numberValue;
3530 bool isNumber = GetInt(*i, numberValue);
3531 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003532 if (required) {
3533 if (err) {
3534 (*err) += "'" + property + "' property is not an integer type.\n";
3535 if (!parent_node.empty()) {
3536 (*err) += " in " + parent_node;
3537 }
3538 (*err) += ".\n";
3539 }
3540 }
3541 return false;
3542 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003543 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003544 }
3545
3546 return true;
3547}
3548
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003549static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003550 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003551 const std::string &property, bool required,
3552 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003553 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003554 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003555 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003556 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003557 (*err) += "'" + property + "' property is missing";
3558 if (parent_node.empty()) {
3559 (*err) += ".\n";
3560 } else {
3561 (*err) += " in `" + parent_node + "'.\n";
3562 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003563 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003564 }
3565 return false;
3566 }
3567
jrkooncecba5d6c2019-08-29 11:26:22 -05003568 std::string strValue;
3569 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003570 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003571 if (err) {
3572 (*err) += "'" + property + "' property is not a string type.\n";
3573 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003574 }
3575 return false;
3576 }
3577
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003578 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003579 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003580 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003581
3582 return true;
3583}
3584
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003585static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3586 std::string *err, const json &o,
3587 const std::string &property,
3588 bool required,
3589 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003590 json_const_iterator it;
3591 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003592 if (required) {
3593 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003594 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003595 (*err) +=
3596 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003597 } else {
3598 (*err) += "'" + property + "' property is missing.\n";
3599 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003600 }
3601 }
3602 return false;
3603 }
3604
jrkooncecba5d6c2019-08-29 11:26:22 -05003605 const json &dict = GetValue(it);
3606
Luke San Antonio19894c72016-06-14 21:19:51 -04003607 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003608 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003609 if (required) {
3610 if (err) {
3611 (*err) += "'" + property + "' property is not an object.\n";
3612 }
3613 }
3614 return false;
3615 }
3616
3617 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003618
jrkooncecba5d6c2019-08-29 11:26:22 -05003619 json_const_iterator dictIt(ObjectBegin(dict));
3620 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003621
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003622 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003623 int intVal;
3624 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003625 if (required) {
3626 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003627 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003628 }
3629 }
3630 return false;
3631 }
3632
3633 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003634 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003635 }
3636 return true;
3637}
3638
Syoyo Fujita5b407452017-06-04 17:42:41 +09003639static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003640 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003641 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003642 json_const_iterator it;
3643 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003644 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003645 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003646 (*err) += "'" + property + "' property is missing. \n'";
3647 }
3648 }
3649 return false;
3650 }
3651
jrkooncecba5d6c2019-08-29 11:26:22 -05003652 const json &obj = GetValue(it);
3653
3654 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003655 if (required) {
3656 if (err) {
3657 (*err) += "'" + property + "' property is not a JSON object.\n";
3658 }
3659 }
3660 return false;
3661 }
3662
3663 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003664
jrkooncecba5d6c2019-08-29 11:26:22 -05003665 json_const_iterator it2(ObjectBegin(obj));
3666 json_const_iterator itEnd(ObjectEnd(obj));
3667 for (; it2 != itEnd; ++it2) {
3668 double numVal;
3669 if (GetNumber(GetValue(it2), numVal))
3670 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003671 }
3672
3673 return true;
3674}
3675
Selmar09d2ff12018-03-15 17:30:42 +01003676static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003677 const json &o, const std::string &prop,
3678 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003679 // A parameter value can either be a string or an array of either a boolean or
3680 // a number. Booleans of any kind aren't supported here. Granted, it
3681 // complicates the Parameter structure and breaks it semantically in the sense
3682 // that the client probably works off the assumption that if the string is
3683 // empty the vector is used, etc. Would a tagged union work?
3684 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3685 // Found string property.
3686 return true;
3687 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3688 false)) {
3689 // Found a number array.
3690 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003691 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02003692 param->has_number_value = true;
3693 return true;
Selmar09d2ff12018-03-15 17:30:42 +01003694 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3695 false)) {
3696 return true;
3697 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3698 return true;
3699 } else {
3700 if (required) {
3701 if (err) {
3702 (*err) += "parameter must be a string or number / number array.\n";
3703 }
3704 }
3705 return false;
3706 }
3707}
3708
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003709static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3710 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003711 (void)err;
3712
jrkooncecba5d6c2019-08-29 11:26:22 -05003713 json_const_iterator it;
3714 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003715 return false;
3716 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003717
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003718 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003719 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003720 return false;
3721 }
3722 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003723 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003724 json_const_iterator extEnd = ObjectEnd(obj);
3725 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003726 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003727 if (!IsObject(itObj)) continue;
3728 std::string key(GetKey(extIt));
3729 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003730 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003731 // create empty object so that an extension object is still of type
3732 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003733 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003734 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003735 }
Selmar09d2ff12018-03-15 17:30:42 +01003736 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003737 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003738 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003739 }
3740 return true;
3741}
3742
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003743static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3744 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003745 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3746 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3747 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003748 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003749
Selmar09d2ff12018-03-15 17:30:42 +01003750 ParseExtensionsProperty(&asset->extensions, err, o);
3751
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003752 // Unity exporter version is added as extra here
3753 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003754
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003755 if (store_original_json_for_extras_and_extensions) {
3756 {
3757 json_const_iterator it;
3758 if (FindMember(o, "extensions", it)) {
3759 asset->extensions_json_string = JsonToString(GetValue(it));
3760 }
3761 }
3762 {
3763 json_const_iterator it;
3764 if (FindMember(o, "extras", it)) {
3765 asset->extras_json_string = JsonToString(GetValue(it));
3766 }
3767 }
3768 }
3769
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003770 return true;
3771}
3772
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003773static bool ParseImage(Image *image, const int image_idx, std::string *err,
3774 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003775 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003776 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003777 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003778 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003779 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003780
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003781 // schema says oneOf [`bufferView`, `uri`]
3782 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003783 json_const_iterator it;
3784 bool hasBufferView = FindMember(o, "bufferView", it);
3785 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003786
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003787 ParseStringProperty(&image->name, err, o, "name", false);
3788
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003789 if (hasBufferView && hasURI) {
3790 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003791 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003792 (*err) +=
3793 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003794 "defined for image[" +
3795 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003796 }
3797 return false;
3798 }
3799
3800 if (!hasBufferView && !hasURI) {
3801 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003802 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3803 std::to_string(image_idx) + "] name = \"" + image->name +
3804 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003805 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003806 return false;
3807 }
3808
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003809 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003810 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003811
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003812 if (store_original_json_for_extras_and_extensions) {
3813 {
3814 json_const_iterator eit;
3815 if (FindMember(o, "extensions", eit)) {
3816 image->extensions_json_string = JsonToString(GetValue(eit));
3817 }
3818 }
3819 {
3820 json_const_iterator eit;
3821 if (FindMember(o, "extras", eit)) {
3822 image->extras_json_string = JsonToString(GetValue(eit));
3823 }
3824 }
3825 }
3826
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003827 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003828 int bufferView = -1;
3829 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003830 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003831 (*err) += "Failed to parse `bufferView` for image[" +
3832 std::to_string(image_idx) + "] name = \"" + image->name +
3833 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003834 }
3835 return false;
3836 }
3837
3838 std::string mime_type;
3839 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3840
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003841 int width = 0;
3842 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003843
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003844 int height = 0;
3845 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003846
3847 // Just only save some information here. Loading actual image data from
3848 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003849 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003850 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003851 image->width = width;
3852 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003853
3854 return true;
3855 }
3856
Syoyo Fujita246654a2018-03-21 20:32:22 +09003857 // Parse URI & Load image data.
3858
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003859 std::string uri;
3860 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003861 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3862 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003863 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3864 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003865 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003866 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003867 }
3868
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003869 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003870
Syoyo Fujita246654a2018-03-21 20:32:22 +09003871 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003872 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003873 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003874 (*err) += "Failed to decode 'uri' for image[" +
3875 std::to_string(image_idx) + "] name = [" + image->name +
3876 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003877 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003878 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003879 }
3880 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003881 // Assume external file
3882 // Keep texture path (for textures that cannot be decoded)
3883 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003884#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003885 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01003886#else
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003887 std::string decoded_uri = dlib::urldecode(uri);
3888 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3889 /* required */ false, /* required bytes */ 0,
3890 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003891 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003892 (*warn) += "Failed to load external 'uri' for image[" +
3893 std::to_string(image_idx) + "] name = [" + image->name +
3894 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003895 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003896 // If the image cannot be loaded, keep uri as image->uri.
3897 return true;
3898 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003899
Syoyo Fujita246654a2018-03-21 20:32:22 +09003900 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003901 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003902 (*warn) += "Image data is empty for image[" +
3903 std::to_string(image_idx) + "] name = [" + image->name +
3904 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003905 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003906 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003907 }
AlvaroBarua43172232022-09-11 00:41:43 +01003908#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003909 }
3910
Squareysff644d82018-03-13 22:36:18 +01003911 if (*LoadImageData == nullptr) {
3912 if (err) {
3913 (*err) += "No LoadImageData callback specified.\n";
3914 }
3915 return false;
3916 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003917 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003918 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003919}
3920
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003921static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003922 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003923 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003924 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003925 int sampler = -1;
3926 int source = -1;
3927 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003928
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003929 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003930
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003931 texture->sampler = sampler;
3932 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003933
Selmar Kokfa7022f2018-04-04 18:10:20 +02003934 ParseExtensionsProperty(&texture->extensions, err, o);
3935 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003936
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003937 if (store_original_json_for_extras_and_extensions) {
3938 {
3939 json_const_iterator it;
3940 if (FindMember(o, "extensions", it)) {
3941 texture->extensions_json_string = JsonToString(GetValue(it));
3942 }
3943 }
3944 {
3945 json_const_iterator it;
3946 if (FindMember(o, "extras", it)) {
3947 texture->extras_json_string = JsonToString(GetValue(it));
3948 }
3949 }
3950 }
3951
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003952 ParseStringProperty(&texture->name, err, o, "name", false);
3953
Syoyo Fujitabde70212016-02-07 17:38:17 +09003954 return true;
3955}
3956
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003957static bool ParseTextureInfo(
3958 TextureInfo *texinfo, std::string *err, const json &o,
3959 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003960 if (texinfo == nullptr) {
3961 return false;
3962 }
3963
3964 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3965 /* required */ true, "TextureInfo")) {
3966 return false;
3967 }
3968
3969 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3970
3971 ParseExtensionsProperty(&texinfo->extensions, err, o);
3972 ParseExtrasProperty(&texinfo->extras, o);
3973
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003974 if (store_original_json_for_extras_and_extensions) {
3975 {
3976 json_const_iterator it;
3977 if (FindMember(o, "extensions", it)) {
3978 texinfo->extensions_json_string = JsonToString(GetValue(it));
3979 }
3980 }
3981 {
3982 json_const_iterator it;
3983 if (FindMember(o, "extras", it)) {
3984 texinfo->extras_json_string = JsonToString(GetValue(it));
3985 }
3986 }
3987 }
3988
Syoyo Fujita046400b2019-07-24 19:26:48 +09003989 return true;
3990}
3991
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003992static bool ParseNormalTextureInfo(
3993 NormalTextureInfo *texinfo, std::string *err, const json &o,
3994 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003995 if (texinfo == nullptr) {
3996 return false;
3997 }
3998
3999 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4000 /* required */ true, "NormalTextureInfo")) {
4001 return false;
4002 }
4003
4004 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4005 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4006
4007 ParseExtensionsProperty(&texinfo->extensions, err, o);
4008 ParseExtrasProperty(&texinfo->extras, o);
4009
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004010 if (store_original_json_for_extras_and_extensions) {
4011 {
4012 json_const_iterator it;
4013 if (FindMember(o, "extensions", it)) {
4014 texinfo->extensions_json_string = JsonToString(GetValue(it));
4015 }
4016 }
4017 {
4018 json_const_iterator it;
4019 if (FindMember(o, "extras", it)) {
4020 texinfo->extras_json_string = JsonToString(GetValue(it));
4021 }
4022 }
4023 }
4024
Syoyo Fujita046400b2019-07-24 19:26:48 +09004025 return true;
4026}
4027
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004028static bool ParseOcclusionTextureInfo(
4029 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
4030 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004031 if (texinfo == nullptr) {
4032 return false;
4033 }
4034
4035 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4036 /* required */ true, "NormalTextureInfo")) {
4037 return false;
4038 }
4039
4040 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4041 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4042
4043 ParseExtensionsProperty(&texinfo->extensions, err, o);
4044 ParseExtrasProperty(&texinfo->extras, o);
4045
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004046 if (store_original_json_for_extras_and_extensions) {
4047 {
4048 json_const_iterator it;
4049 if (FindMember(o, "extensions", it)) {
4050 texinfo->extensions_json_string = JsonToString(GetValue(it));
4051 }
4052 }
4053 {
4054 json_const_iterator it;
4055 if (FindMember(o, "extras", it)) {
4056 texinfo->extras_json_string = JsonToString(GetValue(it));
4057 }
4058 }
4059 }
4060
Syoyo Fujita046400b2019-07-24 19:26:48 +09004061 return true;
4062}
4063
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004064static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004065 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004066 FsCallbacks *fs, const std::string &basedir,
4067 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004068 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004069 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004070 size_t byteLength;
4071 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4072 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004073 return false;
4074 }
4075
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004076 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004077 buffer->uri.clear();
4078 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004079
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004080 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004081 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004082 if (err) {
4083 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4084 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004085 }
4086
jrkooncecba5d6c2019-08-29 11:26:22 -05004087 json_const_iterator type;
4088 if (FindMember(o, "type", type)) {
4089 std::string typeStr;
4090 if (GetString(GetValue(type), typeStr)) {
4091 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004092 // buffer.type = "arraybuffer";
4093 }
4094 }
4095 }
4096
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004097 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004098 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004099 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004100 // First try embedded data URI.
4101 if (IsDataURI(buffer->uri)) {
4102 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004103 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004104 true)) {
4105 if (err) {
4106 (*err) +=
4107 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4108 }
4109 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004110 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004111 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004112 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004113 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004114 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004115 decoded_uri, basedir, /* required */ true,
4116 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004117 return false;
4118 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004119 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004120 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004121 // load data from (embedded) binary data
4122
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004123 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004124 if (err) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09004125 (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004126 }
4127 return false;
4128 }
4129
4130 if (byteLength > bin_size) {
4131 if (err) {
4132 std::stringstream ss;
4133 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004134 "`byteLength' = "
4135 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004136 (*err) += ss.str();
4137 }
4138 return false;
4139 }
4140
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004141 // Read buffer data
4142 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004143 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004144 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004145
4146 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004147 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004148 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004149 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4150 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004151 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004152 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004153 }
4154 return false;
4155 }
4156 } else {
4157 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004158 std::string decoded_uri = dlib::urldecode(buffer->uri);
4159 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4160 basedir, /* required */ true, byteLength,
4161 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004162 return false;
4163 }
4164 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004165 }
4166
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004167 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004168
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004169 ParseExtensionsProperty(&buffer->extensions, err, o);
4170 ParseExtrasProperty(&buffer->extras, o);
4171
4172 if (store_original_json_for_extras_and_extensions) {
4173 {
4174 json_const_iterator it;
4175 if (FindMember(o, "extensions", it)) {
4176 buffer->extensions_json_string = JsonToString(GetValue(it));
4177 }
4178 }
4179 {
4180 json_const_iterator it;
4181 if (FindMember(o, "extras", it)) {
4182 buffer->extras_json_string = JsonToString(GetValue(it));
4183 }
4184 }
4185 }
4186
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004187 return true;
4188}
4189
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004190static bool ParseBufferView(
4191 BufferView *bufferView, std::string *err, const json &o,
4192 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004193 int buffer = -1;
4194 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004195 return false;
4196 }
4197
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004198 size_t byteOffset = 0;
4199 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004200
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004201 size_t byteLength = 1;
4202 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4203 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004204 return false;
4205 }
4206
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004207 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004208 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004209 // Spec says: When byteStride of referenced bufferView is not defined, it
4210 // means that accessor elements are tightly packed, i.e., effective stride
4211 // equals the size of the element.
4212 // We cannot determine the actual byteStride until Accessor are parsed, thus
4213 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4214 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004215 }
4216
4217 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4218 if (err) {
4219 std::stringstream ss;
4220 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4221 "4 : "
4222 << byteStride << std::endl;
4223
4224 (*err) += ss.str();
4225 }
4226 return false;
4227 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004228
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004229 int target = 0;
4230 ParseIntegerProperty(&target, err, o, "target", false);
4231 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4232 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004233 // OK
4234 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004235 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004236 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004237 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004238
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004239 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004240
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004241 ParseExtensionsProperty(&bufferView->extensions, err, o);
4242 ParseExtrasProperty(&bufferView->extras, o);
4243
4244 if (store_original_json_for_extras_and_extensions) {
4245 {
4246 json_const_iterator it;
4247 if (FindMember(o, "extensions", it)) {
4248 bufferView->extensions_json_string = JsonToString(GetValue(it));
4249 }
4250 }
4251 {
4252 json_const_iterator it;
4253 if (FindMember(o, "extras", it)) {
4254 bufferView->extras_json_string = JsonToString(GetValue(it));
4255 }
4256 }
4257 }
4258
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004259 bufferView->buffer = buffer;
4260 bufferView->byteOffset = byteOffset;
4261 bufferView->byteLength = byteLength;
4262 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004263 return true;
4264}
4265
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004266static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4267 const json &o) {
4268 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004269
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004270 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004271 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4272 return false;
4273 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004274
jrkooncecba5d6c2019-08-29 11:26:22 -05004275 json_const_iterator indices_iterator;
4276 json_const_iterator values_iterator;
4277 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004278 (*err) = "the sparse object of this accessor doesn't have indices";
4279 return false;
4280 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004281
jrkooncecba5d6c2019-08-29 11:26:22 -05004282 if (!FindMember(o, "values", values_iterator)) {
imallettd9ce9eb2022-10-07 10:37:09 -07004283 (*err) = "the sparse object of this accessor doesn't have values";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004284 return false;
4285 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004286
jrkooncecba5d6c2019-08-29 11:26:22 -05004287 const json &indices_obj = GetValue(indices_iterator);
4288 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004289
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004290 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004291 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4292 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004293 return false;
4294 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004295 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004296 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004297 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004298 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004299 return false;
4300 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004301
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004302 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004303 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004304 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004305 return false;
4306 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004307 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004308 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004309
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004310 accessor->sparse.count = count;
4311 accessor->sparse.indices.bufferView = indices_buffer_view;
4312 accessor->sparse.indices.byteOffset = indices_byte_offset;
4313 accessor->sparse.indices.componentType = component_type;
4314 accessor->sparse.values.bufferView = values_buffer_view;
4315 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004316
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004317 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004318}
4319
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004320static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4321 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004322 int bufferView = -1;
4323 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004324
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004325 size_t byteOffset = 0;
4326 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004327
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004328 bool normalized = false;
4329 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4330
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004331 size_t componentType = 0;
4332 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4333 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004334 return false;
4335 }
4336
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004337 size_t count = 0;
4338 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004339 return false;
4340 }
4341
4342 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004343 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004344 return false;
4345 }
4346
4347 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004348 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004349 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004350 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004351 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004352 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004353 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004354 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004355 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004356 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004357 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004358 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004359 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004360 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004361 } else {
4362 std::stringstream ss;
4363 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004364 if (err) {
4365 (*err) += ss.str();
4366 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004367 return false;
4368 }
4369
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004370 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004371
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004372 accessor->minValues.clear();
4373 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004374 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4375 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004376
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004377 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4378 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004379
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004380 accessor->count = count;
4381 accessor->bufferView = bufferView;
4382 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004383 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004384 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004385 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4386 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004387 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004388 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004389 } else {
4390 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004391 ss << "Invalid `componentType` in accessor. Got " << componentType
4392 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004393 if (err) {
4394 (*err) += ss.str();
4395 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004396 return false;
4397 }
4398 }
4399
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004400 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004401 ParseExtrasProperty(&(accessor->extras), o);
4402
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004403 if (store_original_json_for_extras_and_extensions) {
4404 {
4405 json_const_iterator it;
4406 if (FindMember(o, "extensions", it)) {
4407 accessor->extensions_json_string = JsonToString(GetValue(it));
4408 }
4409 }
4410 {
4411 json_const_iterator it;
4412 if (FindMember(o, "extras", it)) {
4413 accessor->extras_json_string = JsonToString(GetValue(it));
4414 }
4415 }
4416 }
4417
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004418 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004419 json_const_iterator iterator;
4420 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004421 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004422 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004423 }
4424
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004425 return true;
4426}
4427
Alex Wood7319db72019-01-24 15:38:16 -05004428#ifdef TINYGLTF_ENABLE_DRACO
4429
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004430static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4431 std::vector<uint8_t> &outBuffer) {
4432 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004433 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004434 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4435 outBuffer.size());
4436 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004437 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004438 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4439 const draco::Mesh::Face &face = mesh->face(f);
4440 if (componentSize == 2) {
4441 uint16_t indices[3] = {(uint16_t)face[0].value(),
4442 (uint16_t)face[1].value(),
4443 (uint16_t)face[2].value()};
4444 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4445 faceStride);
4446 } else {
4447 uint8_t indices[3] = {(uint8_t)face[0].value(),
4448 (uint8_t)face[1].value(),
4449 (uint8_t)face[2].value()};
4450 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4451 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004452 }
4453 }
4454 }
4455}
4456
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004457template <typename T>
4458static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4459 const draco::PointAttribute *pAttribute,
4460 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004461 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004462 T values[4] = {0, 0, 0, 0};
4463 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004464 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004465 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4466 values))
Alex Wood7319db72019-01-24 15:38:16 -05004467 return false;
4468
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004469 memcpy(outBuffer.data() + byteOffset, &values[0],
4470 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004471 byteOffset += sizeof(T) * pAttribute->num_components();
4472 }
4473
4474 return true;
4475}
4476
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004477static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4478 const draco::PointAttribute *pAttribute,
4479 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004480 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004481 switch (componentType) {
4482 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4483 decodeResult =
4484 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4485 break;
4486 case TINYGLTF_COMPONENT_TYPE_BYTE:
4487 decodeResult =
4488 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4489 break;
4490 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4491 decodeResult =
4492 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4493 break;
4494 case TINYGLTF_COMPONENT_TYPE_SHORT:
4495 decodeResult =
4496 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4497 break;
4498 case TINYGLTF_COMPONENT_TYPE_INT:
4499 decodeResult =
4500 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4501 break;
4502 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4503 decodeResult =
4504 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4505 break;
4506 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4507 decodeResult =
4508 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4509 break;
4510 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4511 decodeResult =
4512 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4513 break;
4514 default:
4515 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004516 }
4517
4518 return decodeResult;
4519}
4520
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004521static bool ParseDracoExtension(Primitive *primitive, Model *model,
4522 std::string *err,
4523 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004524 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004525 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004526 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004527 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004528 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004529
4530 auto attributesObject = attributesValue.Get<Value::Object>();
4531 int bufferView = bufferViewValue.Get<int>();
4532
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004533 BufferView &view = model->bufferViews[bufferView];
4534 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004535 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004536 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004537 view.dracoDecoded = true;
4538
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004539 const char *bufferViewData =
4540 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004541 size_t bufferViewSize = view.byteLength;
4542
4543 // decode draco
4544 draco::DecoderBuffer decoderBuffer;
4545 decoderBuffer.Init(bufferViewData, bufferViewSize);
4546 draco::Decoder decoder;
4547 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4548 if (!decodeResult.ok()) {
4549 return false;
4550 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004551 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004552
4553 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004554 if (primitive->indices >= 0) {
4555 int32_t componentSize = GetComponentSizeInBytes(
4556 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004557 Buffer decodedIndexBuffer;
4558 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4559
4560 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4561
4562 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4563
4564 BufferView decodedIndexBufferView;
4565 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004566 decodedIndexBufferView.byteLength =
4567 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004568 decodedIndexBufferView.byteOffset = 0;
4569 decodedIndexBufferView.byteStride = 0;
4570 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4571 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4572
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004573 model->accessors[primitive->indices].bufferView =
4574 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004575 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004576 }
4577
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004578 for (const auto &attribute : attributesObject) {
4579 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004580 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004581 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004582
4583 int dracoAttributeIndex = attribute.second.Get<int>();
4584 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004585 const auto componentType =
4586 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004587
4588 // Create a new buffer for this decoded buffer
4589 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004590 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4591 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004592 decodedBuffer.data.resize(bufferSize);
4593
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004594 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4595 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004596 return false;
4597
4598 model->buffers.emplace_back(std::move(decodedBuffer));
4599
4600 BufferView decodedBufferView;
4601 decodedBufferView.buffer = int(model->buffers.size() - 1);
4602 decodedBufferView.byteLength = bufferSize;
4603 decodedBufferView.byteOffset = pAttribute->byte_offset();
4604 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004605 decodedBufferView.target = primitive->indices >= 0
4606 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4607 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004608 model->bufferViews.emplace_back(std::move(decodedBufferView));
4609
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004610 model->accessors[primitiveAttribute->second].bufferView =
4611 int(model->bufferViews.size() - 1);
4612 model->accessors[primitiveAttribute->second].count =
4613 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004614 }
4615
4616 return true;
4617}
4618#endif
4619
4620static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004621 const json &o,
4622 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004623 int material = -1;
4624 ParseIntegerProperty(&material, err, o, "material", false);
4625 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004626
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004627 int mode = TINYGLTF_MODE_TRIANGLES;
4628 ParseIntegerProperty(&mode, err, o, "mode", false);
imallettd9ce9eb2022-10-07 10:37:09 -07004629 primitive->mode = mode; // Why only triangles were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004630
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004631 int indices = -1;
4632 ParseIntegerProperty(&indices, err, o, "indices", false);
4633 primitive->indices = indices;
4634 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4635 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004636 return false;
4637 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004638
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004639 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004640 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004641 if (FindMember(o, "targets", targetsObject) &&
4642 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004643 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4644 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4645 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004646 std::map<std::string, int> targetAttribues;
4647
jrkooncecba5d6c2019-08-29 11:26:22 -05004648 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004649 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004650 json_const_iterator dictIt(ObjectBegin(dict));
4651 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004652
jrkooncecba5d6c2019-08-29 11:26:22 -05004653 for (; dictIt != dictItEnd; ++dictIt) {
4654 int iVal;
4655 if (GetInt(GetValue(dictIt), iVal))
4656 targetAttribues[GetKey(dictIt)] = iVal;
4657 }
4658 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004659 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004660 }
4661 }
4662
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004663 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004664 ParseExtensionsProperty(&primitive->extensions, err, o);
4665
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004666 if (store_original_json_for_extras_and_extensions) {
4667 {
4668 json_const_iterator it;
4669 if (FindMember(o, "extensions", it)) {
4670 primitive->extensions_json_string = JsonToString(GetValue(it));
4671 }
4672 }
4673 {
4674 json_const_iterator it;
4675 if (FindMember(o, "extras", it)) {
4676 primitive->extras_json_string = JsonToString(GetValue(it));
4677 }
4678 }
4679 }
4680
Alex Wood7319db72019-01-24 15:38:16 -05004681#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004682 auto dracoExtension =
4683 primitive->extensions.find("KHR_draco_mesh_compression");
4684 if (dracoExtension != primitive->extensions.end()) {
4685 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004686 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004687#else
4688 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004689#endif
4690
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004691 return true;
4692}
4693
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004694static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4695 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004696 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004697
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004698 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004699 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004700 if (FindMember(o, "primitives", primObject) &&
4701 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004702 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4703 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4704 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004705 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004706 if (ParsePrimitive(&primitive, model, err, *i,
4707 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004708 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004709 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004710 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004711 }
4712 }
4713
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004714 // Should probably check if has targets and if dimensions fit
4715 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4716
Selmar09d2ff12018-03-15 17:30:42 +01004717 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004718 ParseExtrasProperty(&(mesh->extras), o);
4719
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004720 if (store_original_json_for_extras_and_extensions) {
4721 {
4722 json_const_iterator it;
4723 if (FindMember(o, "extensions", it)) {
4724 mesh->extensions_json_string = JsonToString(GetValue(it));
4725 }
4726 }
4727 {
4728 json_const_iterator it;
4729 if (FindMember(o, "extras", it)) {
4730 mesh->extras_json_string = JsonToString(GetValue(it));
4731 }
4732 }
4733 }
4734
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004735 return true;
4736}
4737
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004738static bool ParseNode(Node *node, std::string *err, const json &o,
4739 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004740 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004741
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004742 int skin = -1;
4743 ParseIntegerProperty(&skin, err, o, "skin", false);
4744 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004745
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004746 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004747 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004748 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4749 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4750 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4751 }
4752
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004753 int camera = -1;
4754 ParseIntegerProperty(&camera, err, o, "camera", false);
4755 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004756
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004757 int mesh = -1;
4758 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4759 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004760
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004761 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004762 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004763
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004764 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4765
Selmar09d2ff12018-03-15 17:30:42 +01004766 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004767 ParseExtrasProperty(&(node->extras), o);
4768
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004769 if (store_original_json_for_extras_and_extensions) {
4770 {
4771 json_const_iterator it;
4772 if (FindMember(o, "extensions", it)) {
4773 node->extensions_json_string = JsonToString(GetValue(it));
4774 }
4775 }
4776 {
4777 json_const_iterator it;
4778 if (FindMember(o, "extras", it)) {
4779 node->extras_json_string = JsonToString(GetValue(it));
4780 }
4781 }
4782 }
4783
Emanuel Schrade186322b2017-11-06 11:14:41 +01004784 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004785}
4786
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004787static bool ParsePbrMetallicRoughness(
4788 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4789 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004790 if (pbr == nullptr) {
4791 return false;
4792 }
4793
4794 std::vector<double> baseColorFactor;
4795 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4796 /* required */ false)) {
4797 if (baseColorFactor.size() != 4) {
4798 if (err) {
4799 (*err) +=
4800 "Array length of `baseColorFactor` parameter in "
4801 "pbrMetallicRoughness must be 4, but got " +
4802 std::to_string(baseColorFactor.size()) + "\n";
4803 }
4804 return false;
4805 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004806 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004807 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004808
4809 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004810 json_const_iterator it;
4811 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004812 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4813 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004814 }
4815 }
4816
4817 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004818 json_const_iterator it;
4819 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004820 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4821 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004822 }
4823 }
4824
4825 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4826 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4827
4828 ParseExtensionsProperty(&pbr->extensions, err, o);
4829 ParseExtrasProperty(&pbr->extras, o);
4830
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004831 if (store_original_json_for_extras_and_extensions) {
4832 {
4833 json_const_iterator it;
4834 if (FindMember(o, "extensions", it)) {
4835 pbr->extensions_json_string = JsonToString(GetValue(it));
4836 }
4837 }
4838 {
4839 json_const_iterator it;
4840 if (FindMember(o, "extras", it)) {
4841 pbr->extras_json_string = JsonToString(GetValue(it));
4842 }
4843 }
4844 }
4845
Syoyo Fujita046400b2019-07-24 19:26:48 +09004846 return true;
4847}
4848
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004849static bool ParseMaterial(Material *material, std::string *err, const json &o,
4850 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004851 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4852
Syoyo Fujitaff515702019-08-24 16:29:14 +09004853 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4854 "emissiveFactor",
4855 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004856 if (material->emissiveFactor.size() != 3) {
4857 if (err) {
4858 (*err) +=
4859 "Array length of `emissiveFactor` parameter in "
4860 "material must be 3, but got " +
4861 std::to_string(material->emissiveFactor.size()) + "\n";
4862 }
4863 return false;
4864 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004865 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004866 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004867 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004868 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004869
4870 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4871 /* required */ false);
4872 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4873 /* required */ false);
4874 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4875 /* required */ false);
4876
4877 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004878 json_const_iterator it;
4879 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004880 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004881 GetValue(it),
4882 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004883 }
4884 }
4885
4886 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004887 json_const_iterator it;
4888 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004889 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4890 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004891 }
4892 }
4893
4894 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004895 json_const_iterator it;
4896 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004897 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4898 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004899 }
4900 }
4901
4902 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004903 json_const_iterator it;
4904 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004905 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4906 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004907 }
4908 }
4909
4910 // Old code path. For backward compatibility, we still store material values
4911 // as Parameter. This will create duplicated information for
imallettd9ce9eb2022-10-07 10:37:09 -07004912 // example(pbrMetallicRoughness), but should be negligible in terms of memory
Syoyo Fujita046400b2019-07-24 19:26:48 +09004913 // consumption.
4914 // TODO(syoyo): Remove in the next major release.
4915 material->values.clear();
4916 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004917
jrkooncecba5d6c2019-08-29 11:26:22 -05004918 json_const_iterator it(ObjectBegin(o));
4919 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004920
jrkooncecba5d6c2019-08-29 11:26:22 -05004921 for (; it != itEnd; ++it) {
4922 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004923 if (key == "pbrMetallicRoughness") {
4924 if (IsObject(GetValue(it))) {
4925 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004926
jrkoonce06c30c42019-09-03 15:56:48 -05004927 json_const_iterator itVal(ObjectBegin(values_object));
4928 json_const_iterator itValEnd(ObjectEnd(values_object));
4929
4930 for (; itVal != itValEnd; ++itVal) {
4931 Parameter param;
4932 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004933 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004934 material->values.emplace(GetKey(itVal), std::move(param));
4935 }
4936 }
4937 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004938 } else if (key == "extensions" || key == "extras") {
4939 // done later, skip, otherwise poorly parsed contents will be saved in the
4940 // parametermap and serialized again later
4941 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004942 Parameter param;
4943 if (ParseParameterProperty(&param, err, o, key, false)) {
4944 // names of materials have already been parsed. Putting it in this map
imallettd9ce9eb2022-10-07 10:37:09 -07004945 // doesn't correctly reflect the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004946 if (key != "name")
4947 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004948 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004949 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004950 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004951
Syoyo Fujita046400b2019-07-24 19:26:48 +09004952 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004953 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004954 ParseExtrasProperty(&(material->extras), o);
4955
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004956 if (store_original_json_for_extras_and_extensions) {
4957 {
4958 json_const_iterator eit;
4959 if (FindMember(o, "extensions", eit)) {
4960 material->extensions_json_string = JsonToString(GetValue(eit));
4961 }
4962 }
4963 {
4964 json_const_iterator eit;
4965 if (FindMember(o, "extras", eit)) {
4966 material->extras_json_string = JsonToString(GetValue(eit));
4967 }
4968 }
4969 }
4970
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004971 return true;
4972}
4973
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004974static bool ParseAnimationChannel(
4975 AnimationChannel *channel, std::string *err, const json &o,
4976 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004977 int samplerIndex = -1;
4978 int targetIndex = -1;
4979 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4980 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004981 if (err) {
4982 (*err) += "`sampler` field is missing in animation channels\n";
4983 }
4984 return false;
4985 }
4986
jrkooncecba5d6c2019-08-29 11:26:22 -05004987 json_const_iterator targetIt;
4988 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4989 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004990
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004991 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004992 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004993 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004994 }
4995 return false;
4996 }
4997
4998 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4999 true)) {
5000 if (err) {
5001 (*err) += "`path` field is missing in animation.channels.target\n";
5002 }
5003 return false;
5004 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005005 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
5006 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01005007 json_const_iterator it;
5008 if (FindMember(target_object, "extensions", it)) {
5009 channel->target_extensions_json_string = JsonToString(GetValue(it));
5010 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005011 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005012 }
5013
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005014 channel->sampler = samplerIndex;
5015 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005016
Selmar Kok4e2988e2019-08-16 14:08:08 +02005017 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005018 ParseExtrasProperty(&(channel->extras), o);
5019
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005020 if (store_original_json_for_extras_and_extensions) {
5021 {
5022 json_const_iterator it;
5023 if (FindMember(o, "extensions", it)) {
5024 channel->extensions_json_string = JsonToString(GetValue(it));
5025 }
5026 }
5027 {
5028 json_const_iterator it;
5029 if (FindMember(o, "extras", it)) {
5030 channel->extras_json_string = JsonToString(GetValue(it));
5031 }
5032 }
5033 }
5034
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005035 return true;
5036}
5037
5038static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005039 const json &o,
5040 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005041 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005042 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005043 if (FindMember(o, "channels", channelsIt) &&
5044 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005045 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
5046 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
5047 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005048 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005049 if (ParseAnimationChannel(
5050 &channel, err, *i,
5051 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005052 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005053 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005054 }
5055 }
5056 }
5057 }
5058
5059 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005060 json_const_iterator samplerIt;
5061 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5062 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005063
jrkooncecba5d6c2019-08-29 11:26:22 -05005064 json_const_array_iterator it = ArrayBegin(sampler_array);
5065 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005066
jrkooncecba5d6c2019-08-29 11:26:22 -05005067 for (; it != itEnd; ++it) {
5068 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005069
5070 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005071 int inputIndex = -1;
5072 int outputIndex = -1;
5073 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005074 if (err) {
5075 (*err) += "`input` field is missing in animation.sampler\n";
5076 }
5077 return false;
5078 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005079 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5080 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005081 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005082 if (err) {
5083 (*err) += "`output` field is missing in animation.sampler\n";
5084 }
5085 return false;
5086 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005087 sampler.input = inputIndex;
5088 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005089 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005090 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005091
5092 if (store_original_json_for_extras_and_extensions) {
5093 {
5094 json_const_iterator eit;
5095 if (FindMember(o, "extensions", eit)) {
5096 sampler.extensions_json_string = JsonToString(GetValue(eit));
5097 }
5098 }
5099 {
5100 json_const_iterator eit;
5101 if (FindMember(o, "extras", eit)) {
5102 sampler.extras_json_string = JsonToString(GetValue(eit));
5103 }
5104 }
5105 }
5106
jrkooncecba5d6c2019-08-29 11:26:22 -05005107 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005108 }
5109 }
5110 }
5111
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005112 ParseStringProperty(&animation->name, err, o, "name", false);
5113
Selmar Kok4e2988e2019-08-16 14:08:08 +02005114 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005115 ParseExtrasProperty(&(animation->extras), o);
5116
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005117 if (store_original_json_for_extras_and_extensions) {
5118 {
5119 json_const_iterator it;
5120 if (FindMember(o, "extensions", it)) {
5121 animation->extensions_json_string = JsonToString(GetValue(it));
5122 }
5123 }
5124 {
5125 json_const_iterator it;
5126 if (FindMember(o, "extras", it)) {
5127 animation->extras_json_string = JsonToString(GetValue(it));
5128 }
5129 }
5130 }
5131
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005132 return true;
5133}
5134
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005135static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5136 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005137 ParseStringProperty(&sampler->name, err, o, "name", false);
5138
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005139 int minFilter = -1;
5140 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005141 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5142 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005143 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005144 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5145 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5146 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5147 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005148 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5149 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005150
imallettd9ce9eb2022-10-07 10:37:09 -07005151 // TODO(syoyo): Check the value is allowed one.
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005152 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005153
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005154 sampler->minFilter = minFilter;
5155 sampler->magFilter = magFilter;
5156 sampler->wrapS = wrapS;
5157 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005158 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005159
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005160 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005161 ParseExtrasProperty(&(sampler->extras), o);
5162
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005163 if (store_original_json_for_extras_and_extensions) {
5164 {
5165 json_const_iterator it;
5166 if (FindMember(o, "extensions", it)) {
5167 sampler->extensions_json_string = JsonToString(GetValue(it));
5168 }
5169 }
5170 {
5171 json_const_iterator it;
5172 if (FindMember(o, "extras", it)) {
5173 sampler->extras_json_string = JsonToString(GetValue(it));
5174 }
5175 }
5176 }
5177
Syoyo Fujitac2615632016-06-19 21:56:06 +09005178 return true;
5179}
5180
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005181static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5182 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005183 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005184
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005185 std::vector<int> joints;
5186 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005187 return false;
5188 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005189 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005190
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005191 int skeleton = -1;
5192 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5193 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005194
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005195 int invBind = -1;
5196 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5197 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005198
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005199 ParseExtensionsProperty(&(skin->extensions), err, o);
5200 ParseExtrasProperty(&(skin->extras), o);
5201
5202 if (store_original_json_for_extras_and_extensions) {
5203 {
5204 json_const_iterator it;
5205 if (FindMember(o, "extensions", it)) {
5206 skin->extensions_json_string = JsonToString(GetValue(it));
5207 }
5208 }
5209 {
5210 json_const_iterator it;
5211 if (FindMember(o, "extras", it)) {
5212 skin->extras_json_string = JsonToString(GetValue(it));
5213 }
5214 }
5215 }
5216
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005217 return true;
5218}
5219
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005220static bool ParsePerspectiveCamera(
5221 PerspectiveCamera *camera, std::string *err, const json &o,
5222 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005223 double yfov = 0.0;
5224 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5225 return false;
5226 }
5227
5228 double znear = 0.0;
5229 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5230 "PerspectiveCamera")) {
5231 return false;
5232 }
5233
5234 double aspectRatio = 0.0; // = invalid
5235 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5236 "PerspectiveCamera");
5237
5238 double zfar = 0.0; // = invalid
5239 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5240
Selmar Kok31cb7f92018-10-03 15:39:05 +02005241 camera->aspectRatio = aspectRatio;
5242 camera->zfar = zfar;
5243 camera->yfov = yfov;
5244 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005245
Selmar09d2ff12018-03-15 17:30:42 +01005246 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005247 ParseExtrasProperty(&(camera->extras), o);
5248
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005249 if (store_original_json_for_extras_and_extensions) {
5250 {
5251 json_const_iterator it;
5252 if (FindMember(o, "extensions", it)) {
5253 camera->extensions_json_string = JsonToString(GetValue(it));
5254 }
5255 }
5256 {
5257 json_const_iterator it;
5258 if (FindMember(o, "extras", it)) {
5259 camera->extras_json_string = JsonToString(GetValue(it));
5260 }
5261 }
5262 }
5263
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005264 // TODO(syoyo): Validate parameter values.
5265
5266 return true;
5267}
5268
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005269static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5270 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005271 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5272 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005273
Johan Bowald52936a02019-07-17 09:06:45 +02005274 ParseExtensionsProperty(&light->extensions, err, o);
5275 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005276
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005277 if (store_original_json_for_extras_and_extensions) {
5278 {
5279 json_const_iterator it;
5280 if (FindMember(o, "extensions", it)) {
5281 light->extensions_json_string = JsonToString(GetValue(it));
5282 }
5283 }
5284 {
5285 json_const_iterator it;
5286 if (FindMember(o, "extras", it)) {
5287 light->extras_json_string = JsonToString(GetValue(it));
5288 }
5289 }
5290 }
5291
Johan Bowald52936a02019-07-17 09:06:45 +02005292 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005293
Johan Bowald52936a02019-07-17 09:06:45 +02005294 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005295}
5296
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005297static bool ParseOrthographicCamera(
5298 OrthographicCamera *camera, std::string *err, const json &o,
5299 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005300 double xmag = 0.0;
5301 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5302 return false;
5303 }
5304
5305 double ymag = 0.0;
5306 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5307 return false;
5308 }
5309
5310 double zfar = 0.0;
5311 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5312 return false;
5313 }
5314
5315 double znear = 0.0;
5316 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5317 "OrthographicCamera")) {
5318 return false;
5319 }
5320
Selmar09d2ff12018-03-15 17:30:42 +01005321 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005322 ParseExtrasProperty(&(camera->extras), o);
5323
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005324 if (store_original_json_for_extras_and_extensions) {
5325 {
5326 json_const_iterator it;
5327 if (FindMember(o, "extensions", it)) {
5328 camera->extensions_json_string = JsonToString(GetValue(it));
5329 }
5330 }
5331 {
5332 json_const_iterator it;
5333 if (FindMember(o, "extras", it)) {
5334 camera->extras_json_string = JsonToString(GetValue(it));
5335 }
5336 }
5337 }
5338
Selmar Kok31cb7f92018-10-03 15:39:05 +02005339 camera->xmag = xmag;
5340 camera->ymag = ymag;
5341 camera->zfar = zfar;
5342 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005343
5344 // TODO(syoyo): Validate parameter values.
5345
5346 return true;
5347}
5348
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005349static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5350 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005351 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5352 return false;
5353 }
5354
5355 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005356 json_const_iterator orthoIt;
5357 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005358 if (err) {
5359 std::stringstream ss;
imallettd9ce9eb2022-10-07 10:37:09 -07005360 ss << "Orthographic camera description not found." << std::endl;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005361 (*err) += ss.str();
5362 }
5363 return false;
5364 }
5365
jrkooncecba5d6c2019-08-29 11:26:22 -05005366 const json &v = GetValue(orthoIt);
5367 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005368 if (err) {
5369 std::stringstream ss;
5370 ss << "\"orthographic\" is not a JSON object." << std::endl;
5371 (*err) += ss.str();
5372 }
5373 return false;
5374 }
5375
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005376 if (!ParseOrthographicCamera(
5377 &camera->orthographic, err, v,
5378 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005379 return false;
5380 }
5381 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005382 json_const_iterator perspIt;
5383 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005384 if (err) {
5385 std::stringstream ss;
5386 ss << "Perspective camera description not found." << std::endl;
5387 (*err) += ss.str();
5388 }
5389 return false;
5390 }
5391
jrkooncecba5d6c2019-08-29 11:26:22 -05005392 const json &v = GetValue(perspIt);
5393 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005394 if (err) {
5395 std::stringstream ss;
5396 ss << "\"perspective\" is not a JSON object." << std::endl;
5397 (*err) += ss.str();
5398 }
5399 return false;
5400 }
5401
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005402 if (!ParsePerspectiveCamera(
5403 &camera->perspective, err, v,
5404 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005405 return false;
5406 }
5407 } else {
5408 if (err) {
5409 std::stringstream ss;
5410 ss << "Invalid camera type: \"" << camera->type
5411 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5412 (*err) += ss.str();
5413 }
5414 return false;
5415 }
5416
5417 ParseStringProperty(&camera->name, err, o, "name", false);
5418
Selmar09d2ff12018-03-15 17:30:42 +01005419 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005420 ParseExtrasProperty(&(camera->extras), o);
5421
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005422 if (store_original_json_for_extras_and_extensions) {
5423 {
5424 json_const_iterator it;
5425 if (FindMember(o, "extensions", it)) {
5426 camera->extensions_json_string = JsonToString(GetValue(it));
5427 }
5428 }
5429 {
5430 json_const_iterator it;
5431 if (FindMember(o, "extras", it)) {
5432 camera->extras_json_string = JsonToString(GetValue(it));
5433 }
5434 }
5435 }
5436
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005437 return true;
5438}
5439
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005440static bool ParseLight(Light *light, std::string *err, const json &o,
5441 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005442 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5443 return false;
5444 }
5445
5446 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005447 json_const_iterator spotIt;
5448 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005449 if (err) {
5450 std::stringstream ss;
5451 ss << "Spot light description not found." << std::endl;
5452 (*err) += ss.str();
5453 }
5454 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005455 }
5456
jrkooncecba5d6c2019-08-29 11:26:22 -05005457 const json &v = GetValue(spotIt);
5458 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005459 if (err) {
5460 std::stringstream ss;
5461 ss << "\"spot\" is not a JSON object." << std::endl;
5462 (*err) += ss.str();
5463 }
5464 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005465 }
5466
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005467 if (!ParseSpotLight(&light->spot, err, v,
5468 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005469 return false;
5470 }
5471 }
5472
5473 ParseStringProperty(&light->name, err, o, "name", false);
5474 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5475 ParseNumberProperty(&light->range, err, o, "range", false);
5476 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5477 ParseExtensionsProperty(&light->extensions, err, o);
5478 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005479
5480 if (store_original_json_for_extras_and_extensions) {
5481 {
5482 json_const_iterator it;
5483 if (FindMember(o, "extensions", it)) {
5484 light->extensions_json_string = JsonToString(GetValue(it));
5485 }
5486 }
5487 {
5488 json_const_iterator it;
5489 if (FindMember(o, "extras", it)) {
5490 light->extras_json_string = JsonToString(GetValue(it));
5491 }
5492 }
5493 }
5494
Johan Bowald52936a02019-07-17 09:06:45 +02005495 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005496}
5497
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005498bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005499 const char *json_str,
5500 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005501 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005502 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005503 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005504 if (err) {
5505 (*err) = "JSON string too short.\n";
5506 }
5507 return false;
5508 }
5509
jrkooncecba5d6c2019-08-29 11:26:22 -05005510 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005511
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005512#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5513 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005514 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005515 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005516 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005517
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005518 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005519 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005520 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005521 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005522 return false;
5523 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005524#else
5525 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005526 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005527
jrkooncecba5d6c2019-08-29 11:26:22 -05005528 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005529 // Assume parsing was failed.
5530 if (err) {
5531 (*err) = "Failed to parse JSON object\n";
5532 }
5533 return false;
5534 }
5535 }
5536#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005537
jrkooncecba5d6c2019-08-29 11:26:22 -05005538 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005539 // root is not an object.
5540 if (err) {
5541 (*err) = "Root element is not a JSON object\n";
5542 }
5543 return false;
5544 }
5545
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005546 {
5547 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005548 json_const_iterator it;
5549 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005550 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005551 json_const_iterator version_it;
5552 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005553 if (FindMember(itObj, "version", version_it) &&
5554 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005555 version_found = true;
5556 }
5557 }
5558 if (version_found) {
5559 // OK
5560 } else if (check_sections & REQUIRE_VERSION) {
5561 if (err) {
5562 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5563 }
5564 return false;
5565 }
5566 }
5567
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005568 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005569 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005570
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005571 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005572 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005573 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005574 };
5575
Syoyo Fujita83675312017-12-02 21:14:13 +09005576 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005577 if ((check_sections & REQUIRE_SCENES) &&
5578 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005579 if (err) {
5580 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5581 }
5582 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005583 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005584 }
5585
Syoyo Fujita83675312017-12-02 21:14:13 +09005586 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005587 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005588 if (err) {
5589 (*err) += "\"nodes\" object not found in .gltf\n";
5590 }
5591 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005592 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005593 }
5594
Syoyo Fujita83675312017-12-02 21:14:13 +09005595 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005596 if ((check_sections & REQUIRE_ACCESSORS) &&
5597 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005598 if (err) {
5599 (*err) += "\"accessors\" object not found in .gltf\n";
5600 }
5601 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005602 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005603 }
5604
Syoyo Fujita83675312017-12-02 21:14:13 +09005605 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005606 if ((check_sections & REQUIRE_BUFFERS) &&
5607 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005608 if (err) {
5609 (*err) += "\"buffers\" object not found in .gltf\n";
5610 }
5611 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005612 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005613 }
5614
Syoyo Fujita83675312017-12-02 21:14:13 +09005615 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005616 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5617 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005618 if (err) {
5619 (*err) += "\"bufferViews\" object not found in .gltf\n";
5620 }
5621 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005622 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005623 }
5624
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005625 model->buffers.clear();
5626 model->bufferViews.clear();
5627 model->accessors.clear();
5628 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005629 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005630 model->nodes.clear();
5631 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005632 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005633 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005634 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005635
Syoyo Fujita83675312017-12-02 21:14:13 +09005636 // 1. Parse Asset
5637 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005638 json_const_iterator it;
5639 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5640 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005641
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005642 ParseAsset(&model->asset, err, root,
5643 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005644 }
5645 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005646
jrkoonce51453942019-09-03 09:48:30 -05005647#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005648 auto ForEachInArray = [](const json &_v, const char *member,
5649 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005650#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005651 // The std::function<> implementation can be less efficient because it will
5652 // allocate heap when the size of the captured lambda is above 16 bytes with
5653 // clang and gcc, but it does not require C++14.
5654 auto ForEachInArray = [](const json &_v, const char *member,
5655 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005656#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005657 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005658 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005659 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005660 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005661 auto it = ArrayBegin(root);
5662 auto end = ArrayEnd(root);
5663 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005664 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005665 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005666 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005667 return true;
5668 };
5669
jrkooncecba5d6c2019-08-29 11:26:22 -05005670 // 2. Parse extensionUsed
5671 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005672 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005673 std::string str;
5674 GetString(o, str);
5675 model->extensionsUsed.emplace_back(std::move(str));
5676 return true;
5677 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005678 }
5679
Syoyo Fujita83675312017-12-02 21:14:13 +09005680 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005681 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005682 std::string str;
5683 GetString(o, str);
5684 model->extensionsRequired.emplace_back(std::move(str));
5685 return true;
5686 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005687 }
5688
Syoyo Fujita83675312017-12-02 21:14:13 +09005689 // 3. Parse Buffer
5690 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005691 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005692 if (!IsObject(o)) {
5693 if (err) {
5694 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005695 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005696 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005697 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005698 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005699 if (!ParseBuffer(&buffer, err, o,
5700 store_original_json_for_extras_and_extensions_, &fs,
5701 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005702 return false;
5703 }
5704
5705 model->buffers.emplace_back(std::move(buffer));
5706 return true;
5707 });
5708
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005709 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005710 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005711 }
5712 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005713 // 4. Parse BufferView
5714 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005715 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005716 if (!IsObject(o)) {
5717 if (err) {
5718 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005719 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005720 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005721 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005722 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005723 if (!ParseBufferView(&bufferView, err, o,
5724 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005725 return false;
5726 }
5727
5728 model->bufferViews.emplace_back(std::move(bufferView));
5729 return true;
5730 });
5731
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005732 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005733 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005734 }
5735 }
5736
Syoyo Fujita83675312017-12-02 21:14:13 +09005737 // 5. Parse Accessor
5738 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005739 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005740 if (!IsObject(o)) {
5741 if (err) {
5742 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005743 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005744 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005745 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005746 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005747 if (!ParseAccessor(&accessor, err, o,
5748 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005749 return false;
5750 }
5751
5752 model->accessors.emplace_back(std::move(accessor));
5753 return true;
5754 });
5755
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005756 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005757 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005758 }
5759 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005760
Syoyo Fujita83675312017-12-02 21:14:13 +09005761 // 6. Parse Mesh
5762 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005763 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005764 if (!IsObject(o)) {
5765 if (err) {
5766 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005767 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005768 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005769 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005770 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005771 if (!ParseMesh(&mesh, model, err, o,
5772 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005773 return false;
5774 }
5775
5776 model->meshes.emplace_back(std::move(mesh));
5777 return true;
5778 });
5779
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005780 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005781 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005782 }
5783 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005784
viperscape9df05802018-12-05 14:11:01 -05005785 // Assign missing bufferView target types
5786 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005787 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005788 for (auto &mesh : model->meshes) {
5789 for (auto &primitive : mesh.primitives) {
5790 if (primitive.indices >
5791 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005792 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005793 if (size_t(primitive.indices) >= model->accessors.size()) {
5794 if (err) {
5795 (*err) += "primitive indices accessor out of bounds";
5796 }
5797 return false;
5798 }
5799
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005800 auto bufferView =
5801 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005802 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5803 if (err) {
5804 (*err) += "accessor[" + std::to_string(primitive.indices) +
5805 "] invalid bufferView";
5806 }
5807 return false;
5808 }
5809
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005810 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005811 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
imallettd9ce9eb2022-10-07 10:37:09 -07005812 // we could optionally check if accessors' bufferView type is Scalar, as
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005813 // it should be
viperscape9df05802018-12-05 14:11:01 -05005814 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005815
5816 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07005817 const auto accessorsIndex = size_t(attribute.second);
5818 if (accessorsIndex < model->accessors.size()) {
5819 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5820 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005821 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005822 model->bufferViews[size_t(bufferView)].target =
5823 TINYGLTF_TARGET_ARRAY_BUFFER;
5824 }
5825 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005826 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005827
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005828 for (auto &target : primitive.targets) {
5829 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07005830 const auto accessorsIndex = size_t(attribute.second);
5831 if (accessorsIndex < model->accessors.size()) {
5832 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5833 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005834 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005835 model->bufferViews[size_t(bufferView)].target =
5836 TINYGLTF_TARGET_ARRAY_BUFFER;
5837 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04005838 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005839 }
5840 }
viperscape9df05802018-12-05 14:11:01 -05005841 }
5842 }
5843
Syoyo Fujita83675312017-12-02 21:14:13 +09005844 // 7. Parse Node
5845 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005846 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005847 if (!IsObject(o)) {
5848 if (err) {
5849 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005850 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005851 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005852 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005853 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005854 if (!ParseNode(&node, err, o,
5855 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005856 return false;
5857 }
5858
5859 model->nodes.emplace_back(std::move(node));
5860 return true;
5861 });
5862
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005863 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005864 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005865 }
5866 }
5867
5868 // 8. Parse scenes.
5869 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005870 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005871 if (!IsObject(o)) {
5872 if (err) {
5873 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005874 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005875 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005876 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005877 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005878 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005879
5880 Scene scene;
5881 scene.nodes = std::move(nodes);
5882
5883 ParseStringProperty(&scene.name, err, o, "name", false);
5884
5885 ParseExtensionsProperty(&scene.extensions, err, o);
5886 ParseExtrasProperty(&scene.extras, o);
5887
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005888 if (store_original_json_for_extras_and_extensions_) {
5889 {
5890 json_const_iterator it;
5891 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005892 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005893 }
5894 }
5895 {
5896 json_const_iterator it;
5897 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005898 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005899 }
5900 }
5901 }
5902
jrkooncecba5d6c2019-08-29 11:26:22 -05005903 model->scenes.emplace_back(std::move(scene));
5904 return true;
5905 });
5906
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005907 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005908 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005909 }
5910 }
5911
5912 // 9. Parse default scenes.
5913 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005914 json_const_iterator rootIt;
5915 int iVal;
5916 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5917 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005918 }
5919 }
5920
5921 // 10. Parse Material
5922 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005923 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005924 if (!IsObject(o)) {
5925 if (err) {
5926 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005927 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005928 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005929 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005930 Material material;
5931 ParseStringProperty(&material.name, err, o, "name", false);
5932
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005933 if (!ParseMaterial(&material, err, o,
5934 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005935 return false;
5936 }
5937
5938 model->materials.emplace_back(std::move(material));
5939 return true;
5940 });
5941
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005942 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005943 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005944 }
5945 }
5946
5947 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005948 void *load_image_user_data{nullptr};
5949
5950 LoadImageDataOption load_image_option;
5951
5952 if (user_image_loader_) {
5953 // Use user supplied pointer
5954 load_image_user_data = load_image_user_data_;
5955 } else {
5956 load_image_option.preserve_channels = preserve_image_channels_;
5957 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5958 }
5959
Syoyo Fujita83675312017-12-02 21:14:13 +09005960 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005961 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005962 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005963 if (!IsObject(o)) {
5964 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005965 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005966 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005967 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005968 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005969 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005970 if (!ParseImage(&image, idx, err, warn, o,
5971 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005972 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005973 return false;
5974 }
5975
5976 if (image.bufferView != -1) {
5977 // Load image from the buffer view.
5978 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5979 if (err) {
5980 std::stringstream ss;
5981 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005982 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005983 (*err) += ss.str();
5984 }
5985 return false;
5986 }
5987
5988 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005989 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005990 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5991 if (err) {
5992 std::stringstream ss;
5993 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
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 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6000
6001 if (*LoadImageData == nullptr) {
6002 if (err) {
6003 (*err) += "No LoadImageData callback specified.\n";
6004 }
6005 return false;
6006 }
6007 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006008 &image, idx, err, warn, image.width, image.height,
6009 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006010 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05006011 if (!ret) {
6012 return false;
6013 }
6014 }
6015
6016 model->images.emplace_back(std::move(image));
6017 ++idx;
6018 return true;
6019 });
6020
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006021 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006022 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006023 }
6024 }
6025
6026 // 12. Parse Texture
6027 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006028 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006029 if (!IsObject(o)) {
6030 if (err) {
6031 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006032 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006033 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006034 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006035 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006036 if (!ParseTexture(&texture, err, o,
6037 store_original_json_for_extras_and_extensions_,
6038 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006039 return false;
6040 }
6041
6042 model->textures.emplace_back(std::move(texture));
6043 return true;
6044 });
6045
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006046 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006047 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006048 }
6049 }
6050
6051 // 13. Parse Animation
6052 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006053 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006054 if (!IsObject(o)) {
6055 if (err) {
6056 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006057 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006058 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006059 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006060 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006061 if (!ParseAnimation(&animation, err, o,
6062 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006063 return false;
6064 }
6065
6066 model->animations.emplace_back(std::move(animation));
6067 return true;
6068 });
6069
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006070 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006071 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006072 }
6073 }
6074
6075 // 14. Parse Skin
6076 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006077 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006078 if (!IsObject(o)) {
6079 if (err) {
6080 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006081 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006082 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006083 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006084 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006085 if (!ParseSkin(&skin, err, o,
6086 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006087 return false;
6088 }
6089
6090 model->skins.emplace_back(std::move(skin));
6091 return true;
6092 });
6093
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006094 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006095 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006096 }
6097 }
6098
6099 // 15. Parse Sampler
6100 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006101 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006102 if (!IsObject(o)) {
6103 if (err) {
6104 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006105 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006106 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006107 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006108 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006109 if (!ParseSampler(&sampler, err, o,
6110 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006111 return false;
6112 }
6113
6114 model->samplers.emplace_back(std::move(sampler));
6115 return true;
6116 });
6117
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006118 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006119 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006120 }
6121 }
6122
6123 // 16. Parse Camera
6124 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006125 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006126 if (!IsObject(o)) {
6127 if (err) {
6128 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006129 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006130 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006131 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006132 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006133 if (!ParseCamera(&camera, err, o,
6134 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006135 return false;
6136 }
6137
6138 model->cameras.emplace_back(std::move(camera));
6139 return true;
6140 });
6141
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006142 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006143 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006144 }
6145 }
6146
6147 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006148 ParseExtensionsProperty(&model->extensions, err, v);
6149
6150 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006151 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006152 json_const_iterator rootIt;
6153 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6154 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006155
jrkooncecba5d6c2019-08-29 11:26:22 -05006156 json_const_iterator it(ObjectBegin(root));
6157 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006158 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006159 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006160 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006161 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006162 const json &object = GetValue(it);
6163 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006164 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006165 const json &lights = GetValue(itLight);
6166 if (!IsArray(lights)) {
6167 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006168 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006169
6170 auto arrayIt(ArrayBegin(lights));
6171 auto arrayItEnd(ArrayEnd(lights));
6172 for (; arrayIt != arrayItEnd; ++arrayIt) {
6173 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006174 if (!ParseLight(&light, err, *arrayIt,
6175 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006176 return false;
6177 }
6178 model->lights.emplace_back(std::move(light));
6179 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006180 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006181 }
6182 }
6183 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006184 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006185
mynzc0d4d1c2018-06-28 23:06:00 +09006186 // 19. Parse Extras
6187 ParseExtrasProperty(&model->extras, v);
6188
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006189 if (store_original_json_for_extras_and_extensions_) {
6190 model->extras_json_string = JsonToString(v["extras"]);
6191 model->extensions_json_string = JsonToString(v["extensions"]);
6192 }
6193
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006194 return true;
6195}
6196
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006197bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006198 std::string *warn, const char *str,
6199 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006200 const std::string &base_dir,
6201 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006202 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006203 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006204 bin_size_ = 0;
6205
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006206 return LoadFromString(model, err, warn, str, length, base_dir,
6207 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006208}
6209
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006210bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006211 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006212 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006213 std::stringstream ss;
6214
Paolo Jovone6601bf2018-07-07 20:43:33 +02006215 if (fs.ReadWholeFile == nullptr) {
6216 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006217 ss << "Failed to read file: " << filename
6218 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006219 if (err) {
6220 (*err) = ss.str();
6221 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006222 return false;
6223 }
6224
Paolo Jovone6601bf2018-07-07 20:43:33 +02006225 std::vector<unsigned char> data;
6226 std::string fileerr;
6227 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006228 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006229 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6230 if (err) {
6231 (*err) = ss.str();
6232 }
6233 return false;
6234 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006235
Paolo Jovone6601bf2018-07-07 20:43:33 +02006236 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006237 if (sz == 0) {
6238 if (err) {
6239 (*err) = "Empty file.";
6240 }
6241 return false;
6242 }
6243
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006244 std::string basedir = GetBaseDir(filename);
6245
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006246 bool ret = LoadASCIIFromString(
6247 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6248 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006249
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006250 return ret;
6251}
6252
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006253bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006254 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006255 const unsigned char *bytes,
6256 unsigned int size,
6257 const std::string &base_dir,
6258 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006259 if (size < 20) {
6260 if (err) {
6261 (*err) = "Too short data size for glTF Binary.";
6262 }
6263 return false;
6264 }
6265
Syoyo Fujitabeded612016-05-01 20:03:43 +09006266 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6267 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006268 // ok
6269 } else {
6270 if (err) {
6271 (*err) = "Invalid magic.";
6272 }
6273 return false;
6274 }
6275
Syoyo Fujitabeded612016-05-01 20:03:43 +09006276 unsigned int version; // 4 bytes
6277 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006278 unsigned int chunk0_length; // 4 bytes
6279 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006280
Syoyo Fujitabeded612016-05-01 20:03:43 +09006281 memcpy(&version, bytes + 4, 4);
6282 swap4(&version);
6283 memcpy(&length, bytes + 8, 4);
6284 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006285 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6286 swap4(&chunk0_length);
6287 memcpy(&chunk0_format, bytes + 16, 4);
6288 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006289
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006290 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6291 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006292 // In case the Bin buffer is not present, the size is exactly 20 + size of
6293 // JSON contents,
6294 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006295 //
6296 // https://github.com/syoyo/tinygltf/issues/372
6297 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006298 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006299
Syoyo Fujitac670f082022-09-17 19:52:25 +09006300 if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006301 // Do not allow 4GB or more GLB data.
6302 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6303 }
6304
Syoyo Fujitac670f082022-09-17 19:52:25 +09006305 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6306 (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006307 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006308 if (err) {
6309 (*err) = "Invalid glTF binary.";
6310 }
6311 return false;
6312 }
6313
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006314 // Padding check
6315 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6316 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006317 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006318 if (err) {
6319 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6320 }
6321 }
6322
Syoyo Fujita612e5782022-09-18 21:01:39 +09006323 //std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6324 //std::cout << "length = " << length << "\n";
6325
Syoyo Fujitac670f082022-09-17 19:52:25 +09006326 // Chunk1(BIN) data
6327 // 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 +09006328 // So when header + JSON data == binary size, Chunk1 is omitted.
6329 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006330
Syoyo Fujitac670f082022-09-17 19:52:25 +09006331 bin_data_ = nullptr;
6332 bin_size_ = 0;
6333 } else {
6334 // Read Chunk1 info(BIN data)
imallettd9ce9eb2022-10-07 10:37:09 -07006335 // 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 +09006336 if ((header_and_json_size + 12ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006337 if (err) {
Syoyo Fujita612e5782022-09-18 21:01:39 +09006338 (*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 +09006339 }
6340 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006341 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006342
Syoyo Fujitac670f082022-09-17 19:52:25 +09006343 unsigned int chunk1_length; // 4 bytes
6344 unsigned int chunk1_format; // 4 bytes;
6345 memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
6346 swap4(&chunk1_length);
6347 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6348 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006349
Syoyo Fujita612e5782022-09-18 21:01:39 +09006350 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6351
Syoyo Fujitac670f082022-09-17 19:52:25 +09006352 if (chunk1_length < 4) {
6353 if (err) {
6354 (*err) = "Insufficient Chunk1(BIN) data size.";
6355 }
6356 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006357 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006358
Syoyo Fujitac670f082022-09-17 19:52:25 +09006359 if ((chunk1_length % 4) != 0) {
6360 if (err) {
6361 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6362 }
6363 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006364 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006365
Syoyo Fujitac670f082022-09-17 19:52:25 +09006366 if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
6367 if (err) {
6368 (*err) = "BIN Chunk data length exceeds the GLB size.";
6369 }
6370 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006371 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006372
Syoyo Fujitac670f082022-09-17 19:52:25 +09006373 if (chunk1_format != 0x004e4942) {
6374 if (err) {
imallettd9ce9eb2022-10-07 10:37:09 -07006375 (*err) = "Invalid type for chunk1 data.";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006376 }
6377 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006378 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006379
Syoyo Fujita612e5782022-09-18 21:01:39 +09006380 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6381
Syoyo Fujitac670f082022-09-17 19:52:25 +09006382 bin_data_ = bytes + header_and_json_size +
6383 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6384
6385 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006386 }
6387
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006388 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006389 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006390 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006391
6392 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006393
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006394 bool ret = LoadFromString(model, err, warn,
6395 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006396 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006397 if (!ret) {
6398 return ret;
6399 }
6400
6401 return true;
6402}
6403
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006404bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006405 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006406 const std::string &filename,
6407 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006408 std::stringstream ss;
6409
Paolo Jovone6601bf2018-07-07 20:43:33 +02006410 if (fs.ReadWholeFile == nullptr) {
6411 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006412 ss << "Failed to read file: " << filename
6413 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006414 if (err) {
6415 (*err) = ss.str();
6416 }
6417 return false;
6418 }
6419
Paolo Jovone6601bf2018-07-07 20:43:33 +02006420 std::vector<unsigned char> data;
6421 std::string fileerr;
6422 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006423 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006424 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6425 if (err) {
6426 (*err) = ss.str();
6427 }
6428 return false;
6429 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006430
Syoyo Fujitabeded612016-05-01 20:03:43 +09006431 std::string basedir = GetBaseDir(filename);
6432
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006433 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6434 static_cast<unsigned int>(data.size()),
6435 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006436
6437 return ret;
6438}
6439
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006440///////////////////////
6441// GLTF Serialization
6442///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006443namespace {
6444json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006445#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006446 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006447#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006448 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006449#endif
jrkoonce63419a12019-09-03 17:06:41 -05006450}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006451
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006452void JsonAssign(json &dest, const json &src) {
6453#ifdef TINYGLTF_USE_RAPIDJSON
6454 dest.CopyFrom(src, GetAllocator());
6455#else
6456 dest = src;
6457#endif
6458}
6459
6460void JsonAddMember(json &o, const char *key, json &&value) {
6461#ifdef TINYGLTF_USE_RAPIDJSON
6462 if (!o.IsObject()) {
6463 o.SetObject();
6464 }
6465 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6466#else
6467 o[key] = std::move(value);
6468#endif
6469}
6470
6471void JsonPushBack(json &o, json &&value) {
6472#ifdef TINYGLTF_USE_RAPIDJSON
6473 o.PushBack(std::move(value), GetAllocator());
6474#else
6475 o.push_back(std::move(value));
6476#endif
6477}
6478
6479bool JsonIsNull(const json &o) {
6480#ifdef TINYGLTF_USE_RAPIDJSON
6481 return o.IsNull();
6482#else
6483 return o.is_null();
6484#endif
6485}
6486
6487void JsonSetObject(json &o) {
6488#ifdef TINYGLTF_USE_RAPIDJSON
6489 o.SetObject();
6490#else
6491 o = o.object({});
6492#endif
6493}
6494
6495void JsonReserveArray(json &o, size_t s) {
6496#ifdef TINYGLTF_USE_RAPIDJSON
6497 o.SetArray();
6498 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6499#endif
6500 (void)(o);
6501 (void)(s);
6502}
6503} // namespace
6504
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006505// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006506
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006507template <typename T>
6508static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006509 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006510 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006511 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006512 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006513 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006514}
6515
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006516#ifdef TINYGLTF_USE_RAPIDJSON
6517template <>
6518void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6519 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6520}
6521#endif
6522
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006523template <typename T>
6524static void SerializeNumberArrayProperty(const std::string &key,
6525 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006526 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006527 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006528
jrkooncecba5d6c2019-08-29 11:26:22 -05006529 json ary;
6530 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006531 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006532 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006533 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006534 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006535}
6536
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006537static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006538 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006539 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006540}
6541
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006542static void SerializeStringArrayProperty(const std::string &key,
6543 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006544 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006545 json ary;
6546 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006547 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006548 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006549 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006550 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006551}
6552
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006553static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006554 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006555#ifdef TINYGLTF_USE_RAPIDJSON
6556 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006557 case REAL_TYPE:
6558 obj.SetDouble(value.Get<double>());
6559 break;
6560 case INT_TYPE:
6561 obj.SetInt(value.Get<int>());
6562 break;
6563 case BOOL_TYPE:
6564 obj.SetBool(value.Get<bool>());
6565 break;
6566 case STRING_TYPE:
6567 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6568 break;
6569 case ARRAY_TYPE: {
6570 obj.SetArray();
6571 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6572 GetAllocator());
6573 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6574 Value elementValue = value.Get(int(i));
6575 json elementJson;
6576 if (ValueToJson(value.Get(int(i)), &elementJson))
6577 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006578 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006579 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006580 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006581 case BINARY_TYPE:
6582 // TODO
6583 // obj = json(value.Get<std::vector<unsigned char>>());
6584 return false;
6585 break;
6586 case OBJECT_TYPE: {
6587 obj.SetObject();
6588 Value::Object objMap = value.Get<Value::Object>();
6589 for (auto &it : objMap) {
6590 json elementJson;
6591 if (ValueToJson(it.second, &elementJson)) {
6592 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6593 std::move(elementJson), GetAllocator());
6594 }
6595 }
6596 break;
6597 }
6598 case NULL_TYPE:
6599 default:
6600 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006601 }
6602#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006603 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006604 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006605 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006606 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006607 case INT_TYPE:
6608 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006609 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006610 case BOOL_TYPE:
6611 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006612 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006613 case STRING_TYPE:
6614 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006615 break;
6616 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006617 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6618 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006619 json elementJson;
6620 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006621 obj.push_back(elementJson);
6622 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006623 break;
6624 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006625 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006626 // TODO
6627 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006628 return false;
6629 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006630 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006631 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006632 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006633 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006634 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006635 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006636 break;
6637 }
6638 case NULL_TYPE:
6639 default:
6640 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006641 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006642#endif
6643 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006644 return true;
6645}
6646
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006647static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006648 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006649 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006650 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006651 JsonAddMember(obj, key.c_str(), std::move(ret));
6652 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006653}
6654
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006655static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006656 json &o) {
6657 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006658 if (data.size() > 0) {
6659 std::string encodedData =
6660 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6661 SerializeStringProperty("uri", header + encodedData, o);
6662 } else {
6663 // Issue #229
imallettd9ce9eb2022-10-07 10:37:09 -07006664 // size 0 is allowed. Just emit mime header.
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006665 SerializeStringProperty("uri", header, o);
6666 }
johan bowald30c53472018-03-30 11:49:36 +02006667}
6668
Selmar Koke4677492018-10-25 16:45:49 +02006669static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006670 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006671#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006672#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006673 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6674 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6675 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6676 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006677 std::ostream output(&wfile_buf);
6678 if (!wfile_buf.is_open()) return false;
6679#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006680 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006681 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006682#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006683 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006684 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006685#endif
6686#else
6687 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6688 if (!output.is_open()) return false;
6689#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006690 if (data.size() > 0) {
6691 output.write(reinterpret_cast<const char *>(&data[0]),
6692 std::streamsize(data.size()));
6693 } else {
6694 // Issue #229
6695 // size 0 will be still valid buffer data.
6696 // write empty file.
6697 }
Selmar Koke4677492018-10-25 16:45:49 +02006698 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006699}
6700
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006701#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006702static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006703 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6704 ++paramIt) {
6705 if (paramIt->second.number_array.size()) {
6706 SerializeNumberArrayProperty<double>(paramIt->first,
6707 paramIt->second.number_array, o);
6708 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006709 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006710 for (std::map<std::string, double>::iterator it =
6711 paramIt->second.json_double_value.begin();
6712 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006713 if (it->first == "index") {
6714 json_double_value[it->first] = paramIt->second.TextureIndex();
6715 } else {
6716 json_double_value[it->first] = it->second;
6717 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006718 }
6719
Syoyo Fujita83675312017-12-02 21:14:13 +09006720 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006721 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006722 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006723 } else if (paramIt->second.has_number_value) {
6724 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006725 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006726 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006727 }
6728 }
6729}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006730#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006731
Selmar Kok81b672b2019-10-18 16:08:44 +02006732static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006733 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006734
6735 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006736 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006737 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006738 // Allow an empty object for extension(#97)
6739 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006740 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006741 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006742 isNull = JsonIsNull(ret);
6743 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006744 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006745 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006746 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6747 // create empty object so that an extension name is still included in
6748 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006749 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006750 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006751 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006752 }
6753 }
Selmar09d2ff12018-03-15 17:30:42 +01006754 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006755 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006756}
6757
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006758static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006759 if (accessor.bufferView >= 0)
6760 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006761
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006762 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006763 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006764
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006765 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6766 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006767
6768 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6769 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6770 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6771 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6772 } else {
6773 // Issue #301. Serialize as integer.
6774 // Assume int value is within [-2**31-1, 2**31-1]
6775 {
6776 std::vector<int> values;
6777 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6778 std::back_inserter(values),
6779 [](double v) { return static_cast<int>(v); });
6780
6781 SerializeNumberArrayProperty<int>("min", values, o);
6782 }
6783
6784 {
6785 std::vector<int> values;
6786 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6787 std::back_inserter(values),
6788 [](double v) { return static_cast<int>(v); });
6789
6790 SerializeNumberArrayProperty<int>("max", values, o);
6791 }
6792 }
6793
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006794 if (accessor.normalized)
6795 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006796 std::string type;
6797 switch (accessor.type) {
6798 case TINYGLTF_TYPE_SCALAR:
6799 type = "SCALAR";
6800 break;
6801 case TINYGLTF_TYPE_VEC2:
6802 type = "VEC2";
6803 break;
6804 case TINYGLTF_TYPE_VEC3:
6805 type = "VEC3";
6806 break;
6807 case TINYGLTF_TYPE_VEC4:
6808 type = "VEC4";
6809 break;
6810 case TINYGLTF_TYPE_MAT2:
6811 type = "MAT2";
6812 break;
6813 case TINYGLTF_TYPE_MAT3:
6814 type = "MAT3";
6815 break;
6816 case TINYGLTF_TYPE_MAT4:
6817 type = "MAT4";
6818 break;
6819 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006820
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006821 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006822 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006823
6824 if (accessor.extras.Type() != NULL_TYPE) {
6825 SerializeValue("extras", accessor.extras, o);
6826 }
feiy0b315432022-08-13 10:08:17 +08006827
6828 // sparse
6829 if (accessor.sparse.isSparse)
6830 {
6831 json sparse;
6832 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
6833 {
6834 json indices;
6835 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
6836 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
6837 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
6838 JsonAddMember(sparse, "indices", std::move(indices));
6839 }
6840 {
6841 json values;
6842 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08006843 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
feiy0b315432022-08-13 10:08:17 +08006844 JsonAddMember(sparse, "values", std::move(values));
6845 }
6846 JsonAddMember(o, "sparse", std::move(sparse));
6847 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006848}
6849
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006850static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006851 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006852 {
6853 json target;
6854 SerializeNumberProperty("node", channel.target_node, target);
6855 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006856
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006857 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006858
jrkooncecba5d6c2019-08-29 11:26:22 -05006859 JsonAddMember(o, "target", std::move(target));
6860 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006861
6862 if (channel.extras.Type() != NULL_TYPE) {
6863 SerializeValue("extras", channel.extras, o);
6864 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006865
Selmar Kok4e2988e2019-08-16 14:08:08 +02006866 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006867}
6868
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006869static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006870 SerializeNumberProperty("input", sampler.input, o);
6871 SerializeNumberProperty("output", sampler.output, o);
6872 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006873
6874 if (sampler.extras.Type() != NULL_TYPE) {
6875 SerializeValue("extras", sampler.extras, o);
6876 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006877}
6878
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006879static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006880 if (!animation.name.empty())
6881 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006882
jrkooncecba5d6c2019-08-29 11:26:22 -05006883 {
6884 json channels;
6885 JsonReserveArray(channels, animation.channels.size());
6886 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6887 json channel;
6888 AnimationChannel gltfChannel = animation.channels[i];
6889 SerializeGltfAnimationChannel(gltfChannel, channel);
6890 JsonPushBack(channels, std::move(channel));
6891 }
6892
6893 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006894 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006895
jrkooncecba5d6c2019-08-29 11:26:22 -05006896 {
6897 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006898 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006899 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6900 json sampler;
6901 AnimationSampler gltfSampler = animation.samplers[i];
6902 SerializeGltfAnimationSampler(gltfSampler, sampler);
6903 JsonPushBack(samplers, std::move(sampler));
6904 }
6905 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006906 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006907
Jens Olssonb3af2f12018-06-04 10:17:49 +02006908 if (animation.extras.Type() != NULL_TYPE) {
6909 SerializeValue("extras", animation.extras, o);
6910 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006911
Selmar Kok4e2988e2019-08-16 14:08:08 +02006912 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006913}
6914
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006915static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006916 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006917 SerializeStringProperty("generator", asset.generator, o);
6918 }
6919
Christophe820ede82019-07-04 15:21:21 +09006920 if (!asset.copyright.empty()) {
6921 SerializeStringProperty("copyright", asset.copyright, o);
6922 }
6923
Syoyo Fujitab702de72021-03-02 19:08:29 +09006924 if (asset.version.empty()) {
6925 // Just in case
6926 // `version` must be defined
6927 asset.version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006928 }
6929
Syoyo Fujitab702de72021-03-02 19:08:29 +09006930 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6931 SerializeStringProperty("version", asset.version, o);
6932
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006933 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006934 SerializeValue("extras", asset.extras, o);
6935 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006936
Selmar09d2ff12018-03-15 17:30:42 +01006937 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006938}
6939
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006940static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6941 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006942 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006943 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006944
6945 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6946
6947 if (buffer.extras.Type() != NULL_TYPE) {
6948 SerializeValue("extras", buffer.extras, o);
6949 }
6950}
6951
johan bowald30c53472018-03-30 11:49:36 +02006952static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6953 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6954 SerializeGltfBufferData(buffer.data, o);
6955
6956 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006957
6958 if (buffer.extras.Type() != NULL_TYPE) {
6959 SerializeValue("extras", buffer.extras, o);
6960 }
johan bowald30c53472018-03-30 11:49:36 +02006961}
6962
Selmar Koke4677492018-10-25 16:45:49 +02006963static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006964 const std::string &binFilename,
6965 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006966 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006967 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006968 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006969
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006970 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006971
6972 if (buffer.extras.Type() != NULL_TYPE) {
6973 SerializeValue("extras", buffer.extras, o);
6974 }
Selmar Koke4677492018-10-25 16:45:49 +02006975 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006976}
6977
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006978static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006979 SerializeNumberProperty("buffer", bufferView.buffer, o);
6980 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006981
Johan Bowaldfaa27222018-03-28 14:44:45 +02006982 // byteStride is optional, minimum allowed is 4
6983 if (bufferView.byteStride >= 4) {
6984 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6985 }
6986 // byteOffset is optional, default is 0
6987 if (bufferView.byteOffset > 0) {
6988 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6989 }
6990 // Target is optional, check if it contains a valid value
6991 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6992 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6993 SerializeNumberProperty("target", bufferView.target, o);
6994 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006995 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006996 SerializeStringProperty("name", bufferView.name, o);
6997 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006998
6999 if (bufferView.extras.Type() != NULL_TYPE) {
7000 SerializeValue("extras", bufferView.extras, o);
7001 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007002}
7003
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007004static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007005 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08007006 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007007 SerializeStringProperty("mimeType", image.mimeType, o);
7008 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7009 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007010 // TODO(syoyo): dlib::urilencode?
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007011 SerializeStringProperty("uri", image.uri, o);
7012 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007013
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007014 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007015 SerializeStringProperty("name", image.name, o);
7016 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007017
7018 if (image.extras.Type() != NULL_TYPE) {
7019 SerializeValue("extras", image.extras, o);
7020 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09007021
7022 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007023}
7024
Syoyo Fujita046400b2019-07-24 19:26:48 +09007025static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
7026 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007027
Syoyo Fujita046400b2019-07-24 19:26:48 +09007028 if (texinfo.texCoord != 0) {
7029 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7030 }
7031
7032 if (texinfo.extras.Type() != NULL_TYPE) {
7033 SerializeValue("extras", texinfo.extras, o);
7034 }
7035
7036 SerializeExtensionMap(texinfo.extensions, o);
7037}
7038
7039static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
7040 json &o) {
7041 SerializeNumberProperty("index", texinfo.index, o);
7042
7043 if (texinfo.texCoord != 0) {
7044 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7045 }
7046
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007047 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007048 SerializeNumberProperty("scale", texinfo.scale, o);
7049 }
7050
7051 if (texinfo.extras.Type() != NULL_TYPE) {
7052 SerializeValue("extras", texinfo.extras, o);
7053 }
7054
7055 SerializeExtensionMap(texinfo.extensions, o);
7056}
7057
7058static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
7059 json &o) {
7060 SerializeNumberProperty("index", texinfo.index, o);
7061
7062 if (texinfo.texCoord != 0) {
7063 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7064 }
7065
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007066 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007067 SerializeNumberProperty("strength", texinfo.strength, o);
7068 }
7069
7070 if (texinfo.extras.Type() != NULL_TYPE) {
7071 SerializeValue("extras", texinfo.extras, o);
7072 }
7073
7074 SerializeExtensionMap(texinfo.extensions, o);
7075}
7076
7077static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
7078 json &o) {
7079 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7080 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7081 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7082 o);
7083 }
7084
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007085 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007086 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7087 }
7088
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007089 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007090 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7091 }
7092
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007093 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007094 json texinfo;
7095 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007096 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007097 }
7098
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007099 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007100 json texinfo;
7101 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007102 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007103 }
7104
7105 SerializeExtensionMap(pbr.extensions, o);
7106
7107 if (pbr.extras.Type() != NULL_TYPE) {
7108 SerializeValue("extras", pbr.extras, o);
7109 }
7110}
7111
7112static void SerializeGltfMaterial(Material &material, json &o) {
7113 if (material.name.size()) {
7114 SerializeStringProperty("name", material.name, o);
7115 }
7116
7117 // QUESTION(syoyo): Write material parameters regardless of its default value?
7118
7119 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7120 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7121 }
7122
Patrick Härtld9a468b2019-08-14 14:14:07 +02007123 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007124 SerializeStringProperty("alphaMode", material.alphaMode, o);
7125 }
7126
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007127 if (material.doubleSided != false)
7128 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007129
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007130 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007131 json texinfo;
7132 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007133 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007134 }
7135
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007136 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007137 json texinfo;
7138 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007139 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007140 }
7141
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007142 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007143 json texinfo;
7144 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007145 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007146 }
7147
7148 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7149 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7150 SerializeNumberArrayProperty<double>("emissiveFactor",
7151 material.emissiveFactor, o);
7152 }
7153
7154 {
7155 json pbrMetallicRoughness;
7156 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7157 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007158 // Issue 204
7159 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7160 // default values(json is null). Otherwise it will serialize to
7161 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
imallettd9ce9eb2022-10-07 10:37:09 -07007162 // importers (and validators).
Syoyo Fujita7e009042019-09-13 15:32:22 +09007163 //
7164 if (!JsonIsNull(pbrMetallicRoughness)) {
7165 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7166 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007167 }
7168
7169#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007170 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007171 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007172 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007173 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007174 }
7175
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007176 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007177#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007178
Syoyo Fujita046400b2019-07-24 19:26:48 +09007179#endif
7180
7181 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007182
7183 if (material.extras.Type() != NULL_TYPE) {
7184 SerializeValue("extras", material.extras, o);
7185 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007186}
7187
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007188static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007189 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007190 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007191 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007192 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007193 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007194 {
7195 json attributes;
7196 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007197 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007198 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7199 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007200
jrkooncecba5d6c2019-08-29 11:26:22 -05007201 JsonAddMember(primitive, "attributes", std::move(attributes));
7202 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007203
imallettd9ce9eb2022-10-07 10:37:09 -07007204 // Indices is optional
Johan Bowaldfaa27222018-03-28 14:44:45 +02007205 if (gltfPrimitive.indices > -1) {
7206 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7207 }
7208 // Material is optional
7209 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007210 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7211 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007212 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007213 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007214
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007215 // Morph targets
7216 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007217 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007218 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007219 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007220 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007221 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7222 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7223 attrIt != targetData.end(); ++attrIt) {
7224 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7225 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007226 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007227 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007228 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007229 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007230 }
7231
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007232 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007233
Jens Olssonb3af2f12018-06-04 10:17:49 +02007234 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7235 SerializeValue("extras", gltfPrimitive.extras, primitive);
7236 }
7237
jrkooncecba5d6c2019-08-29 11:26:22 -05007238 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007239 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007240
jrkooncecba5d6c2019-08-29 11:26:22 -05007241 JsonAddMember(o, "primitives", std::move(primitives));
7242
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007243 if (mesh.weights.size()) {
7244 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7245 }
7246
7247 if (mesh.name.size()) {
7248 SerializeStringProperty("name", mesh.name, o);
7249 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007250
Selmar Kok81b672b2019-10-18 16:08:44 +02007251 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007252 if (mesh.extras.Type() != NULL_TYPE) {
7253 SerializeValue("extras", mesh.extras, o);
7254 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007255}
7256
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007257static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007258 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7259 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7260 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007261 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007262 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007263 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007264}
7265
Syoyo Fujita83675312017-12-02 21:14:13 +09007266static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007267 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007268 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007269 if (light.range > 0.0) {
7270 SerializeNumberProperty("range", light.range, o);
7271 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007272 SerializeNumberArrayProperty("color", light.color, o);
7273 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007274 if (light.type == "spot") {
7275 json spot;
7276 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007277 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007278 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007279 SerializeExtensionMap(light.extensions, o);
7280 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007281 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007282 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007283}
7284
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007285static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007286 if (node.translation.size() > 0) {
7287 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7288 }
7289 if (node.rotation.size() > 0) {
7290 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7291 }
7292 if (node.scale.size() > 0) {
7293 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7294 }
7295 if (node.matrix.size() > 0) {
7296 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7297 }
7298 if (node.mesh != -1) {
7299 SerializeNumberProperty<int>("mesh", node.mesh, o);
7300 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007301
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007302 if (node.skin != -1) {
7303 SerializeNumberProperty<int>("skin", node.skin, o);
7304 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007305
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007306 if (node.camera != -1) {
7307 SerializeNumberProperty<int>("camera", node.camera, o);
7308 }
7309
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007310 if (node.weights.size() > 0) {
7311 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7312 }
7313
Jens Olssona9718662018-05-24 15:48:49 +02007314 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007315 SerializeValue("extras", node.extras, o);
7316 }
7317
Selmar09d2ff12018-03-15 17:30:42 +01007318 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007319 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007320 SerializeNumberArrayProperty<int>("children", node.children, o);
7321}
7322
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007323static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007324 if (sampler.magFilter != -1) {
7325 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7326 }
7327 if (sampler.minFilter != -1) {
7328 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7329 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007330 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007331 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7332 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007333
7334 if (sampler.extras.Type() != NULL_TYPE) {
7335 SerializeValue("extras", sampler.extras, o);
7336 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007337}
7338
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007339static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007340 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007341 SerializeNumberProperty("zfar", camera.zfar, o);
7342 SerializeNumberProperty("znear", camera.znear, o);
7343 SerializeNumberProperty("xmag", camera.xmag, o);
7344 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007345
7346 if (camera.extras.Type() != NULL_TYPE) {
7347 SerializeValue("extras", camera.extras, o);
7348 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007349}
7350
7351static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007352 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007353 SerializeNumberProperty("zfar", camera.zfar, o);
7354 SerializeNumberProperty("znear", camera.znear, o);
7355 if (camera.aspectRatio > 0) {
7356 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7357 }
7358
7359 if (camera.yfov > 0) {
7360 SerializeNumberProperty("yfov", camera.yfov, o);
7361 }
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
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007368static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007369 SerializeStringProperty("type", camera.type, o);
7370 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007371 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007372 }
7373
7374 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007375 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007376 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007377 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007378 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007379 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007380 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007381 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007382 } else {
7383 // ???
7384 }
Syoyofe77cc52020-05-09 02:41:07 +09007385
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007386 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007387 SerializeValue("extras", camera.extras, o);
7388 }
7389 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007390}
7391
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007392static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007393 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7394
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007395 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007396 SerializeStringProperty("name", scene.name, o);
7397 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007398 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007399 SerializeValue("extras", scene.extras, o);
7400 }
7401 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007402}
7403
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007404static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007405 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007406 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007407
7408 if (skin.inverseBindMatrices >= 0) {
7409 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7410 }
7411
7412 if (skin.skeleton >= 0) {
7413 SerializeNumberProperty("skeleton", skin.skeleton, o);
7414 }
7415
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007416 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007417 SerializeStringProperty("name", skin.name, o);
7418 }
7419}
7420
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007421static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007422 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007423 SerializeNumberProperty("sampler", texture.sampler, o);
7424 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007425 if (texture.source > -1) {
7426 SerializeNumberProperty("source", texture.source, o);
7427 }
Christophe820ede82019-07-04 15:21:21 +09007428 if (texture.name.size()) {
7429 SerializeStringProperty("name", texture.name, o);
7430 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007431 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007432 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007433 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007434 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007435}
7436
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007437///
7438/// Serialize all properties except buffers and images.
7439///
7440static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007441 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007442 if (model->accessors.size()) {
7443 json accessors;
7444 JsonReserveArray(accessors, model->accessors.size());
7445 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7446 json accessor;
7447 SerializeGltfAccessor(model->accessors[i], accessor);
7448 JsonPushBack(accessors, std::move(accessor));
7449 }
7450 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007451 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007452
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007453 // ANIMATIONS
7454 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007455 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007456 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007457 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7458 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007459 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007460 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007461 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007462 }
7463 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007464
jrkooncecba5d6c2019-08-29 11:26:22 -05007465 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007466 }
7467
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007468 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007469 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007470 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007471 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007472
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007473 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007474 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007475 json bufferViews;
7476 JsonReserveArray(bufferViews, model->bufferViews.size());
7477 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7478 json bufferView;
7479 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7480 JsonPushBack(bufferViews, std::move(bufferView));
7481 }
7482 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007483 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007484
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007485 // Extensions required
7486 if (model->extensionsRequired.size()) {
7487 SerializeStringArrayProperty("extensionsRequired",
7488 model->extensionsRequired, o);
7489 }
7490
7491 // MATERIALS
7492 if (model->materials.size()) {
7493 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007494 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007495 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7496 json material;
7497 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007498
7499 if (JsonIsNull(material)) {
7500 // Issue 294.
7501 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007502 // so the result may be null(unmodified) when all material parameters
7503 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007504 //
7505 // null is not allowed thus we create an empty JSON object.
7506 JsonSetObject(material);
7507 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007508 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007509 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007510 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007511 }
7512
7513 // MESHES
7514 if (model->meshes.size()) {
7515 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007516 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007517 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7518 json mesh;
7519 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007520 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007521 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007522 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007523 }
7524
7525 // NODES
7526 if (model->nodes.size()) {
7527 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007528 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007529 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7530 json node;
7531 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007532 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007533 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007534 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007535 }
7536
7537 // SCENE
7538 if (model->defaultScene > -1) {
7539 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7540 }
7541
7542 // SCENES
7543 if (model->scenes.size()) {
7544 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007545 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007546 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7547 json currentScene;
7548 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007549 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007550 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007551 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007552 }
7553
7554 // SKINS
7555 if (model->skins.size()) {
7556 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007557 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007558 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7559 json skin;
7560 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007561 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007562 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007563 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007564 }
7565
7566 // TEXTURES
7567 if (model->textures.size()) {
7568 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007569 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007570 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7571 json texture;
7572 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007573 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007574 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007575 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007576 }
7577
7578 // SAMPLERS
7579 if (model->samplers.size()) {
7580 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007581 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007582 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7583 json sampler;
7584 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007585 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007586 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007587 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007588 }
7589
7590 // CAMERAS
7591 if (model->cameras.size()) {
7592 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007593 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007594 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7595 json camera;
7596 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007597 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007598 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007599 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007600 }
7601
7602 // EXTENSIONS
7603 SerializeExtensionMap(model->extensions, o);
7604
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007605 auto extensionsUsed = model->extensionsUsed;
7606
7607 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007608 if (model->lights.size()) {
7609 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007610 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007611 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7612 json light;
7613 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007614 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007615 }
7616 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007617 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007618 json ext_j;
7619
jrkooncecba5d6c2019-08-29 11:26:22 -05007620 {
7621 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007622 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007623 JsonAssign(ext_j, GetValue(it));
7624 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007625 }
7626
jrkooncecba5d6c2019-08-29 11:26:22 -05007627 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007628
jrkooncecba5d6c2019-08-29 11:26:22 -05007629 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007630
7631 // Also add "KHR_lights_punctual" to `extensionsUsed`
7632 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007633 auto has_khr_lights_punctual =
7634 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7635 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007636 return (s.compare("KHR_lights_punctual") == 0);
7637 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007638
7639 if (has_khr_lights_punctual == extensionsUsed.end()) {
7640 extensionsUsed.push_back("KHR_lights_punctual");
7641 }
7642 }
7643 }
7644
7645 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007646 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007647 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007648 }
7649
7650 // EXTRAS
7651 if (model->extras.Type() != NULL_TYPE) {
7652 SerializeValue("extras", model->extras, o);
7653 }
7654}
7655
Johan Bowald52936a02019-07-17 09:06:45 +02007656static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007657 stream << content << std::endl;
7658 return true;
7659}
7660
7661static bool WriteGltfFile(const std::string &output,
7662 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007663#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007664#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007665 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007666#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007667 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7668 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7669 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7670 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007671 std::ostream gltfFile(&wfile_buf);
7672 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007673#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007674 std::ofstream gltfFile(output.c_str());
7675 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007676#endif
7677#else
7678 std::ofstream gltfFile(output.c_str());
7679 if (!gltfFile.is_open()) return false;
7680#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007681 return WriteGltfStream(gltfFile, content);
7682}
7683
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007684static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007685 const std::string &content,
7686 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007687 const std::string header = "glTF";
7688 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007689
Alexander Wood190382a2021-10-08 12:19:13 -04007690 const uint32_t content_size = uint32_t(content.size());
7691 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7692 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007693 const uint32_t content_padding_size =
7694 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7695 const uint32_t bin_padding_size =
7696 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007697
7698 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007699 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007700 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007701 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007702 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007703
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007704 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007705 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7706 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7707
7708 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007709 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007710 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007711 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007712 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007713 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007714 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007715 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007716
7717 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007718 if (content_padding_size > 0) {
7719 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007720 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007721 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007722 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007723 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007724 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7725 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007726 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007727 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007728 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007729 sizeof(bin_format));
7730 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7731 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007732 // Chunksize must be multiplies of 4, so pad with zeroes
7733 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007734 const std::vector<unsigned char> padding =
7735 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7736 stream.write(reinterpret_cast<const char *>(padding.data()),
7737 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007738 }
7739 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007740
7741 // TODO: Check error on stream.write
7742 return true;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007743}
7744
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007745static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007746 const std::string &content,
7747 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007748#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007749#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007750 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007751#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007752 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7753 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7754 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7755 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007756 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007757#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007758 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007759#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007760#else
7761 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007762#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007763 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007764}
7765
7766bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7767 bool prettyPrint = true,
7768 bool writeBinary = false) {
7769 JsonDocument output;
7770
7771 /// Serialize all properties except buffers and images.
7772 SerializeGltfModel(model, output);
7773
7774 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007775 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007776 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007777 json buffers;
7778 JsonReserveArray(buffers, model->buffers.size());
7779 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7780 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007781 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7782 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007783 } else {
7784 SerializeGltfBuffer(model->buffers[i], buffer);
7785 }
7786 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007787 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007788 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007789 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007790
7791 // IMAGES
7792 if (model->images.size()) {
7793 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007794 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007795 for (unsigned int i = 0; i < model->images.size(); ++i) {
7796 json image;
7797
7798 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007799 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7800 // enabled, since we won't write separate images when writing to a stream
7801 // we
Hendrik Schwanekamp41e11022022-06-29 12:22:40 +02007802 UpdateImageObject(model->images[i], dummystring, int(i), true,
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007803 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007804 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007805 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007806 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007807 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007808 }
7809
7810 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007811 return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007812 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007813 return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007814 }
7815
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007816}
7817
7818bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7819 bool embedImages = false,
7820 bool embedBuffers = false,
7821 bool prettyPrint = true,
7822 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007823 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007824 std::string defaultBinFilename = GetBaseFilename(filename);
7825 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007826 std::string::size_type pos =
7827 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007828
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007829 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007830 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007831 }
johan bowald642a3432018-04-01 12:37:18 +02007832 std::string baseDir = GetBaseDir(filename);
7833 if (baseDir.empty()) {
7834 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007835 }
Johan Bowald52936a02019-07-17 09:06:45 +02007836 /// Serialize all properties except buffers and images.
7837 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007838
Selmar Kok7cb31e42018-10-05 16:02:29 +02007839 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007840 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007841 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007842 if (model->buffers.size()) {
7843 json buffers;
7844 JsonReserveArray(buffers, model->buffers.size());
7845 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7846 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007847 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7848 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007849 } else if (embedBuffers) {
7850 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007851 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007852 std::string binSavePath;
7853 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007854 if (!model->buffers[i].uri.empty() &&
7855 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007856 binUri = model->buffers[i].uri;
7857 } else {
7858 binUri = defaultBinFilename + defaultBinFileExt;
7859 bool inUse = true;
7860 int numUsed = 0;
7861 while (inUse) {
7862 inUse = false;
7863 for (const std::string &usedName : usedUris) {
7864 if (binUri.compare(usedName) != 0) continue;
7865 inUse = true;
7866 binUri = defaultBinFilename + std::to_string(numUsed++) +
7867 defaultBinFileExt;
7868 break;
7869 }
Selmar Kokc884e582018-10-05 16:25:54 +02007870 }
7871 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007872 usedUris.push_back(binUri);
7873 binSavePath = JoinPath(baseDir, binUri);
7874 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7875 binUri)) {
7876 return false;
7877 }
Selmar Kokc884e582018-10-05 16:25:54 +02007878 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007879 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007880 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007881 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007882 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007883
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007884 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007885 if (model->images.size()) {
7886 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007887 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007888 for (unsigned int i = 0; i < model->images.size(); ++i) {
7889 json image;
johan bowald642a3432018-04-01 12:37:18 +02007890
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007891 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007892 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007893 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007894 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007895 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007896 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007897 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007898
David Harmonda9eac22018-08-30 08:06:05 -04007899 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007900 return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007901 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007902 return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007903 }
7904
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007905}
7906
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007907} // namespace tinygltf
7908
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007909#ifdef __clang__
7910#pragma clang diagnostic pop
7911#endif
7912
Syoyo Fujita612e5782022-09-18 21:01:39 +09007913#endif // TINYGLTF_IMPLEMENTATION