blob: 9768fce88004177766ca8bdcea38230baa20bd74 [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 Fujitaca56f722019-03-07 21:04:25 +09007// Copyright (c) 2015 - 2019 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 Fujita6e08b172019-10-30 17:25:38 +090029// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
30// `extras` property.
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +090031// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
Syoyo Fujitaee179b22019-08-16 13:11:30 +090032// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
Syoyo Fujita046400b2019-07-24 19:26:48 +090033// - v2.3.0 Modified Material representation according to glTF 2.0 schema
34// (and introduced TextureInfo class)
Syoyo Fujita150f2432019-07-25 19:22:44 +090035// Change the behavior of `Value::IsNumber`. It return true either the
36// value is int or real.
Syoyo Fujitaca56f722019-03-07 21:04:25 +090037// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
38// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090039// - v2.1.0 Add draco compression.
Syoyo Fujita0820d832018-10-04 15:45:13 +090040// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090041// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090042//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090043// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090044//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090045// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090046// - base64: base64 decode/encode library.
47// - stb_image: Image loading library.
48//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090049#ifndef TINY_GLTF_H_
50#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090051
Syoyo Fujitad42767e2018-03-15 21:52:00 -050052#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090053#include <cassert>
Syoyo Fujitad42767e2018-03-15 21:52:00 -050054#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020055#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090056#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090057#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090058#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090059#include <string>
60#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090061
jrkoonce51453942019-09-03 09:48:30 -050062#ifndef TINYGLTF_USE_CPP14
63#include <functional>
64#endif
65
Sascha Willems5f9cb242018-12-28 20:53:41 +010066#ifdef __ANDROID__
67#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
68#include <android/asset_manager.h>
69#endif
70#endif
71
Selmar Kok79e3df22019-10-29 16:22:07 +010072#ifdef __GNUC__
73#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010074#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010075#else
76#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010077#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010078#else
79#define TINYGLTF_NOEXCEPT noexcept
80#endif
81
Syoyo Fujita6e08b172019-10-30 17:25:38 +090082#define DEFAULT_METHODS(x) \
83 ~x() = default; \
84 x(const x &) = default; \
85 x(x &&) TINYGLTF_NOEXCEPT = default; \
86 x &operator=(const x &) = default; \
87 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +010088
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090089namespace tinygltf {
90
91#define TINYGLTF_MODE_POINTS (0)
92#define TINYGLTF_MODE_LINE (1)
93#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +010094#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090095#define TINYGLTF_MODE_TRIANGLES (4)
96#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
97#define TINYGLTF_MODE_TRIANGLE_FAN (6)
98
99#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
100#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
101#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
102#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
103#define TINYGLTF_COMPONENT_TYPE_INT (5124)
104#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
105#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita476a8b22018-01-21 12:19:01 +0900106#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900107
Syoyo Fujitac2615632016-06-19 21:56:06 +0900108#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
109#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
110#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
111#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
112#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
113#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
114
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100115#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900116#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400117#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900118
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400119// Redeclarations of the above for technique.parameters.
120#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
121#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
122#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
123#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
124#define TINYGLTF_PARAMETER_TYPE_INT (5124)
125#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
126#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
127
128#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
129#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
130#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
131
132#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
133#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
134#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
135
136#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
137#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
138#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
139#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
140
141#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
142#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
143#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
144
145#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
146
147// End parameter types
148
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900149#define TINYGLTF_TYPE_VEC2 (2)
150#define TINYGLTF_TYPE_VEC3 (3)
151#define TINYGLTF_TYPE_VEC4 (4)
152#define TINYGLTF_TYPE_MAT2 (32 + 2)
153#define TINYGLTF_TYPE_MAT3 (32 + 3)
154#define TINYGLTF_TYPE_MAT4 (32 + 4)
155#define TINYGLTF_TYPE_SCALAR (64 + 1)
156#define TINYGLTF_TYPE_VECTOR (64 + 4)
157#define TINYGLTF_TYPE_MATRIX (64 + 16)
158
159#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
160#define TINYGLTF_IMAGE_FORMAT_PNG (1)
161#define TINYGLTF_IMAGE_FORMAT_BMP (2)
162#define TINYGLTF_IMAGE_FORMAT_GIF (3)
163
Luke San Antonio6d616f52016-06-23 14:09:23 -0400164#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
165#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900166#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400167#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
168#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
169
Syoyo Fujitabde70212016-02-07 17:38:17 +0900170#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
171#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
172
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900173#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
174#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
175
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400176#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
177#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
178
Selmar Kok31cb7f92018-10-03 15:39:05 +0200179#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900180#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200181
Sascha Willems5f9cb242018-12-28 20:53:41 +0100182#ifdef __ANDROID__
183#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000184AAssetManager *asset_manager = nullptr;
Sascha Willems5f9cb242018-12-28 20:53:41 +0100185#endif
186#endif
187
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900188typedef enum {
189 NULL_TYPE = 0,
Syoyo Fujita150f2432019-07-25 19:22:44 +0900190 REAL_TYPE = 1,
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900191 INT_TYPE = 2,
192 BOOL_TYPE = 3,
193 STRING_TYPE = 4,
194 ARRAY_TYPE = 5,
195 BINARY_TYPE = 6,
196 OBJECT_TYPE = 7
197} Type;
198
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500199static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900200 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
201 return 1;
202 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
203 return 1;
204 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
205 return 2;
206 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
207 return 2;
208 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
209 return 4;
210 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
211 return 4;
212 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
213 return 4;
214 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
215 return 8;
216 } else {
217 // Unknown componenty type
218 return -1;
219 }
220}
221
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400222static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900223 if (ty == TINYGLTF_TYPE_SCALAR) {
224 return 1;
225 } else if (ty == TINYGLTF_TYPE_VEC2) {
226 return 2;
227 } else if (ty == TINYGLTF_TYPE_VEC3) {
228 return 3;
229 } else if (ty == TINYGLTF_TYPE_VEC4) {
230 return 4;
231 } else if (ty == TINYGLTF_TYPE_MAT2) {
232 return 4;
233 } else if (ty == TINYGLTF_TYPE_MAT3) {
234 return 9;
235 } else if (ty == TINYGLTF_TYPE_MAT4) {
236 return 16;
237 } else {
238 // Unknown componenty type
239 return -1;
240 }
241}
242
Syoyo Fujita150f2432019-07-25 19:22:44 +0900243// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200244bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900245bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
246 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200247
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900248#ifdef __clang__
249#pragma clang diagnostic push
250// Suppress warning for : static Value null_value
251// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
252#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900253#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900254#endif
255
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900256// Simple class to represent JSON object
257class Value {
258 public:
259 typedef std::vector<Value> Array;
260 typedef std::map<std::string, Value> Object;
261
Syoyo Fujita046400b2019-07-24 19:26:48 +0900262 Value()
263 : type_(NULL_TYPE),
264 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900265 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900266 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900267
268 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900269 explicit Value(int i) : type_(INT_TYPE) {
270 int_value_ = i;
271 real_value_ = i;
272 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900273 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900274 explicit Value(const std::string &s) : type_(STRING_TYPE) {
275 string_value_ = s;
276 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900277 explicit Value(std::string &&s)
278 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900279 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900280 binary_value_.resize(n);
281 memcpy(binary_value_.data(), p, n);
282 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900283 explicit Value(std::vector<unsigned char> &&v) noexcept
284 : type_(BINARY_TYPE),
285 binary_value_(std::move(v)) {}
286 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
287 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
288 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500289
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900290 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
291 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
292 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100293
294 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900295
296 char Type() const { return static_cast<const char>(type_); }
297
298 bool IsBool() const { return (type_ == BOOL_TYPE); }
299
300 bool IsInt() const { return (type_ == INT_TYPE); }
301
Syoyo Fujita150f2432019-07-25 19:22:44 +0900302 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900303
Syoyo Fujita150f2432019-07-25 19:22:44 +0900304 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900305
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900306 bool IsString() const { return (type_ == STRING_TYPE); }
307
308 bool IsBinary() const { return (type_ == BINARY_TYPE); }
309
310 bool IsArray() const { return (type_ == ARRAY_TYPE); }
311
312 bool IsObject() const { return (type_ == OBJECT_TYPE); }
313
Syoyo Fujita150f2432019-07-25 19:22:44 +0900314 // Use this function if you want to have number value as double.
315 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900316 if (type_ == INT_TYPE) {
317 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900318 } else {
319 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900320 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900321 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900322
Syoyo Fujita150f2432019-07-25 19:22:44 +0900323 // Use this function if you want to have number value as int.
324 double GetNumberAsInt() const {
325 if (type_ == REAL_TYPE) {
326 return int(real_value_);
327 } else {
328 return int_value_;
329 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900330 }
331
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900332 // Accessor
333 template <typename T>
334 const T &Get() const;
335 template <typename T>
336 T &Get();
337
338 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900339 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900340 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900341 assert(IsArray());
342 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900343 return (static_cast<size_t>(idx) < array_value_.size())
344 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900345 : null_value;
346 }
347
348 // Lookup value from a key-value pair
349 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900350 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900351 assert(IsObject());
352 Object::const_iterator it = object_value_.find(key);
353 return (it != object_value_.end()) ? it->second : null_value;
354 }
355
356 size_t ArrayLen() const {
357 if (!IsArray()) return 0;
358 return array_value_.size();
359 }
360
361 // Valid only for object type.
362 bool Has(const std::string &key) const {
363 if (!IsObject()) return false;
364 Object::const_iterator it = object_value_.find(key);
365 return (it != object_value_.end()) ? true : false;
366 }
367
368 // List keys
369 std::vector<std::string> Keys() const {
370 std::vector<std::string> keys;
371 if (!IsObject()) return keys; // empty
372
373 for (Object::const_iterator it = object_value_.begin();
374 it != object_value_.end(); ++it) {
375 keys.push_back(it->first);
376 }
377
378 return keys;
379 }
380
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900381 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900382
383 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000384
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900385 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900386 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900387
Syoyo Fujita046400b2019-07-24 19:26:48 +0900388 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900389 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900390 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900391 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900392 Array array_value_;
393 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900394 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900395};
396
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900397#ifdef __clang__
398#pragma clang diagnostic pop
399#endif
400
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900401#define TINYGLTF_VALUE_GET(ctype, var) \
402 template <> \
403 inline const ctype &Value::Get<ctype>() const { \
404 return var; \
405 } \
406 template <> \
407 inline ctype &Value::Get<ctype>() { \
408 return var; \
409 }
410TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900411TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900412TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900413TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900414TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900415TINYGLTF_VALUE_GET(Value::Array, array_value_)
416TINYGLTF_VALUE_GET(Value::Object, object_value_)
417#undef TINYGLTF_VALUE_GET
418
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900419#ifdef __clang__
420#pragma clang diagnostic push
421#pragma clang diagnostic ignored "-Wc++98-compat"
422#pragma clang diagnostic ignored "-Wpadded"
423#endif
424
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500425/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100426using ColorValue = std::array<double, 4>;
427
Syoyo Fujita046400b2019-07-24 19:26:48 +0900428// === legacy interface ====
429// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500430struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200431 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700432 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900433 std::string string_value;
434 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000435 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200436 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900437
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500438 // context sensitive methods. depending the type of the Parameter you are
439 // accessing, these are either valid or not
440 // If this parameter represent a texture map in a material, will return the
441 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100442
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500443 /// Return the index of a texture if this Parameter is a texture map.
444 /// Returned value is only valid if the parameter represent a texture from a
445 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100446 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100447 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500448 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100449 return int(it->second);
450 }
451 return -1;
452 }
453
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000454 /// Return the index of a texture coordinate set if this Parameter is a
455 /// texture map. Returned value is only valid if the parameter represent a
456 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100457 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000458 const auto it = json_double_value.find("texCoord");
459 if (it != std::end(json_double_value)) {
460 return int(it->second);
461 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200462 // As per the spec, if texCoord is ommited, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000463 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100464 }
465
Christophe820ede82019-07-04 15:21:21 +0900466 /// Return the scale of a texture if this Parameter is a normal texture map.
467 /// Returned value is only valid if the parameter represent a normal texture
468 /// from a material
469 double TextureScale() const {
470 const auto it = json_double_value.find("scale");
471 if (it != std::end(json_double_value)) {
472 return it->second;
473 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200474 // As per the spec, if scale is ommited, this paramter is 1
475 return 1;
476 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200477
Arthur Brainville8a98d982019-07-05 00:26:02 +0200478 /// Return the strength of a texture if this Parameter is a an occlusion map.
479 /// Returned value is only valid if the parameter represent an occlusion map
480 /// from a material
481 double TextureStrength() const {
482 const auto it = json_double_value.find("strength");
483 if (it != std::end(json_double_value)) {
484 return it->second;
485 }
486 // As per the spec, if strenghth is ommited, this parameter is 1
487 return 1;
Christophe820ede82019-07-04 15:21:21 +0900488 }
489
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500490 /// Material factor, like the roughness or metalness of a material
491 /// Returned value is only valid if the parameter represent a texture from a
492 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700493 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100494
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500495 /// Return the color of a material
496 /// Returned value is only valid if the parameter represent a texture from a
497 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100498 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100499 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500500 {// this agregate intialize the std::array object, and uses C++11 RVO.
501 number_array[0], number_array[1], number_array[2],
502 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100503 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200504
Selmar Kokff2b1f92019-10-21 17:58:09 +0200505 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100506 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900507 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100508};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900509
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900510#ifdef __clang__
511#pragma clang diagnostic pop
512#endif
513
514#ifdef __clang__
515#pragma clang diagnostic push
516#pragma clang diagnostic ignored "-Wpadded"
517#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900518
Syoyo Fujitabde70212016-02-07 17:38:17 +0900519typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200520typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900521
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000522struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900523 int sampler; // required
524 int target_node; // required (index of the node to target)
525 std::string target_path; // required in ["translation", "rotation", "scale",
526 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900527 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200528 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100529 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900530
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900531 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
532 std::string extras_json_string;
533 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100534 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900535
Syoyo Fujita5b407452017-06-04 17:42:41 +0900536 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100537 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900538 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000539};
540
541struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900542 int input; // required
543 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200544 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
545 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200546 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900547 ExtensionMap extensions;
548
549 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
550 std::string extras_json_string;
551 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000552
Syoyo Fujita5b407452017-06-04 17:42:41 +0900553 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100554 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900555 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000556};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900557
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900558struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900559 std::string name;
560 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000561 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900562 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200563 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200564
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900565 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
566 std::string extras_json_string;
567 std::string extensions_json_string;
568
Selmar Kokff2b1f92019-10-21 17:58:09 +0200569 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100570 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900571 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900572};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900573
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000574struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900575 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900576 int inverseBindMatrices; // required here but not in the spec
577 int skeleton; // The index of the node used as a skeleton root
578 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000579
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900580 Value extras;
581 ExtensionMap extensions;
582
583 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
584 std::string extras_json_string;
585 std::string extensions_json_string;
586
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900587 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000588 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000589 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000590 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100591 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900592 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000593};
594
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000595struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900596 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900597 // glTF 2.0 spec does not define default value for `minFilter` and
598 // `magFilter`. Set -1 in TinyGLTF(issue #186)
599 int minFilter =
600 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
601 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_NEAREST",
602 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
603 int magFilter =
604 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
605 int wrapS =
606 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
607 // "REPEAT"], default "REPEAT"
608 int wrapT =
609 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
610 // "REPEAT"], default "REPEAT"
611 int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900612
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900613 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900614 ExtensionMap extensions;
615
616 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
617 std::string extras_json_string;
618 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900619
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000620 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900621 : minFilter(-1),
622 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800623 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Selmar Kok5892d3e2018-12-04 19:55:56 +0100624 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000625 wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100626 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900627 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000628};
629
Syoyo Fujita5b407452017-06-04 17:42:41 +0900630struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900631 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900632 int width;
633 int height;
634 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000635 int bits; // bit depth per channel. 8(byte), 16 or 32.
636 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
637 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900638 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900639 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500640 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
641 // "image/bmp", "image/gif"]
Squareys188965b2018-03-13 22:20:01 +0100642 std::string uri; // (required if no mimeType)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900643 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900644 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900645
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900646 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
647 std::string extras_json_string;
648 std::string extensions_json_string;
649
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900650 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
651 // compressed for "image/jpeg" mime) This feature is good if you use custom
652 // image loader function. (e.g. delayed decoding of images for faster glTF
653 // parsing) Default parser for Image does not provide as-is loading feature at
654 // the moment. (You can manipulate this by providing your own LoadImageData
655 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200656 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900657
658 Image() : as_is(false) {
659 bufferView = -1;
660 width = -1;
661 height = -1;
662 component = -1;
663 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100664 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500665
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900666 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000667};
668
669struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200670 std::string name;
671
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000672 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200673 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900674 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200675 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900676
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900677 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
678 std::string extras_json_string;
679 std::string extensions_json_string;
680
Syoyo Fujita5b407452017-06-04 17:42:41 +0900681 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100682 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500683
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900684 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000685};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900686
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900687struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900688 int index = -1; // required.
689 int texCoord; // The set index of texture's TEXCOORD attribute used for
690 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900691
692 Value extras;
693 ExtensionMap extensions;
694
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900695 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
696 std::string extras_json_string;
697 std::string extensions_json_string;
698
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900699 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100700 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900701 bool operator==(const TextureInfo &) const;
702};
703
704struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900705 int index = -1; // required
706 int texCoord; // The set index of texture's TEXCOORD attribute used for
707 // texture coordinate mapping.
708 double scale; // scaledNormal = normalize((<sampled normal texture value>
709 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900710
711 Value extras;
712 ExtensionMap extensions;
713
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900714 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
715 std::string extras_json_string;
716 std::string extensions_json_string;
717
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900718 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100719 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900720 bool operator==(const NormalTextureInfo &) const;
721};
722
723struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900724 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900725 int texCoord; // The set index of texture's TEXCOORD attribute used for
726 // texture coordinate mapping.
727 double strength; // occludedColor = lerp(color, color * <sampled occlusion
728 // texture value>, <occlusion strength>)
729
730 Value extras;
731 ExtensionMap extensions;
732
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900733 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
734 std::string extras_json_string;
735 std::string extensions_json_string;
736
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900737 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100738 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900739 bool operator==(const OcclusionTextureInfo &) const;
740};
741
742// pbrMetallicRoughness class defined in glTF 2.0 spec.
743struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900744 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900745 TextureInfo baseColorTexture;
746 double metallicFactor; // default 1
747 double roughnessFactor; // default 1
748 TextureInfo metallicRoughnessTexture;
749
750 Value extras;
751 ExtensionMap extensions;
752
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900753 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
754 std::string extras_json_string;
755 std::string extensions_json_string;
756
757 PbrMetallicRoughness()
758 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
759 metallicFactor(1.0),
760 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100761 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900762 bool operator==(const PbrMetallicRoughness &) const;
763};
764
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000765// Each extension should be stored in a ParameterMap.
766// members not in the values could be included in the ParameterMap
767// to keep a single material model
768struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900769 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900770
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900771 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900772 std::string alphaMode; // default "OPAQUE"
773 double alphaCutoff; // default 0.5
774 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900775
776 PbrMetallicRoughness pbrMetallicRoughness;
777
778 NormalTextureInfo normalTexture;
779 OcclusionTextureInfo occlusionTexture;
780 TextureInfo emissiveTexture;
781
Syoyo Fujita046400b2019-07-24 19:26:48 +0900782 // For backward compatibility
783 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
784 ParameterMap values;
785 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100786
787 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900788 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200789
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900790 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
791 std::string extras_json_string;
792 std::string extensions_json_string;
793
Syoyo Fujita046400b2019-07-24 19:26:48 +0900794 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100795 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900796
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900797 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000798};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900799
Syoyo Fujita5b407452017-06-04 17:42:41 +0900800struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900801 std::string name;
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900802 int buffer{-1}; // Required
803 size_t byteOffset{0}; // minimum 0, default 0
804 size_t byteLength{0}; // required, minimum 1. 0 = invalid
805 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900806 // understood to be tightly packed
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900807 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices or atttribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900808 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900809 ExtensionMap extensions;
810
811 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
812 std::string extras_json_string;
813 std::string extensions_json_string;
814
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900815 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900816
Syoyo Fujita72f4a552020-01-08 00:40:41 +0900817 BufferView() : buffer(-1), byteOffset(0), byteLength(0), byteStride(0), target(0), dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100818 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900819 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000820};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900821
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000822struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900823 int bufferView; // optional in spec but required here since sparse accessor
824 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900825 std::string name;
826 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100827 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000828 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900829 size_t count; // required
830 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900831 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900832 ExtensionMap extensions;
833
834 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
835 std::string extras_json_string;
836 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000837
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900838 std::vector<double> minValues; // optional
839 std::vector<double> maxValues; // optional
840
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100841 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000842 int count;
843 bool isSparse;
844 struct {
845 int byteOffset;
846 int bufferView;
847 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
848 } indices;
849 struct {
850 int bufferView;
851 int byteOffset;
852 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100853 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000854
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900855 ///
856 /// Utility function to compute byteStride for a given bufferView object.
857 /// Returns -1 upon invalid glTF value or parameter configuration.
858 ///
859 int ByteStride(const BufferView &bufferViewObject) const {
860 if (bufferViewObject.byteStride == 0) {
861 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500862 int componentSizeInBytes =
863 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900864 if (componentSizeInBytes <= 0) {
865 return -1;
866 }
867
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900868 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
869 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900870 return -1;
871 }
872
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900873 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900874 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500875 // Check if byteStride is a mulple of the size of the accessor's component
876 // type.
877 int componentSizeInBytes =
878 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900879 if (componentSizeInBytes <= 0) {
880 return -1;
881 }
882
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900883 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900884 return -1;
885 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100886 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900887 }
888
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900889 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900890 }
891
Eero Pajarreae936492019-11-18 12:59:05 +0200892 Accessor() :
893 bufferView(-1),
894 byteOffset(0),
895 normalized(false),
896 componentType(-1),
897 count(0),
898 type(-1){
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000899 sparse.isSparse = false;
900 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100901 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900902 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000903};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900904
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900905struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200906 double aspectRatio; // min > 0
907 double yfov; // required. min > 0
908 double zfar; // min > 0
909 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900910
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900911 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900912 : aspectRatio(0.0),
913 yfov(0.0),
914 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900915 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900916 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100917 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900918 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900919
Selmar09d2ff12018-03-15 17:30:42 +0100920 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900921 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900922
923 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
924 std::string extras_json_string;
925 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900926};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000927
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900928struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200929 double xmag; // required. must not be zero.
930 double ymag; // required. must not be zero.
931 double zfar; // required. `zfar` must be greater than `znear`.
932 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000933
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900934 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100935 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900936 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900937
Selmar09d2ff12018-03-15 17:30:42 +0100938 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900939 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900940
941 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
942 std::string extras_json_string;
943 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900944};
945
946struct Camera {
947 std::string type; // required. "perspective" or "orthographic"
948 std::string name;
949
950 PerspectiveCamera perspective;
951 OrthographicCamera orthographic;
952
953 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100954 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900955 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900956
Selmar09d2ff12018-03-15 17:30:42 +0100957 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000958 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900959
960 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
961 std::string extras_json_string;
962 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900963};
964
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000965struct Primitive {
966 std::map<std::string, int> attributes; // (required) A dictionary object of
967 // integer, where each integer
968 // is the index of the accessor
969 // containing an attribute.
970 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900971 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000972 int indices; // The index of the accessor that contains the indices.
973 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900974 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +0900975 // where each target is a dict with attribues in ["POSITION, "NORMAL",
976 // "TANGENT"] pointing
977 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -0500978 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000979 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900980
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900981 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
982 std::string extras_json_string;
983 std::string extensions_json_string;
984
Syoyo Fujita5b407452017-06-04 17:42:41 +0900985 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000986 material = -1;
987 indices = -1;
988 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100989 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900990 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000991};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900992
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900993struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900994 std::string name;
995 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900996 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +0100997 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900998 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200999
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001000 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1001 std::string extras_json_string;
1002 std::string extensions_json_string;
1003
jrkoonced1e14722019-08-27 11:51:02 -05001004 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001005 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001006 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001007};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001008
1009class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001010 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001011 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001012
Selmar Kokb74fade2019-10-29 16:09:32 +01001013 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001014
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001015 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001016
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001017 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001018
1019 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001020 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001021 int mesh;
1022 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001023 std::vector<double> rotation; // length must be 0 or 4
1024 std::vector<double> scale; // length must be 0 or 3
1025 std::vector<double> translation; // length must be 0 or 3
1026 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001027 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001028
Selmar09d2ff12018-03-15 17:30:42 +01001029 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001030 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001031
1032 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1033 std::string extras_json_string;
1034 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001035};
1036
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001037struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001038 std::string name;
1039 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001040 std::string
1041 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001042 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001043 ExtensionMap extensions;
1044
1045 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1046 std::string extras_json_string;
1047 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001048
Selmar Kokb74fade2019-10-29 16:09:32 +01001049 Buffer() = default;
1050 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001051 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001052};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001053
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001054struct Asset {
Syoyo Fujita5b407452017-06-04 17:42:41 +09001055 std::string version; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001056 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001057 std::string minVersion;
1058 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001059 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001060 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001061
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001062 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1063 std::string extras_json_string;
1064 std::string extensions_json_string;
1065
jrkoonced1e14722019-08-27 11:51:02 -05001066 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001067 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001068 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001069};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001070
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001071struct Scene {
1072 std::string name;
1073 std::vector<int> nodes;
1074
Selmar09d2ff12018-03-15 17:30:42 +01001075 ExtensionMap extensions;
1076 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001077
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001078 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1079 std::string extras_json_string;
1080 std::string extensions_json_string;
1081
jrkoonced1e14722019-08-27 11:51:02 -05001082 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001083 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001084 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001085};
1086
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001087struct SpotLight {
1088 double innerConeAngle;
1089 double outerConeAngle;
1090
Johan Bowald52936a02019-07-17 09:06:45 +02001091 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001092 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001093 bool operator==(const SpotLight &) const;
1094
1095 ExtensionMap extensions;
1096 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001097
1098 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1099 std::string extras_json_string;
1100 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001101};
1102
Emanuel Schrade186322b2017-11-06 11:14:41 +01001103struct Light {
1104 std::string name;
1105 std::vector<double> color;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001106 double intensity;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001107 std::string type;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001108 double range;
1109 SpotLight spot;
1110
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001111 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001112 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001113
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001114 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001115
1116 ExtensionMap extensions;
1117 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001118
1119 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1120 std::string extras_json_string;
1121 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001122};
1123
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001124class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001125 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001126 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001127 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001128
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001129 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001130
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001131 std::vector<Accessor> accessors;
1132 std::vector<Animation> animations;
1133 std::vector<Buffer> buffers;
1134 std::vector<BufferView> bufferViews;
1135 std::vector<Material> materials;
1136 std::vector<Mesh> meshes;
1137 std::vector<Node> nodes;
1138 std::vector<Texture> textures;
1139 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001140 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001141 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001142 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001143 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001144 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001145
sammyKhana0a62bd2020-01-17 13:41:16 +01001146 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001147 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001148 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001149
1150 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001151
1152 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001153 ExtensionMap extensions;
1154
1155 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1156 std::string extras_json_string;
1157 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001158};
1159
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001160enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001161 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001162 REQUIRE_VERSION = 0x01,
1163 REQUIRE_SCENE = 0x02,
1164 REQUIRE_SCENES = 0x04,
1165 REQUIRE_NODES = 0x08,
1166 REQUIRE_ACCESSORS = 0x10,
1167 REQUIRE_BUFFERS = 0x20,
1168 REQUIRE_BUFFER_VIEWS = 0x40,
1169 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001170};
1171
Squareysff644d82018-03-13 22:36:18 +01001172///
1173/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1174///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001175typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1176 std::string *, int, int,
1177 const unsigned char *, int, void *);
Squareysff644d82018-03-13 22:36:18 +01001178
johan bowald642a3432018-04-01 12:37:18 +02001179///
1180/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1181///
1182typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1183 Image *, bool, void *);
1184
Squareys2d3594d2018-03-13 22:40:53 +01001185#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001186// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001187bool LoadImageData(Image *image, const int image_idx, std::string *err,
1188 std::string *warn, int req_width, int req_height,
1189 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001190#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001191
johan bowald642a3432018-04-01 12:37:18 +02001192#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1193// Declaration of default image writer callback
1194bool WriteImageData(const std::string *basepath, const std::string *filename,
1195 Image *image, bool embedImages, void *);
1196#endif
1197
Paolo Jovone6601bf2018-07-07 20:43:33 +02001198///
1199/// FilExistsFunction type. Signature for custom filesystem callbacks.
1200///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001201typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001202
1203///
1204/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1205///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001206typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001207
1208///
1209/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1210///
1211typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001212 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001213 void *);
1214
1215///
1216/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1217///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001218typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001219 const std::vector<unsigned char> &,
1220 void *);
1221
1222///
1223/// A structure containing all required filesystem callbacks and a pointer to
1224/// their user data.
1225///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001226struct FsCallbacks {
1227 FileExistsFunction FileExists;
1228 ExpandFilePathFunction ExpandFilePath;
1229 ReadWholeFileFunction ReadWholeFile;
1230 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001231
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001232 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001233};
1234
1235#ifndef TINYGLTF_NO_FS
1236// Declaration of default filesystem callbacks
1237
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001238bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001239
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001240std::string ExpandFilePath(const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001241
1242bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001243 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001244
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001245bool WriteWholeFile(std::string *err, const std::string &filepath,
1246 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001247#endif
1248
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001249///
1250/// glTF Parser/Serialier context.
1251///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001252class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001253 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001254#ifdef __clang__
1255#pragma clang diagnostic push
1256#pragma clang diagnostic ignored "-Wc++98-compat"
1257#endif
1258
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001259 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001260
1261#ifdef __clang__
1262#pragma clang diagnostic pop
1263#endif
1264
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001265 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001266
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001267 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001268 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001269 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001270 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001271 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001272 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001273 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001274 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001275
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001276 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001277 /// Loads glTF ASCII asset from string(memory).
1278 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001279 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001280 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001281 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001282 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1283 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001284 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001285 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001286
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001287 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001288 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001289 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001290 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001291 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001292 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001293 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001294 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001295
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001296 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001297 /// Loads glTF binary asset from memory.
1298 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001299 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001300 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001301 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001302 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001303 const unsigned char *bytes,
1304 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001305 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001306 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001307
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001308 ///
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001309 /// Write glTF to stream, buffers and images will be embeded
1310 ///
1311 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1312 bool prettyPrint, bool writeBinary);
1313
1314 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001315 /// Write glTF to file.
1316 ///
johan bowald642a3432018-04-01 12:37:18 +02001317 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001318 bool embedImages, bool embedBuffers,
1319 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001320
Squareysff644d82018-03-13 22:36:18 +01001321 ///
1322 /// Set callback to use for loading image data
1323 ///
1324 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1325
johan bowald642a3432018-04-01 12:37:18 +02001326 ///
1327 /// Set callback to use for writing image data
1328 ///
1329 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1330
Paolo Jovone6601bf2018-07-07 20:43:33 +02001331 ///
1332 /// Set callbacks to use for filesystem (fs) access and their user data
1333 ///
1334 void SetFsCallbacks(FsCallbacks callbacks);
1335
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001336 ///
1337 /// Set serializing default values(default = false).
1338 /// When true, default values are force serialized to .glTF.
Syoyo Fujitaff515702019-08-24 16:29:14 +09001339 /// This may be helpfull if you want to serialize a full description of glTF
1340 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001341 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001342 /// TODO(LTE): Supply parsing option as function arguments to
1343 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001344 ///
1345 void SetSerializeDefaultValues(const bool enabled) {
1346 serialize_default_values_ = enabled;
1347 }
1348
Syoyo Fujitaff515702019-08-24 16:29:14 +09001349 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001350
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001351 ///
1352 /// Store original JSON string for `extras` and `extensions`.
1353 /// This feature will be useful when the user want to reconstruct custom data
1354 /// structure from JSON string.
1355 ///
1356 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1357 store_original_json_for_extras_and_extensions_ = enabled;
1358 }
1359
1360 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1361 return store_original_json_for_extras_and_extensions_;
1362 }
1363
Syoyo Fujitabeded612016-05-01 20:03:43 +09001364 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001365 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001366 /// Loads glTF asset from string(memory).
1367 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001368 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001369 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001370 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001371 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1372 const char *str, const unsigned int length,
1373 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001374
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001375 const unsigned char *bin_data_ = nullptr;
1376 size_t bin_size_ = 0;
1377 bool is_binary_ = false;
1378
Syoyo Fujitaff515702019-08-24 16:29:14 +09001379 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001380
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001381 bool store_original_json_for_extras_and_extensions_ = false;
1382
Paolo Jovone6601bf2018-07-07 20:43:33 +02001383 FsCallbacks fs = {
1384#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001385 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1386 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001387
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001388 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001389#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001390 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001391
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001392 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001393#endif
1394 };
1395
Squareysff644d82018-03-13 22:36:18 +01001396 LoadImageDataFunction LoadImageData =
1397#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001398 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001399#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001400 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001401#endif
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001402 void *load_image_user_data_ = reinterpret_cast<void *>(&fs);
johan bowald642a3432018-04-01 12:37:18 +02001403
1404 WriteImageDataFunction WriteImageData =
1405#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001406 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001407#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001408 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001409#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02001410 void *write_image_user_data_ = reinterpret_cast<void *>(&fs);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001411};
1412
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001413#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001414#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001415#endif
1416
Syoyo Fujita7c877972016-03-08 01:31:49 +09001417} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001418
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001419#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001420
Selmar Kok31cb7f92018-10-03 15:39:05 +02001421#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001422#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001423//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001424#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001425#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001426#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001427#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001428#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001429
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001430#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001431// Disable some warnings for external files.
1432#pragma clang diagnostic push
1433#pragma clang diagnostic ignored "-Wfloat-equal"
1434#pragma clang diagnostic ignored "-Wexit-time-destructors"
1435#pragma clang diagnostic ignored "-Wconversion"
1436#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001437#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001438#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001439#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001440#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001441#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1442#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001443#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001444#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001445#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1446#pragma clang diagnostic ignored "-Wswitch-enum"
1447#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001448#pragma clang diagnostic ignored "-Wweak-vtables"
1449#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001450#if __has_warning("-Wdouble-promotion")
1451#pragma clang diagnostic ignored "-Wdouble-promotion"
1452#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001453#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001454#pragma clang diagnostic ignored "-Wcomma"
1455#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001456#if __has_warning("-Wzero-as-null-pointer-constant")
1457#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1458#endif
1459#if __has_warning("-Wcast-qual")
1460#pragma clang diagnostic ignored "-Wcast-qual"
1461#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001462#if __has_warning("-Wmissing-variable-declarations")
1463#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1464#endif
1465#if __has_warning("-Wmissing-prototypes")
1466#pragma clang diagnostic ignored "-Wmissing-prototypes"
1467#endif
1468#if __has_warning("-Wcast-align")
1469#pragma clang diagnostic ignored "-Wcast-align"
1470#endif
1471#if __has_warning("-Wnewline-eof")
1472#pragma clang diagnostic ignored "-Wnewline-eof"
1473#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001474#if __has_warning("-Wunused-parameter")
1475#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001476#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001477#if __has_warning("-Wmismatched-tags")
1478#pragma clang diagnostic ignored "-Wmismatched-tags"
1479#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001480#if __has_warning("-Wextra-semi-stmt")
1481#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1482#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001483#endif
1484
1485// Disable GCC warnigs
1486#ifdef __GNUC__
1487#pragma GCC diagnostic push
1488#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001489#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001490
krokofc0116b2019-03-03 08:28:49 +02001491#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001492#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001493#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001494#else
jrkooncecba5d6c2019-08-29 11:26:22 -05001495#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001496#include "prettywriter.h"
1497#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001498#include "stringbuffer.h"
1499#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001500#endif
krokof4b6d112019-03-03 01:11:31 +02001501#endif
Alex Wood7319db72019-01-24 15:38:16 -05001502
1503#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001504#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001505#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001506#endif
Squareys2d3594d2018-03-13 22:40:53 +01001507
1508#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001509#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001510#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001511#endif
krokof4b6d112019-03-03 01:11:31 +02001512#endif
Squareys2d3594d2018-03-13 22:40:53 +01001513
johan bowald642a3432018-04-01 12:37:18 +02001514#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001515#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001516#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001517#endif
krokof4b6d112019-03-03 01:11:31 +02001518#endif
johan bowald642a3432018-04-01 12:37:18 +02001519
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001520#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001521#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001522#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001523
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001524#ifdef __GNUC__
1525#pragma GCC diagnostic pop
1526#endif
1527
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001528#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001529
1530// issue 143.
1531// Define NOMINMAX to avoid min/max defines,
1532// but undef it after included windows.h
1533#ifndef NOMINMAX
1534#define TINYGLTF_INTERNAL_NOMINMAX
1535#define NOMINMAX
1536#endif
1537
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001538#ifndef WIN32_LEAN_AND_MEAN
1539#define WIN32_LEAN_AND_MEAN
1540#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1541#endif
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001542#include <windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001543
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001544#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1545#undef WIN32_LEAN_AND_MEAN
1546#endif
1547
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001548#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1549#undef NOMINMAX
1550#endif
1551
Syoyo Fujita45cac782019-11-09 20:42:55 +09001552#if defined(__GLIBCXX__) // mingw
1553
1554#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
1555#include <fcntl.h> // _O_RDONLY
1556
1557#endif
1558
Florian Märkld525e192017-09-22 15:25:48 +02001559#elif !defined(__ANDROID__)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001560#include <wordexp.h>
1561#endif
1562
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001563#if defined(__sparcv9)
1564// Big endian
1565#else
1566#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1567#define TINYGLTF_LITTLE_ENDIAN 1
1568#endif
1569#endif
1570
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001571namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001572#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001573
1574#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001575// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1576// documents may be active at once.
1577using json =
1578 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1579using json_const_iterator = json::ConstMemberIterator;
1580using json_const_array_iterator = json const *;
1581using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001582 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001583rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1584rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001585#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001586// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1587// not thread safe. Only a single JsonDocument may be active at any one time,
1588// meaning only a single gltf load/save can be active any one time.
1589using json = rapidjson::Value;
1590using json_const_iterator = json::ConstMemberIterator;
1591using json_const_array_iterator = json const *;
1592rapidjson::Document *s_pActiveDocument = nullptr;
1593rapidjson::Document::AllocatorType &GetAllocator() {
1594 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1595 return s_pActiveDocument->GetAllocator();
1596}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001597
1598#ifdef __clang__
1599#pragma clang diagnostic push
1600// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1601#pragma clang diagnostic ignored "-Wunused-member-function"
1602#endif
1603
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001604struct JsonDocument : public rapidjson::Document {
1605 JsonDocument() {
1606 assert(s_pActiveDocument ==
1607 nullptr); // When using default allocator, only one document can be
1608 // active at a time, if you need multiple active at once,
1609 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1610 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001611 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001612 JsonDocument(const JsonDocument &) = delete;
1613 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001614 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001615 s_pActiveDocument = this;
1616 rhs.isNil = true;
1617 }
1618 ~JsonDocument() {
1619 if (!isNil) {
1620 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001621 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001622 }
jrkooncece7fa742019-09-04 13:31:44 -05001623
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001624 private:
1625 bool isNil = false;
1626};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001627
1628#ifdef __clang__
1629#pragma clang diagnostic pop
1630#endif
1631
jrkooncece7fa742019-09-04 13:31:44 -05001632#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001633
jrkooncecba5d6c2019-08-29 11:26:22 -05001634#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001635using nlohmann::json;
1636using json_const_iterator = json::const_iterator;
1637using json_const_array_iterator = json_const_iterator;
1638using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001639#endif
1640
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001641void JsonParse(JsonDocument &doc, const char *str, size_t length,
1642 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001643#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001644 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001645 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001646#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001647 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001648#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001649}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001650} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001651
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001652#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001653#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001654#endif
1655
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001656#ifdef __clang__
1657#pragma clang diagnostic push
1658#pragma clang diagnostic ignored "-Wc++98-compat"
1659#endif
1660
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001661namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001662
Selmar Kok31cb7f92018-10-03 15:39:05 +02001663// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001664static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1665 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001666
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001667 switch (one.Type()) {
1668 case NULL_TYPE:
1669 return true;
1670 case BOOL_TYPE:
1671 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001672 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001673 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1674 case INT_TYPE:
1675 return one.Get<int>() == other.Get<int>();
1676 case OBJECT_TYPE: {
1677 auto oneObj = one.Get<tinygltf::Value::Object>();
1678 auto otherObj = other.Get<tinygltf::Value::Object>();
1679 if (oneObj.size() != otherObj.size()) return false;
1680 for (auto &it : oneObj) {
1681 auto otherIt = otherObj.find(it.first);
1682 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001683
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001684 if (!Equals(it.second, otherIt->second)) return false;
1685 }
1686 return true;
1687 }
1688 case ARRAY_TYPE: {
1689 if (one.Size() != other.Size()) return false;
1690 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001691 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001692 return true;
1693 }
1694 case STRING_TYPE:
1695 return one.Get<std::string>() == other.Get<std::string>();
1696 case BINARY_TYPE:
1697 return one.Get<std::vector<unsigned char> >() ==
1698 other.Get<std::vector<unsigned char> >();
1699 default: {
1700 // unhandled type
1701 return false;
1702 }
1703 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001704}
1705
1706// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001707static bool Equals(const std::vector<double> &one,
1708 const std::vector<double> &other) {
1709 if (one.size() != other.size()) return false;
1710 for (int i = 0; i < int(one.size()); ++i) {
1711 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1712 }
1713 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001714}
1715
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001716bool Accessor::operator==(const Accessor &other) const {
1717 return this->bufferView == other.bufferView &&
1718 this->byteOffset == other.byteOffset &&
1719 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001720 this->count == other.count && this->extensions == other.extensions &&
1721 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001722 Equals(this->maxValues, other.maxValues) &&
1723 Equals(this->minValues, other.minValues) && this->name == other.name &&
1724 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001725}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001726bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001727 return this->channels == other.channels &&
1728 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001729 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001730}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001731bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001732 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001733 this->target_node == other.target_node &&
1734 this->target_path == other.target_path &&
1735 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001736}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001737bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001738 return this->extras == other.extras && this->extensions == other.extensions &&
1739 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001740 this->interpolation == other.interpolation &&
1741 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001742}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001743bool Asset::operator==(const Asset &other) const {
1744 return this->copyright == other.copyright &&
1745 this->extensions == other.extensions && this->extras == other.extras &&
1746 this->generator == other.generator &&
1747 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001748}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001749bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001750 return this->data == other.data && this->extensions == other.extensions &&
1751 this->extras == other.extras && this->name == other.name &&
1752 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001753}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001754bool BufferView::operator==(const BufferView &other) const {
1755 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1756 this->byteOffset == other.byteOffset &&
1757 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001758 this->target == other.target && this->extensions == other.extensions &&
1759 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001760 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001761}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001762bool Camera::operator==(const Camera &other) const {
1763 return this->name == other.name && this->extensions == other.extensions &&
1764 this->extras == other.extras &&
1765 this->orthographic == other.orthographic &&
1766 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001767}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001768bool Image::operator==(const Image &other) const {
1769 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001770 this->component == other.component &&
1771 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001772 this->height == other.height && this->image == other.image &&
1773 this->mimeType == other.mimeType && this->name == other.name &&
1774 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001775}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001776bool Light::operator==(const Light &other) const {
1777 return Equals(this->color, other.color) && this->name == other.name &&
1778 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001779}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001780bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001781 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1782 (this->normalTexture == other.normalTexture) &&
1783 (this->occlusionTexture == other.occlusionTexture) &&
1784 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001785 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001786 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001787 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001788 (this->doubleSided == other.doubleSided) &&
1789 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001790 (this->extras == other.extras) && (this->values == other.values) &&
1791 (this->additionalValues == other.additionalValues) &&
1792 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001793}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001794bool Mesh::operator==(const Mesh &other) const {
1795 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001796 this->name == other.name && Equals(this->weights, other.weights) &&
1797 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001798}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001799bool Model::operator==(const Model &other) const {
1800 return this->accessors == other.accessors &&
1801 this->animations == other.animations && this->asset == other.asset &&
1802 this->buffers == other.buffers &&
1803 this->bufferViews == other.bufferViews &&
1804 this->cameras == other.cameras &&
1805 this->defaultScene == other.defaultScene &&
1806 this->extensions == other.extensions &&
1807 this->extensionsRequired == other.extensionsRequired &&
1808 this->extensionsUsed == other.extensionsUsed &&
1809 this->extras == other.extras && this->images == other.images &&
1810 this->lights == other.lights && this->materials == other.materials &&
1811 this->meshes == other.meshes && this->nodes == other.nodes &&
1812 this->samplers == other.samplers && this->scenes == other.scenes &&
1813 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001814}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001815bool Node::operator==(const Node &other) const {
1816 return this->camera == other.camera && this->children == other.children &&
1817 this->extensions == other.extensions && this->extras == other.extras &&
1818 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1819 this->name == other.name && Equals(this->rotation, other.rotation) &&
1820 Equals(this->scale, other.scale) && this->skin == other.skin &&
1821 Equals(this->translation, other.translation) &&
1822 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001823}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001824bool SpotLight::operator==(const SpotLight &other) const {
1825 return this->extensions == other.extensions && this->extras == other.extras &&
1826 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1827 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1828}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001829bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1830 return this->extensions == other.extensions && this->extras == other.extras &&
1831 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1832 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1833 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1834 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001835}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001836bool Parameter::operator==(const Parameter &other) const {
1837 if (this->bool_value != other.bool_value ||
1838 this->has_number_value != other.has_number_value)
1839 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001840
Selmar Kok2bda71c2018-10-05 14:36:05 +02001841 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1842 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001843
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001844 if (this->json_double_value.size() != other.json_double_value.size())
1845 return false;
1846 for (auto &it : this->json_double_value) {
1847 auto otherIt = other.json_double_value.find(it.first);
1848 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001849
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001850 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1851 }
1852
1853 if (!Equals(this->number_array, other.number_array)) return false;
1854
1855 if (this->string_value != other.string_value) return false;
1856
1857 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001858}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001859bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1860 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1861 this->extensions == other.extensions && this->extras == other.extras &&
1862 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1863 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1864 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001865}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001866bool Primitive::operator==(const Primitive &other) const {
1867 return this->attributes == other.attributes && this->extras == other.extras &&
1868 this->indices == other.indices && this->material == other.material &&
1869 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001870}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001871bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001872 return this->extensions == other.extensions && this->extras == other.extras &&
1873 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001874 this->minFilter == other.minFilter && this->name == other.name &&
1875 this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
1876 this->wrapT == other.wrapT;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001877}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001878bool Scene::operator==(const Scene &other) const {
1879 return this->extensions == other.extensions && this->extras == other.extras &&
1880 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001881}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001882bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001883 return this->extensions == other.extensions && this->extras == other.extras &&
1884 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001885 this->joints == other.joints && this->name == other.name &&
1886 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001887}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001888bool Texture::operator==(const Texture &other) const {
1889 return this->extensions == other.extensions && this->extras == other.extras &&
1890 this->name == other.name && this->sampler == other.sampler &&
1891 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001892}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001893bool TextureInfo::operator==(const TextureInfo &other) const {
1894 return this->extensions == other.extensions && this->extras == other.extras &&
1895 this->index == other.index && this->texCoord == other.texCoord;
1896}
1897bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1898 return this->extensions == other.extensions && this->extras == other.extras &&
1899 this->index == other.index && this->texCoord == other.texCoord &&
1900 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
1901}
1902bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
1903 return this->extensions == other.extensions && this->extras == other.extras &&
1904 this->index == other.index && this->texCoord == other.texCoord &&
1905 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
1906}
1907bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
1908 return this->extensions == other.extensions && this->extras == other.extras &&
1909 (this->baseColorTexture == other.baseColorTexture) &&
1910 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001911 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001912 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
1913 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
1914}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001915bool Value::operator==(const Value &other) const {
1916 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001917}
1918
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001919static void swap4(unsigned int *val) {
1920#ifdef TINYGLTF_LITTLE_ENDIAN
1921 (void)val;
1922#else
1923 unsigned int tmp = *val;
1924 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
1925 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
1926
1927 dst[0] = src[3];
1928 dst[1] = src[2];
1929 dst[2] = src[1];
1930 dst[3] = src[0];
1931#endif
1932}
1933
Syoyo Fujitabeded612016-05-01 20:03:43 +09001934static std::string JoinPath(const std::string &path0,
1935 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001936 if (path0.empty()) {
1937 return path1;
1938 } else {
1939 // check '/'
1940 char lastChar = *path0.rbegin();
1941 if (lastChar != '/') {
1942 return path0 + std::string("/") + path1;
1943 } else {
1944 return path0 + path1;
1945 }
1946 }
1947}
1948
Syoyo Fujita643ce102016-05-01 17:19:37 +09001949static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001950 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001951 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
1952 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02001953 // Error, fs callback[s] missing
1954 return std::string();
1955 }
1956
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001957 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001958 std::string absPath =
1959 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001960 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001961 return absPath;
1962 }
1963 }
1964
1965 return std::string();
1966}
1967
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001968static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02001969 if (FileName.find_last_of(".") != std::string::npos)
1970 return FileName.substr(FileName.find_last_of(".") + 1);
1971 return "";
1972}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001973
Syoyo Fujita643ce102016-05-01 17:19:37 +09001974static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001975 if (filepath.find_last_of("/\\") != std::string::npos)
1976 return filepath.substr(0, filepath.find_last_of("/\\"));
1977 return "";
1978}
1979
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001980// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001981static std::string GetBaseFilename(const std::string &filepath) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001982 return filepath.substr(filepath.find_last_of("/\\") + 1);
1983}
1984
Syoyo Fujita719d7e42018-03-30 19:26:35 +09001985std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001986std::string base64_decode(std::string const &s);
1987
1988/*
1989 base64.cpp and base64.h
1990
1991 Copyright (C) 2004-2008 René Nyffenegger
1992
1993 This source code is provided 'as-is', without any express or implied
1994 warranty. In no event will the author be held liable for any damages
1995 arising from the use of this software.
1996
1997 Permission is granted to anyone to use this software for any purpose,
1998 including commercial applications, and to alter it and redistribute it
1999 freely, subject to the following restrictions:
2000
2001 1. The origin of this source code must not be misrepresented; you must not
2002 claim that you wrote the original source code. If you use this source code
2003 in a product, an acknowledgment in the product documentation would be
2004 appreciated but is not required.
2005
2006 2. Altered source versions must be plainly marked as such, and must not be
2007 misrepresented as being the original source code.
2008
2009 3. This notice may not be removed or altered from any source distribution.
2010
2011 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2012
2013*/
2014
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002015#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002016#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002017#pragma clang diagnostic ignored "-Wsign-conversion"
2018#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002019#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002020
2021static inline bool is_base64(unsigned char c) {
2022 return (isalnum(c) || (c == '+') || (c == '/'));
2023}
2024
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002025std::string base64_encode(unsigned char const *bytes_to_encode,
2026 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002027 std::string ret;
2028 int i = 0;
2029 int j = 0;
2030 unsigned char char_array_3[3];
2031 unsigned char char_array_4[4];
2032
Syoyo Fujitaff515702019-08-24 16:29:14 +09002033 const char *base64_chars =
2034 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2035 "abcdefghijklmnopqrstuvwxyz"
2036 "0123456789+/";
2037
johan bowald30c53472018-03-30 11:49:36 +02002038 while (in_len--) {
2039 char_array_3[i++] = *(bytes_to_encode++);
2040 if (i == 3) {
2041 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002042 char_array_4[1] =
2043 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2044 char_array_4[2] =
2045 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002046 char_array_4[3] = char_array_3[2] & 0x3f;
2047
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002048 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002049 i = 0;
2050 }
2051 }
2052
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002053 if (i) {
2054 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002055
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002056 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2057 char_array_4[1] =
2058 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2059 char_array_4[2] =
2060 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002061
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002062 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002063
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002064 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002065 }
2066
2067 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002068}
2069
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002070std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002071 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002072 int i = 0;
2073 int j = 0;
2074 int in_ = 0;
2075 unsigned char char_array_4[4], char_array_3[3];
2076 std::string ret;
2077
Syoyo Fujitaff515702019-08-24 16:29:14 +09002078 const std::string base64_chars =
2079 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2080 "abcdefghijklmnopqrstuvwxyz"
2081 "0123456789+/";
2082
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002083 while (in_len-- && (encoded_string[in_] != '=') &&
2084 is_base64(encoded_string[in_])) {
2085 char_array_4[i++] = encoded_string[in_];
2086 in_++;
2087 if (i == 4) {
2088 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002089 char_array_4[i] =
2090 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002091
2092 char_array_3[0] =
2093 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2094 char_array_3[1] =
2095 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2096 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2097
Syoyo Fujita7c877972016-03-08 01:31:49 +09002098 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002099 i = 0;
2100 }
2101 }
2102
2103 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002104 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002105
2106 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002107 char_array_4[j] =
2108 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002109
2110 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2111 char_array_3[1] =
2112 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2113 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2114
Syoyo Fujita7c877972016-03-08 01:31:49 +09002115 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002116 }
2117
2118 return ret;
2119}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002120#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002121#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002122#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002123
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002124static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002125 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002126 const std::string &basedir, bool required,
2127 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002128 if (fs == nullptr || fs->FileExists == nullptr ||
2129 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002130 // This is a developer error, assert() ?
2131 if (err) {
2132 (*err) += "FS callback[s] not set\n";
2133 }
2134 return false;
2135 }
2136
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002137 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002138
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002139 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002140
2141 std::vector<std::string> paths;
2142 paths.push_back(basedir);
2143 paths.push_back(".");
2144
Paolo Jovone6601bf2018-07-07 20:43:33 +02002145 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002146 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002147 if (failMsgOut) {
2148 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002149 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002150 return false;
2151 }
2152
Paolo Jovone6601bf2018-07-07 20:43:33 +02002153 std::vector<unsigned char> buf;
2154 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002155 bool fileRead =
2156 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002157 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002158 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002159 (*failMsgOut) +=
2160 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002161 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002162 return false;
2163 }
2164
Paolo Jovone6601bf2018-07-07 20:43:33 +02002165 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002166 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002167 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002168 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002169 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002170 return false;
2171 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002172
2173 if (checkSize) {
2174 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002175 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002176 return true;
2177 } else {
2178 std::stringstream ss;
2179 ss << "File size mismatch : " << filepath << ", requestedBytes "
2180 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002181 if (failMsgOut) {
2182 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002183 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002184 return false;
2185 }
2186 }
2187
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002188 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002189 return true;
2190}
2191
Squareysff644d82018-03-13 22:36:18 +01002192void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002193 LoadImageData = func;
2194 load_image_user_data_ = user_data;
Squareysff644d82018-03-13 22:36:18 +01002195}
2196
Squareys2d3594d2018-03-13 22:40:53 +01002197#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002198bool LoadImageData(Image *image, const int image_idx, std::string *err,
2199 std::string *warn, int req_width, int req_height,
2200 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09002201 (void)user_data;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002202 (void)warn;
2203
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002204 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002205
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002206 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002207
Victor Bushong18ef3382018-08-22 22:03:30 -05002208 // force 32-bit textures for common Vulkan compatibility. It appears that
2209 // some GPU drivers do not support 24-bit images for Vulkan
2210 req_comp = 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002211 int bits = 8;
2212 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002213
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002214 // It is possible that the image we want to load is a 16bit per channel image
2215 // We are going to attempt to load it as 16bit per channel, and if it worked,
2216 // set the image data accodingly. We are casting the returned pointer into
2217 // unsigned char, because we are representing "bytes". But we are updating
2218 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2219 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002220 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002221 data = reinterpret_cast<unsigned char *>(
2222 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002223 if (data) {
2224 bits = 16;
2225 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2226 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002227 }
2228
2229 // at this point, if data is still NULL, it means that the image wasn't
2230 // 16bit per channel, we are going to load it as a normal 8bit per channel
2231 // mage as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002232 // if image cannot be decoded, ignore parsing and keep it by its path
2233 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002234 // FIXME we should only enter this function if the image is embedded. If
2235 // image->uri references
2236 // an image file, it should be left as it is. Image loading should not be
2237 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002238 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002239 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002240 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002241 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002242 (*err) +=
2243 "Unknown image format. STB cannot decode image data for image[" +
2244 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002245 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002246 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002247 }
2248
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002249 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002250 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002251 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002252 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2253 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002254 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002255 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002256 }
2257
2258 if (req_width > 0) {
2259 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002260 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002261 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002262 (*err) += "Image width mismatch for image[" +
2263 std::to_string(image_idx) + "] name = \"" + image->name +
2264 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002265 }
2266 return false;
2267 }
2268 }
2269
2270 if (req_height > 0) {
2271 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002272 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002273 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002274 (*err) += "Image height mismatch. for image[" +
2275 std::to_string(image_idx) + "] name = \"" + image->name +
2276 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002277 }
2278 return false;
2279 }
2280 }
2281
2282 image->width = w;
2283 image->height = h;
Victor Bushong18ef3382018-08-22 22:03:30 -05002284 image->component = req_comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002285 image->bits = bits;
2286 image->pixel_type = pixel_type;
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002287 image->image.resize(static_cast<size_t>(w * h * req_comp) * size_t(bits / 8));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002288 std::copy(data, data + w * h * req_comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002289 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002290
Syoyo Fujitabeded612016-05-01 20:03:43 +09002291 return true;
2292}
Squareys2d3594d2018-03-13 22:40:53 +01002293#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002294
johan bowald642a3432018-04-01 12:37:18 +02002295void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2296 WriteImageData = func;
2297 write_image_user_data_ = user_data;
2298}
2299
2300#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2301static void WriteToMemory_stbi(void *context, void *data, int size) {
2302 std::vector<unsigned char> *buffer =
2303 reinterpret_cast<std::vector<unsigned char> *>(context);
2304
2305 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2306
2307 buffer->insert(buffer->end(), pData, pData + size);
2308}
2309
2310bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002311 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002312 const std::string ext = GetFilePathExtension(*filename);
2313
Paolo Jovone6601bf2018-07-07 20:43:33 +02002314 // Write image to temporary buffer
2315 std::string header;
2316 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002317
Paolo Jovone6601bf2018-07-07 20:43:33 +02002318 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002319 if ((image->bits != 8) ||
2320 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002321 // Unsupported pixel format
2322 return false;
2323 }
2324
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002325 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002326 image->height, image->component,
2327 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002328 return false;
2329 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002330 header = "data:image/png;base64,";
2331 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002332 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002333 image->height, image->component,
2334 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002335 return false;
2336 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002337 header = "data:image/jpeg;base64,";
2338 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002339 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002340 image->height, image->component,
2341 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002342 return false;
2343 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002344 header = "data:image/bmp;base64,";
2345 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002346 // Error: can't output requested format to file
2347 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002348 }
johan bowald642a3432018-04-01 12:37:18 +02002349
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002350 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002351 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002352 if (data.size()) {
2353 image->uri =
2354 header +
2355 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
2356 } else {
2357 // Throw error?
2358 }
2359 } else {
2360 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002361 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002362 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002363 const std::string imagefilepath = JoinPath(*basepath, *filename);
2364 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002365 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2366 fs->user_data)) {
2367 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002368 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002369 }
johan bowald642a3432018-04-01 12:37:18 +02002370 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002371 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002372 }
2373 image->uri = *filename;
2374 }
2375
2376 return true;
2377}
2378#endif
2379
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002380void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002381
Harokyangfb256602019-10-30 16:13:52 +08002382#ifdef _WIN32
2383static inline std::wstring UTF8ToWchar(const std::string &str) {
2384 int wstr_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
2385 std::wstring wstr(wstr_size, 0);
Harokyang05a44562019-10-30 16:30:00 +08002386 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002387 return wstr;
2388}
2389#endif
2390
Paolo Jovone6601bf2018-07-07 20:43:33 +02002391#ifndef TINYGLTF_NO_FS
2392// Default implementations of filesystem functions
2393
2394bool FileExists(const std::string &abs_filename, void *) {
2395 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002396#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002397 if (asset_manager) {
2398 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2399 AASSET_MODE_STREAMING);
2400 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002401 return false;
2402 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002403 AAsset_close(asset);
2404 ret = true;
2405 } else {
2406 return false;
2407 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002408#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002409#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09002410#if defined(_MSC_VER) || defined(__GLIBCXX__)
2411 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002412 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002413 if (err != 0) {
2414 return false;
2415 }
2416#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002417 FILE *fp = nullptr;
2418 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2419 if (err != 0) {
2420 return false;
2421 }
2422#endif
2423
2424#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002425 FILE *fp = fopen(abs_filename.c_str(), "rb");
2426#endif
2427 if (fp) {
2428 ret = true;
2429 fclose(fp);
2430 } else {
2431 ret = false;
2432 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002433#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002434
2435 return ret;
2436}
2437
2438std::string ExpandFilePath(const std::string &filepath, void *) {
2439#ifdef _WIN32
2440 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
2441 char *str = new char[len];
2442 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
2443
2444 std::string s(str);
2445
2446 delete[] str;
2447
2448 return s;
2449#else
2450
2451#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Vladimír Vondruš9f045832018-07-24 23:32:17 +02002452 defined(__ANDROID__) || defined(__EMSCRIPTEN__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002453 // no expansion
2454 std::string s = filepath;
2455#else
2456 std::string s;
2457 wordexp_t p;
2458
2459 if (filepath.empty()) {
2460 return "";
2461 }
2462
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002463 // Quote the string to keep any spaces in filepath intact.
2464 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002465 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002466 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002467 if (ret) {
2468 // err
2469 s = filepath;
2470 return s;
2471 }
2472
2473 // Use first element only.
2474 if (p.we_wordv) {
2475 s = std::string(p.we_wordv[0]);
2476 wordfree(&p);
2477 } else {
2478 s = filepath;
2479 }
2480
2481#endif
2482
2483 return s;
2484#endif
2485}
2486
2487bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2488 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002489#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2490 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002491 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2492 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002493 if (!asset) {
2494 if (err) {
2495 (*err) += "File open error : " + filepath + "\n";
2496 }
2497 return false;
2498 }
2499 size_t size = AAsset_getLength(asset);
2500 if (size <= 0) {
2501 if (err) {
2502 (*err) += "Invalid file size : " + filepath +
2503 " (does the path point to a directory?)";
2504 }
2505 }
2506 out->resize(size);
2507 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2508 AAsset_close(asset);
2509 return true;
2510 } else {
2511 if (err) {
2512 (*err) += "No asset manager specified : " + filepath + "\n";
2513 }
2514 return false;
2515 }
2516#else
Harokyang5cecef22019-10-30 15:16:46 +08002517#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09002518#if defined(__GLIBCXX__) // mingw
2519 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2520 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2521 std::istream f(&wfile_buf);
2522#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002523 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002524#else // clang?
2525 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2526#endif
Harokyang5cecef22019-10-30 15:16:46 +08002527#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002528 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002529#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002530 if (!f) {
2531 if (err) {
2532 (*err) += "File open error : " + filepath + "\n";
2533 }
2534 return false;
2535 }
2536
2537 f.seekg(0, f.end);
2538 size_t sz = static_cast<size_t>(f.tellg());
2539 f.seekg(0, f.beg);
2540
Syoyo Fujitae8862472019-10-20 17:47:50 +09002541 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002542 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002543 (*err) += "Invalid file size : " + filepath +
2544 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002545 }
2546 return false;
2547 } else if (sz == 0) {
2548 if (err) {
2549 (*err) += "File is empty : " + filepath + "\n";
2550 }
2551 return false;
2552 }
2553
2554 out->resize(sz);
2555 f.read(reinterpret_cast<char *>(&out->at(0)),
2556 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002557
2558 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002559#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002560}
2561
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002562bool WriteWholeFile(std::string *err, const std::string &filepath,
2563 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002564#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09002565#if defined(__GLIBCXX__) // mingw
2566 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), _O_WRONLY | _O_BINARY);
2567 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2568 std::ostream f(&wfile_buf);
2569#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002570 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002571#else // clang?
2572 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2573#endif
Harokyangfb256602019-10-30 16:13:52 +08002574#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002575 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002576#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002577 if (!f) {
2578 if (err) {
2579 (*err) += "File open error for writing : " + filepath + "\n";
2580 }
2581 return false;
2582 }
2583
2584 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2585 static_cast<std::streamsize>(contents.size()));
2586 if (!f) {
2587 if (err) {
2588 (*err) += "File write error: " + filepath + "\n";
2589 }
2590 return false;
2591 }
2592
Paolo Jovone6601bf2018-07-07 20:43:33 +02002593 return true;
2594}
2595
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002596#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002597
johan bowald642a3432018-04-01 12:37:18 +02002598static std::string MimeToExt(const std::string &mimeType) {
2599 if (mimeType == "image/jpeg") {
2600 return "jpg";
2601 } else if (mimeType == "image/png") {
2602 return "png";
2603 } else if (mimeType == "image/bmp") {
2604 return "bmp";
2605 } else if (mimeType == "image/gif") {
2606 return "gif";
2607 }
2608
2609 return "";
2610}
2611
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002612static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002613 bool embedImages,
2614 WriteImageDataFunction *WriteImageData = nullptr,
2615 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002616 std::string filename;
2617 std::string ext;
FsiGuy00015623db855c62020-03-09 16:57:21 -05002618 // If image has uri, use it it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002619 if (image.uri.size()) {
2620 filename = GetBaseFilename(image.uri);
2621 ext = GetFilePathExtension(filename);
FsiGuy00015623db855c62020-03-09 16:57:21 -05002622 }
2623 else if (image.bufferView != -1) {
2624 //If there's no URI and the data exists in a buffer,
2625 //don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002626 } else if (image.name.size()) {
2627 ext = MimeToExt(image.mimeType);
2628 // Otherwise use name as filename
2629 filename = image.name + "." + ext;
2630 } else {
2631 ext = MimeToExt(image.mimeType);
2632 // Fallback to index of image as filename
2633 filename = std::to_string(index) + "." + ext;
2634 }
2635
2636 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002637 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002638 std::string uri;
2639 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2640 }
2641}
2642
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002643bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002644 std::string header = "data:application/octet-stream;base64,";
2645 if (in.find(header) == 0) {
2646 return true;
2647 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002648
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002649 header = "data:image/jpeg;base64,";
2650 if (in.find(header) == 0) {
2651 return true;
2652 }
Squareys43374632018-03-13 22:20:48 +01002653
Syoyo Fujita620eed12016-01-02 23:37:12 +09002654 header = "data:image/png;base64,";
2655 if (in.find(header) == 0) {
2656 return true;
2657 }
Squareys43374632018-03-13 22:20:48 +01002658
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002659 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002660 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002661 return true;
2662 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002663
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002664 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002665 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002666 return true;
2667 }
2668
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002669 header = "data:text/plain;base64,";
2670 if (in.find(header) == 0) {
2671 return true;
2672 }
2673
Syoyo Fujita20244e12018-03-15 11:01:05 -05002674 header = "data:application/gltf-buffer;base64,";
2675 if (in.find(header) == 0) {
2676 return true;
2677 }
2678
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002679 return false;
2680}
2681
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002682bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2683 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002684 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002685 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002686 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002687 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002688 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002689
2690 if (data.empty()) {
2691 header = "data:image/jpeg;base64,";
2692 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002693 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002694 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002695 }
2696 }
2697
2698 if (data.empty()) {
2699 header = "data:image/png;base64,";
2700 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002701 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002702 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002703 }
2704 }
Squareys43374632018-03-13 22:20:48 +01002705
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002706 if (data.empty()) {
2707 header = "data:image/bmp;base64,";
2708 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002709 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002710 data = base64_decode(in.substr(header.size())); // cut mime string.
2711 }
2712 }
2713
2714 if (data.empty()) {
2715 header = "data:image/gif;base64,";
2716 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002717 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002718 data = base64_decode(in.substr(header.size())); // cut mime string.
2719 }
2720 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002721
2722 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002723 header = "data:text/plain;base64,";
2724 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002725 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002726 data = base64_decode(in.substr(header.size()));
2727 }
2728 }
2729
2730 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002731 header = "data:application/gltf-buffer;base64,";
2732 if (in.find(header) == 0) {
2733 data = base64_decode(in.substr(header.size()));
2734 }
2735 }
2736
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002737 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002738 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002739 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002740 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002741
2742 if (checkSize) {
2743 if (data.size() != reqBytes) {
2744 return false;
2745 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002746 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002747 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002748 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002749 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002750 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002751 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002752}
2753
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002754namespace {
2755bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002756#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002757 if (!o.IsDouble()) {
2758 if (o.IsInt()) {
2759 val = o.GetInt();
2760 return true;
2761 } else if (o.IsUint()) {
2762 val = static_cast<int>(o.GetUint());
2763 return true;
2764 } else if (o.IsInt64()) {
2765 val = static_cast<int>(o.GetInt64());
2766 return true;
2767 } else if (o.IsUint64()) {
2768 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002769 return true;
2770 }
jrkoonce5cecc412019-08-29 11:45:04 -05002771 }
2772
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002773 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002774#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002775 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002776
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002777 if ((type == json::value_t::number_integer) ||
2778 (type == json::value_t::number_unsigned)) {
2779 val = static_cast<int>(o.get<int64_t>());
2780 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002781 }
2782
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002783 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002784#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05002785}
2786
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002787#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002788bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002789 if (o.IsDouble()) {
2790 val = o.GetDouble();
2791 return true;
2792 }
2793
2794 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002795}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002796#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002797
2798bool GetNumber(const json &o, double &val) {
2799#ifdef TINYGLTF_USE_RAPIDJSON
2800 if (o.IsNumber()) {
2801 val = o.GetDouble();
2802 return true;
2803 }
2804
2805 return false;
2806#else
2807 if (o.is_number()) {
2808 val = o.get<double>();
2809 return true;
2810 }
2811
2812 return false;
2813#endif
2814}
2815
2816bool GetString(const json &o, std::string &val) {
2817#ifdef TINYGLTF_USE_RAPIDJSON
2818 if (o.IsString()) {
2819 val = o.GetString();
2820 return true;
2821 }
2822
2823 return false;
2824#else
2825 if (o.type() == json::value_t::string) {
2826 val = o.get<std::string>();
2827 return true;
2828 }
2829
2830 return false;
2831#endif
2832}
2833
2834bool IsArray(const json &o) {
2835#ifdef TINYGLTF_USE_RAPIDJSON
2836 return o.IsArray();
2837#else
2838 return o.is_array();
2839#endif
2840}
2841
2842json_const_array_iterator ArrayBegin(const json &o) {
2843#ifdef TINYGLTF_USE_RAPIDJSON
2844 return o.Begin();
2845#else
2846 return o.begin();
2847#endif
2848}
2849
2850json_const_array_iterator ArrayEnd(const json &o) {
2851#ifdef TINYGLTF_USE_RAPIDJSON
2852 return o.End();
2853#else
2854 return o.end();
2855#endif
2856}
2857
2858bool IsObject(const json &o) {
2859#ifdef TINYGLTF_USE_RAPIDJSON
2860 return o.IsObject();
2861#else
2862 return o.is_object();
2863#endif
2864}
2865
2866json_const_iterator ObjectBegin(const json &o) {
2867#ifdef TINYGLTF_USE_RAPIDJSON
2868 return o.MemberBegin();
2869#else
2870 return o.begin();
2871#endif
2872}
2873
2874json_const_iterator ObjectEnd(const json &o) {
2875#ifdef TINYGLTF_USE_RAPIDJSON
2876 return o.MemberEnd();
2877#else
2878 return o.end();
2879#endif
2880}
2881
2882const char *GetKey(json_const_iterator &it) {
2883#ifdef TINYGLTF_USE_RAPIDJSON
2884 return it->name.GetString();
2885#else
2886 return it.key().c_str();
2887#endif
2888}
2889
2890bool FindMember(const json &o, const char *member, json_const_iterator &it) {
2891#ifdef TINYGLTF_USE_RAPIDJSON
2892 if (!o.IsObject()) {
2893 return false;
2894 }
2895 it = o.FindMember(member);
2896 return it != o.MemberEnd();
2897#else
2898 it = o.find(member);
2899 return it != o.end();
2900#endif
2901}
2902
2903const json &GetValue(json_const_iterator &it) {
2904#ifdef TINYGLTF_USE_RAPIDJSON
2905 return it->value;
2906#else
2907 return it.value();
2908#endif
2909}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002910
2911std::string JsonToString(const json &o, int spacing = -1) {
2912#ifdef TINYGLTF_USE_RAPIDJSON
2913 using namespace rapidjson;
2914 StringBuffer buffer;
2915 if (spacing == -1) {
2916 Writer<StringBuffer> writer(buffer);
2917 o.Accept(writer);
2918 } else {
2919 PrettyWriter<StringBuffer> writer(buffer);
2920 writer.SetIndent(' ', uint32_t(spacing));
2921 o.Accept(writer);
2922 }
2923 return buffer.GetString();
2924#else
2925 return o.dump(spacing);
2926#endif
2927}
2928
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002929} // namespace
2930
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002931static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002932 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05002933#ifdef TINYGLTF_USE_RAPIDJSON
2934 using rapidjson::Type;
2935 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002936 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002937 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05002938 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002939 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05002940 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002941 if (entry.Type() != NULL_TYPE)
2942 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02002943 }
jrkooncecba5d6c2019-08-29 11:26:22 -05002944 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002945 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05002946 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002947 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05002948 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05002949 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02002950 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05002951 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002952 if (entry.Type() != NULL_TYPE)
2953 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02002954 }
jrkooncecba5d6c2019-08-29 11:26:22 -05002955 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002956 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05002957 case Type::kStringType:
2958 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02002959 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05002960 case Type::kFalseType:
2961 case Type::kTrueType:
2962 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02002963 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05002964 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002965 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05002966 int i = 0;
2967 GetInt(o, i);
2968 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002969 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05002970 double d = 0.0;
2971 GetDouble(o, d);
2972 val = Value(d);
2973 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02002974 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05002975 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02002976 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002977 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002978 }
jrkooncecba5d6c2019-08-29 11:26:22 -05002979#else
2980 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002981 case json::value_t::object: {
2982 Value::Object value_object;
2983 for (auto it = o.begin(); it != o.end(); it++) {
2984 Value entry;
2985 ParseJsonAsValue(&entry, it.value());
2986 if (entry.Type() != NULL_TYPE)
2987 value_object.emplace(it.key(), std::move(entry));
2988 }
2989 if (value_object.size() > 0) val = Value(std::move(value_object));
2990 } break;
2991 case json::value_t::array: {
2992 Value::Array value_array;
2993 value_array.reserve(o.size());
2994 for (auto it = o.begin(); it != o.end(); it++) {
2995 Value entry;
2996 ParseJsonAsValue(&entry, it.value());
2997 if (entry.Type() != NULL_TYPE)
2998 value_array.emplace_back(std::move(entry));
2999 }
3000 if (value_array.size() > 0) val = Value(std::move(value_array));
3001 } break;
3002 case json::value_t::string:
3003 val = Value(o.get<std::string>());
3004 break;
3005 case json::value_t::boolean:
3006 val = Value(o.get<bool>());
3007 break;
3008 case json::value_t::number_integer:
3009 case json::value_t::number_unsigned:
3010 val = Value(static_cast<int>(o.get<int64_t>()));
3011 break;
3012 case json::value_t::number_float:
3013 val = Value(o.get<double>());
3014 break;
3015 case json::value_t::null:
3016 case json::value_t::discarded:
3017 // default:
3018 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003019 }
3020#endif
3021 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003022
Selmar Kokfa7022f2018-04-04 18:10:20 +02003023 return val.Type() != NULL_TYPE;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003024}
3025
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003026static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003027 json_const_iterator it;
3028 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003029 return false;
3030 }
3031
jrkooncecba5d6c2019-08-29 11:26:22 -05003032 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003033}
3034
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003035static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003036 const std::string &property,
3037 const bool required,
3038 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003039 json_const_iterator it;
3040 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003041 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003042 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003043 (*err) += "'" + property + "' property is missing";
3044 if (!parent_node.empty()) {
3045 (*err) += " in " + parent_node;
3046 }
3047 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003048 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003049 }
3050 return false;
3051 }
3052
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003053 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003054
3055 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003056 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003057#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003058 isBoolean = value.IsBool();
3059 if (isBoolean) {
3060 boolValue = value.GetBool();
3061 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003062#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003063 isBoolean = value.is_boolean();
3064 if (isBoolean) {
3065 boolValue = value.get<bool>();
3066 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003067#endif
3068 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003069 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003070 if (err) {
3071 (*err) += "'" + property + "' property is not a bool type.\n";
3072 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003073 }
3074 return false;
3075 }
3076
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003077 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003078 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003079 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003080
3081 return true;
3082}
3083
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003084static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3085 const std::string &property,
3086 const bool required,
3087 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003088 json_const_iterator it;
3089 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003090 if (required) {
3091 if (err) {
3092 (*err) += "'" + property + "' property is missing";
3093 if (!parent_node.empty()) {
3094 (*err) += " in " + parent_node;
3095 }
3096 (*err) += ".\n";
3097 }
3098 }
3099 return false;
3100 }
3101
jrkooncecba5d6c2019-08-29 11:26:22 -05003102 int intValue;
3103 bool isInt = GetInt(GetValue(it), intValue);
3104 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003105 if (required) {
3106 if (err) {
3107 (*err) += "'" + property + "' property is not an integer type.\n";
3108 }
3109 }
3110 return false;
3111 }
3112
3113 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003114 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003115 }
3116
3117 return true;
3118}
3119
3120static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3121 const std::string &property,
3122 const bool required,
3123 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003124 json_const_iterator it;
3125 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003126 if (required) {
3127 if (err) {
3128 (*err) += "'" + property + "' property is missing";
3129 if (!parent_node.empty()) {
3130 (*err) += " in " + parent_node;
3131 }
3132 (*err) += ".\n";
3133 }
3134 }
3135 return false;
3136 }
3137
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003138 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003139
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003140 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003141 bool isUValue;
3142#ifdef TINYGLTF_USE_RAPIDJSON
3143 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003144 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003145 uValue = value.GetUint();
3146 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003147 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003148 uValue = value.GetUint64();
3149 isUValue = true;
3150 }
3151#else
3152 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003153 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003154 uValue = value.get<size_t>();
3155 }
3156#endif
3157 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003158 if (required) {
3159 if (err) {
3160 (*err) += "'" + property + "' property is not a positive integer.\n";
3161 }
3162 }
3163 return false;
3164 }
3165
3166 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003167 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003168 }
3169
3170 return true;
3171}
3172
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003173static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003174 const std::string &property,
3175 const bool required,
3176 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003177 json_const_iterator it;
3178
3179 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003180 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003181 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003182 (*err) += "'" + property + "' property is missing";
3183 if (!parent_node.empty()) {
3184 (*err) += " in " + parent_node;
3185 }
3186 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003187 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003188 }
3189 return false;
3190 }
3191
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003192 double numberValue;
3193 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003194
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003195 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003196 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003197 if (err) {
3198 (*err) += "'" + property + "' property is not a number type.\n";
3199 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003200 }
3201 return false;
3202 }
3203
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003204 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003205 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003206 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003207
3208 return true;
3209}
3210
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003211static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003212 const json &o, const std::string &property,
3213 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003214 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003215 json_const_iterator it;
3216 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003217 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003218 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003219 (*err) += "'" + property + "' property is missing";
3220 if (!parent_node.empty()) {
3221 (*err) += " in " + parent_node;
3222 }
3223 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003224 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003225 }
3226 return false;
3227 }
3228
jrkooncecba5d6c2019-08-29 11:26:22 -05003229 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003230 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003231 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003232 (*err) += "'" + property + "' property is not an array";
3233 if (!parent_node.empty()) {
3234 (*err) += " in " + parent_node;
3235 }
3236 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003237 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003238 }
3239 return false;
3240 }
3241
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003242 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003243 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003244 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003245 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003246 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003247 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003248 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003249 if (err) {
3250 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003251 if (!parent_node.empty()) {
3252 (*err) += " in " + parent_node;
3253 }
3254 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003255 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003256 }
3257 return false;
3258 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003259 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003260 }
3261
3262 return true;
3263}
3264
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003265static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3266 const json &o,
3267 const std::string &property,
3268 bool required,
3269 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003270 json_const_iterator it;
3271 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003272 if (required) {
3273 if (err) {
3274 (*err) += "'" + property + "' property is missing";
3275 if (!parent_node.empty()) {
3276 (*err) += " in " + parent_node;
3277 }
3278 (*err) += ".\n";
3279 }
3280 }
3281 return false;
3282 }
3283
jrkooncecba5d6c2019-08-29 11:26:22 -05003284 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003285 if (required) {
3286 if (err) {
3287 (*err) += "'" + property + "' property is not an array";
3288 if (!parent_node.empty()) {
3289 (*err) += " in " + parent_node;
3290 }
3291 (*err) += ".\n";
3292 }
3293 }
3294 return false;
3295 }
3296
3297 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003298 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003299 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003300 int numberValue;
3301 bool isNumber = GetInt(*i, numberValue);
3302 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003303 if (required) {
3304 if (err) {
3305 (*err) += "'" + property + "' property is not an integer type.\n";
3306 if (!parent_node.empty()) {
3307 (*err) += " in " + parent_node;
3308 }
3309 (*err) += ".\n";
3310 }
3311 }
3312 return false;
3313 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003314 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003315 }
3316
3317 return true;
3318}
3319
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003320static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003321 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003322 const std::string &property, bool required,
3323 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003324 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003325 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003326 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003327 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003328 (*err) += "'" + property + "' property is missing";
3329 if (parent_node.empty()) {
3330 (*err) += ".\n";
3331 } else {
3332 (*err) += " in `" + parent_node + "'.\n";
3333 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003334 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003335 }
3336 return false;
3337 }
3338
jrkooncecba5d6c2019-08-29 11:26:22 -05003339 std::string strValue;
3340 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003341 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003342 if (err) {
3343 (*err) += "'" + property + "' property is not a string type.\n";
3344 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003345 }
3346 return false;
3347 }
3348
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003349 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003350 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003351 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003352
3353 return true;
3354}
3355
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003356static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3357 std::string *err, const json &o,
3358 const std::string &property,
3359 bool required,
3360 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003361 json_const_iterator it;
3362 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003363 if (required) {
3364 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003365 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003366 (*err) +=
3367 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003368 } else {
3369 (*err) += "'" + property + "' property is missing.\n";
3370 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003371 }
3372 }
3373 return false;
3374 }
3375
jrkooncecba5d6c2019-08-29 11:26:22 -05003376 const json &dict = GetValue(it);
3377
Luke San Antonio19894c72016-06-14 21:19:51 -04003378 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003379 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003380 if (required) {
3381 if (err) {
3382 (*err) += "'" + property + "' property is not an object.\n";
3383 }
3384 }
3385 return false;
3386 }
3387
3388 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003389
jrkooncecba5d6c2019-08-29 11:26:22 -05003390 json_const_iterator dictIt(ObjectBegin(dict));
3391 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003392
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003393 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003394 int intVal;
3395 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003396 if (required) {
3397 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003398 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003399 }
3400 }
3401 return false;
3402 }
3403
3404 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003405 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003406 }
3407 return true;
3408}
3409
Syoyo Fujita5b407452017-06-04 17:42:41 +09003410static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003411 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003412 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003413 json_const_iterator it;
3414 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003415 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003416 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003417 (*err) += "'" + property + "' property is missing. \n'";
3418 }
3419 }
3420 return false;
3421 }
3422
jrkooncecba5d6c2019-08-29 11:26:22 -05003423 const json &obj = GetValue(it);
3424
3425 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003426 if (required) {
3427 if (err) {
3428 (*err) += "'" + property + "' property is not a JSON object.\n";
3429 }
3430 }
3431 return false;
3432 }
3433
3434 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003435
jrkooncecba5d6c2019-08-29 11:26:22 -05003436 json_const_iterator it2(ObjectBegin(obj));
3437 json_const_iterator itEnd(ObjectEnd(obj));
3438 for (; it2 != itEnd; ++it2) {
3439 double numVal;
3440 if (GetNumber(GetValue(it2), numVal))
3441 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003442 }
3443
3444 return true;
3445}
3446
Selmar09d2ff12018-03-15 17:30:42 +01003447static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003448 const json &o, const std::string &prop,
3449 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003450 // A parameter value can either be a string or an array of either a boolean or
3451 // a number. Booleans of any kind aren't supported here. Granted, it
3452 // complicates the Parameter structure and breaks it semantically in the sense
3453 // that the client probably works off the assumption that if the string is
3454 // empty the vector is used, etc. Would a tagged union work?
3455 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3456 // Found string property.
3457 return true;
3458 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3459 false)) {
3460 // Found a number array.
3461 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003462 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
3463 return param->has_number_value = true;
Selmar09d2ff12018-03-15 17:30:42 +01003464 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3465 false)) {
3466 return true;
3467 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3468 return true;
3469 } else {
3470 if (required) {
3471 if (err) {
3472 (*err) += "parameter must be a string or number / number array.\n";
3473 }
3474 }
3475 return false;
3476 }
3477}
3478
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003479static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3480 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003481 (void)err;
3482
jrkooncecba5d6c2019-08-29 11:26:22 -05003483 json_const_iterator it;
3484 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003485 return false;
3486 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003487
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003488 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003489 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003490 return false;
3491 }
3492 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003493 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003494 json_const_iterator extEnd = ObjectEnd(obj);
3495 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003496 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003497 if (!IsObject(itObj)) continue;
3498 std::string key(GetKey(extIt));
3499 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003500 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003501 // create empty object so that an extension object is still of type
3502 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003503 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003504 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003505 }
Selmar09d2ff12018-03-15 17:30:42 +01003506 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003507 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003508 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003509 }
3510 return true;
3511}
3512
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003513static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3514 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003515 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3516 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3517 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003518 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003519
Selmar09d2ff12018-03-15 17:30:42 +01003520 ParseExtensionsProperty(&asset->extensions, err, o);
3521
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003522 // Unity exporter version is added as extra here
3523 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003524
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003525 if (store_original_json_for_extras_and_extensions) {
3526 {
3527 json_const_iterator it;
3528 if (FindMember(o, "extensions", it)) {
3529 asset->extensions_json_string = JsonToString(GetValue(it));
3530 }
3531 }
3532 {
3533 json_const_iterator it;
3534 if (FindMember(o, "extras", it)) {
3535 asset->extras_json_string = JsonToString(GetValue(it));
3536 }
3537 }
3538 }
3539
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003540 return true;
3541}
3542
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003543static bool ParseImage(Image *image, const int image_idx, std::string *err,
3544 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003545 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003546 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003547 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003548 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003549 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003550
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003551 // schema says oneOf [`bufferView`, `uri`]
3552 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003553 json_const_iterator it;
3554 bool hasBufferView = FindMember(o, "bufferView", it);
3555 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003556
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003557 ParseStringProperty(&image->name, err, o, "name", false);
3558
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003559 if (hasBufferView && hasURI) {
3560 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003561 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003562 (*err) +=
3563 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003564 "defined for image[" +
3565 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003566 }
3567 return false;
3568 }
3569
3570 if (!hasBufferView && !hasURI) {
3571 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003572 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3573 std::to_string(image_idx) + "] name = \"" + image->name +
3574 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003575 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003576 return false;
3577 }
3578
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003579 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003580 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003581
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003582 if (store_original_json_for_extras_and_extensions) {
3583 {
3584 json_const_iterator eit;
3585 if (FindMember(o, "extensions", eit)) {
3586 image->extensions_json_string = JsonToString(GetValue(eit));
3587 }
3588 }
3589 {
3590 json_const_iterator eit;
3591 if (FindMember(o, "extras", eit)) {
3592 image->extras_json_string = JsonToString(GetValue(eit));
3593 }
3594 }
3595 }
3596
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003597 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003598 int bufferView = -1;
3599 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003600 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003601 (*err) += "Failed to parse `bufferView` for image[" +
3602 std::to_string(image_idx) + "] name = \"" + image->name +
3603 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003604 }
3605 return false;
3606 }
3607
3608 std::string mime_type;
3609 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3610
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003611 int width = 0;
3612 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003613
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003614 int height = 0;
3615 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003616
3617 // Just only save some information here. Loading actual image data from
3618 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003619 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003620 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003621 image->width = width;
3622 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003623
3624 return true;
3625 }
3626
Syoyo Fujita246654a2018-03-21 20:32:22 +09003627 // Parse URI & Load image data.
3628
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003629 std::string uri;
3630 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003631 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3632 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003633 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3634 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003635 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003636 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003637 }
3638
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003639 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003640
Syoyo Fujita246654a2018-03-21 20:32:22 +09003641 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003642 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003643 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003644 (*err) += "Failed to decode 'uri' for image[" +
3645 std::to_string(image_idx) + "] name = [" + image->name +
3646 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003647 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003648 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003649 }
3650 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003651 // Assume external file
3652 // Keep texture path (for textures that cannot be decoded)
3653 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003654#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003655 return true;
Selmar67af3c92018-03-16 11:48:19 +01003656#endif
Selmar Koke3b3fa92018-08-22 19:04:21 +02003657 if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003658 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003659 (*warn) += "Failed to load external 'uri' for image[" +
3660 std::to_string(image_idx) + "] name = [" + image->name +
3661 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003662 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003663 // If the image cannot be loaded, keep uri as image->uri.
3664 return true;
3665 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003666
Syoyo Fujita246654a2018-03-21 20:32:22 +09003667 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003668 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003669 (*warn) += "Image data is empty for image[" +
3670 std::to_string(image_idx) + "] name = [" + image->name +
3671 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003672 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003673 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003674 }
3675 }
3676
Squareysff644d82018-03-13 22:36:18 +01003677 if (*LoadImageData == nullptr) {
3678 if (err) {
3679 (*err) += "No LoadImageData callback specified.\n";
3680 }
3681 return false;
3682 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003683 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003684 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003685}
3686
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003687static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003688 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003689 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003690 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003691 int sampler = -1;
3692 int source = -1;
3693 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003694
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003695 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003696
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003697 texture->sampler = sampler;
3698 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003699
Selmar Kokfa7022f2018-04-04 18:10:20 +02003700 ParseExtensionsProperty(&texture->extensions, err, o);
3701 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003702
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003703 if (store_original_json_for_extras_and_extensions) {
3704 {
3705 json_const_iterator it;
3706 if (FindMember(o, "extensions", it)) {
3707 texture->extensions_json_string = JsonToString(GetValue(it));
3708 }
3709 }
3710 {
3711 json_const_iterator it;
3712 if (FindMember(o, "extras", it)) {
3713 texture->extras_json_string = JsonToString(GetValue(it));
3714 }
3715 }
3716 }
3717
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003718 ParseStringProperty(&texture->name, err, o, "name", false);
3719
Syoyo Fujitabde70212016-02-07 17:38:17 +09003720 return true;
3721}
3722
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003723static bool ParseTextureInfo(
3724 TextureInfo *texinfo, std::string *err, const json &o,
3725 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003726 if (texinfo == nullptr) {
3727 return false;
3728 }
3729
3730 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3731 /* required */ true, "TextureInfo")) {
3732 return false;
3733 }
3734
3735 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3736
3737 ParseExtensionsProperty(&texinfo->extensions, err, o);
3738 ParseExtrasProperty(&texinfo->extras, o);
3739
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003740 if (store_original_json_for_extras_and_extensions) {
3741 {
3742 json_const_iterator it;
3743 if (FindMember(o, "extensions", it)) {
3744 texinfo->extensions_json_string = JsonToString(GetValue(it));
3745 }
3746 }
3747 {
3748 json_const_iterator it;
3749 if (FindMember(o, "extras", it)) {
3750 texinfo->extras_json_string = JsonToString(GetValue(it));
3751 }
3752 }
3753 }
3754
Syoyo Fujita046400b2019-07-24 19:26:48 +09003755 return true;
3756}
3757
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003758static bool ParseNormalTextureInfo(
3759 NormalTextureInfo *texinfo, std::string *err, const json &o,
3760 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003761 if (texinfo == nullptr) {
3762 return false;
3763 }
3764
3765 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3766 /* required */ true, "NormalTextureInfo")) {
3767 return false;
3768 }
3769
3770 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3771 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
3772
3773 ParseExtensionsProperty(&texinfo->extensions, err, o);
3774 ParseExtrasProperty(&texinfo->extras, o);
3775
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003776 if (store_original_json_for_extras_and_extensions) {
3777 {
3778 json_const_iterator it;
3779 if (FindMember(o, "extensions", it)) {
3780 texinfo->extensions_json_string = JsonToString(GetValue(it));
3781 }
3782 }
3783 {
3784 json_const_iterator it;
3785 if (FindMember(o, "extras", it)) {
3786 texinfo->extras_json_string = JsonToString(GetValue(it));
3787 }
3788 }
3789 }
3790
Syoyo Fujita046400b2019-07-24 19:26:48 +09003791 return true;
3792}
3793
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003794static bool ParseOcclusionTextureInfo(
3795 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
3796 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003797 if (texinfo == nullptr) {
3798 return false;
3799 }
3800
3801 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3802 /* required */ true, "NormalTextureInfo")) {
3803 return false;
3804 }
3805
3806 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3807 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
3808
3809 ParseExtensionsProperty(&texinfo->extensions, err, o);
3810 ParseExtrasProperty(&texinfo->extras, o);
3811
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003812 if (store_original_json_for_extras_and_extensions) {
3813 {
3814 json_const_iterator it;
3815 if (FindMember(o, "extensions", it)) {
3816 texinfo->extensions_json_string = JsonToString(GetValue(it));
3817 }
3818 }
3819 {
3820 json_const_iterator it;
3821 if (FindMember(o, "extras", it)) {
3822 texinfo->extras_json_string = JsonToString(GetValue(it));
3823 }
3824 }
3825 }
3826
Syoyo Fujita046400b2019-07-24 19:26:48 +09003827 return true;
3828}
3829
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003830static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003831 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003832 FsCallbacks *fs, const std::string &basedir,
3833 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09003834 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003835 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003836 size_t byteLength;
3837 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
3838 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003839 return false;
3840 }
3841
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00003842 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05003843 buffer->uri.clear();
3844 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003845
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00003846 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05003847 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003848 if (err) {
3849 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
3850 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00003851 }
3852
jrkooncecba5d6c2019-08-29 11:26:22 -05003853 json_const_iterator type;
3854 if (FindMember(o, "type", type)) {
3855 std::string typeStr;
3856 if (GetString(GetValue(type), typeStr)) {
3857 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003858 // buffer.type = "arraybuffer";
3859 }
3860 }
3861 }
3862
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003863 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003864 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05003865 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09003866 // First try embedded data URI.
3867 if (IsDataURI(buffer->uri)) {
3868 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003869 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003870 true)) {
3871 if (err) {
3872 (*err) +=
3873 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
3874 }
3875 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09003876 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003877 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09003878 // External .bin file.
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09003879 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003880 buffer->uri, basedir, true, byteLength, true,
3881 fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02003882 return false;
3883 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09003884 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003885 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09003886 // load data from (embedded) binary data
3887
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09003888 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09003889 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09003890 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003891 }
3892 return false;
3893 }
3894
3895 if (byteLength > bin_size) {
3896 if (err) {
3897 std::stringstream ss;
3898 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09003899 "`byteLength' = "
3900 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003901 (*err) += ss.str();
3902 }
3903 return false;
3904 }
3905
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00003906 // Read buffer data
3907 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09003908 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09003909 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003910
3911 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05003912 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02003913 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003914 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
3915 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003916 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05003917 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003918 }
3919 return false;
3920 }
3921 } else {
3922 // Assume external .bin file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003923 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003924 basedir, true, byteLength, true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003925 return false;
3926 }
3927 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003928 }
3929
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003930 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003931
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003932 ParseExtensionsProperty(&buffer->extensions, err, o);
3933 ParseExtrasProperty(&buffer->extras, o);
3934
3935 if (store_original_json_for_extras_and_extensions) {
3936 {
3937 json_const_iterator it;
3938 if (FindMember(o, "extensions", it)) {
3939 buffer->extensions_json_string = JsonToString(GetValue(it));
3940 }
3941 }
3942 {
3943 json_const_iterator it;
3944 if (FindMember(o, "extras", it)) {
3945 buffer->extras_json_string = JsonToString(GetValue(it));
3946 }
3947 }
3948 }
3949
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003950 return true;
3951}
3952
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003953static bool ParseBufferView(
3954 BufferView *bufferView, std::string *err, const json &o,
3955 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003956 int buffer = -1;
3957 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003958 return false;
3959 }
3960
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003961 size_t byteOffset = 0;
3962 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003963
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003964 size_t byteLength = 1;
3965 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
3966 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003967 return false;
3968 }
3969
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003970 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003971 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003972 // Spec says: When byteStride of referenced bufferView is not defined, it
3973 // means that accessor elements are tightly packed, i.e., effective stride
3974 // equals the size of the element.
3975 // We cannot determine the actual byteStride until Accessor are parsed, thus
3976 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
3977 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003978 }
3979
3980 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
3981 if (err) {
3982 std::stringstream ss;
3983 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
3984 "4 : "
3985 << byteStride << std::endl;
3986
3987 (*err) += ss.str();
3988 }
3989 return false;
3990 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003991
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003992 int target = 0;
3993 ParseIntegerProperty(&target, err, o, "target", false);
3994 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
3995 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003996 // OK
3997 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003998 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003999 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004000 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004001
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004002 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004003
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004004 ParseExtensionsProperty(&bufferView->extensions, err, o);
4005 ParseExtrasProperty(&bufferView->extras, o);
4006
4007 if (store_original_json_for_extras_and_extensions) {
4008 {
4009 json_const_iterator it;
4010 if (FindMember(o, "extensions", it)) {
4011 bufferView->extensions_json_string = JsonToString(GetValue(it));
4012 }
4013 }
4014 {
4015 json_const_iterator it;
4016 if (FindMember(o, "extras", it)) {
4017 bufferView->extras_json_string = JsonToString(GetValue(it));
4018 }
4019 }
4020 }
4021
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004022 bufferView->buffer = buffer;
4023 bufferView->byteOffset = byteOffset;
4024 bufferView->byteLength = byteLength;
4025 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004026 return true;
4027}
4028
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004029static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4030 const json &o) {
4031 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004032
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004033 int count = 0;
4034 ParseIntegerProperty(&count, err, o, "count", true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004035
jrkooncecba5d6c2019-08-29 11:26:22 -05004036 json_const_iterator indices_iterator;
4037 json_const_iterator values_iterator;
4038 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004039 (*err) = "the sparse object of this accessor doesn't have indices";
4040 return false;
4041 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004042
jrkooncecba5d6c2019-08-29 11:26:22 -05004043 if (!FindMember(o, "values", values_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004044 (*err) = "the sparse object ob ths accessor doesn't have values";
4045 return false;
4046 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004047
jrkooncecba5d6c2019-08-29 11:26:22 -05004048 const json &indices_obj = GetValue(indices_iterator);
4049 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004050
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004051 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
4052 ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
4053 true);
4054 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
4055 true);
4056 ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
4057 true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004058
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004059 int values_buffer_view = 0, values_byte_offset = 0;
4060 ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
4061 true);
4062 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
4063 true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004064
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004065 accessor->sparse.count = count;
4066 accessor->sparse.indices.bufferView = indices_buffer_view;
4067 accessor->sparse.indices.byteOffset = indices_byte_offset;
4068 accessor->sparse.indices.componentType = component_type;
4069 accessor->sparse.values.bufferView = values_buffer_view;
4070 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004071
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004072 // todo check theses values
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004073
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004074 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004075}
4076
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004077static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4078 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004079 int bufferView = -1;
4080 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004081
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004082 size_t byteOffset = 0;
4083 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004084
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004085 bool normalized = false;
4086 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4087
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004088 size_t componentType = 0;
4089 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4090 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004091 return false;
4092 }
4093
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004094 size_t count = 0;
4095 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004096 return false;
4097 }
4098
4099 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004100 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004101 return false;
4102 }
4103
4104 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004105 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004106 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004107 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004108 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004109 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004110 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004111 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004112 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004113 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004114 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004115 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004116 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004117 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004118 } else {
4119 std::stringstream ss;
4120 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004121 if (err) {
4122 (*err) += ss.str();
4123 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004124 return false;
4125 }
4126
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004127 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004128
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004129 accessor->minValues.clear();
4130 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004131 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4132 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004133
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004134 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4135 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004136
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004137 accessor->count = count;
4138 accessor->bufferView = bufferView;
4139 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004140 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004141 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004142 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4143 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004144 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004145 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004146 } else {
4147 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004148 ss << "Invalid `componentType` in accessor. Got " << componentType
4149 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004150 if (err) {
4151 (*err) += ss.str();
4152 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004153 return false;
4154 }
4155 }
4156
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004157 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004158 ParseExtrasProperty(&(accessor->extras), o);
4159
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004160 if (store_original_json_for_extras_and_extensions) {
4161 {
4162 json_const_iterator it;
4163 if (FindMember(o, "extensions", it)) {
4164 accessor->extensions_json_string = JsonToString(GetValue(it));
4165 }
4166 }
4167 {
4168 json_const_iterator it;
4169 if (FindMember(o, "extras", it)) {
4170 accessor->extras_json_string = JsonToString(GetValue(it));
4171 }
4172 }
4173 }
4174
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004175 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004176 json_const_iterator iterator;
4177 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004178 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004179 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004180 }
4181
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004182 return true;
4183}
4184
Alex Wood7319db72019-01-24 15:38:16 -05004185#ifdef TINYGLTF_ENABLE_DRACO
4186
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004187static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4188 std::vector<uint8_t> &outBuffer) {
4189 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004190 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004191 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4192 outBuffer.size());
4193 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004194 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004195 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4196 const draco::Mesh::Face &face = mesh->face(f);
4197 if (componentSize == 2) {
4198 uint16_t indices[3] = {(uint16_t)face[0].value(),
4199 (uint16_t)face[1].value(),
4200 (uint16_t)face[2].value()};
4201 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4202 faceStride);
4203 } else {
4204 uint8_t indices[3] = {(uint8_t)face[0].value(),
4205 (uint8_t)face[1].value(),
4206 (uint8_t)face[2].value()};
4207 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4208 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004209 }
4210 }
4211 }
4212}
4213
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004214template <typename T>
4215static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4216 const draco::PointAttribute *pAttribute,
4217 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004218 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004219 T values[4] = {0, 0, 0, 0};
4220 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004221 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004222 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4223 values))
Alex Wood7319db72019-01-24 15:38:16 -05004224 return false;
4225
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004226 memcpy(outBuffer.data() + byteOffset, &values[0],
4227 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004228 byteOffset += sizeof(T) * pAttribute->num_components();
4229 }
4230
4231 return true;
4232}
4233
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004234static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4235 const draco::PointAttribute *pAttribute,
4236 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004237 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004238 switch (componentType) {
4239 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4240 decodeResult =
4241 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4242 break;
4243 case TINYGLTF_COMPONENT_TYPE_BYTE:
4244 decodeResult =
4245 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4246 break;
4247 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4248 decodeResult =
4249 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4250 break;
4251 case TINYGLTF_COMPONENT_TYPE_SHORT:
4252 decodeResult =
4253 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4254 break;
4255 case TINYGLTF_COMPONENT_TYPE_INT:
4256 decodeResult =
4257 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4258 break;
4259 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4260 decodeResult =
4261 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4262 break;
4263 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4264 decodeResult =
4265 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4266 break;
4267 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4268 decodeResult =
4269 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4270 break;
4271 default:
4272 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004273 }
4274
4275 return decodeResult;
4276}
4277
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004278static bool ParseDracoExtension(Primitive *primitive, Model *model,
4279 std::string *err,
4280 const Value &dracoExtensionValue) {
Alex Wood7319db72019-01-24 15:38:16 -05004281 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004282 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004283 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004284 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004285
4286 auto attributesObject = attributesValue.Get<Value::Object>();
4287 int bufferView = bufferViewValue.Get<int>();
4288
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004289 BufferView &view = model->bufferViews[bufferView];
4290 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004291 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004292 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004293 view.dracoDecoded = true;
4294
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004295 const char *bufferViewData =
4296 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004297 size_t bufferViewSize = view.byteLength;
4298
4299 // decode draco
4300 draco::DecoderBuffer decoderBuffer;
4301 decoderBuffer.Init(bufferViewData, bufferViewSize);
4302 draco::Decoder decoder;
4303 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4304 if (!decodeResult.ok()) {
4305 return false;
4306 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004307 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004308
4309 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004310 if (primitive->indices >= 0) {
4311 int32_t componentSize = GetComponentSizeInBytes(
4312 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004313 Buffer decodedIndexBuffer;
4314 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4315
4316 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4317
4318 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4319
4320 BufferView decodedIndexBufferView;
4321 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004322 decodedIndexBufferView.byteLength =
4323 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004324 decodedIndexBufferView.byteOffset = 0;
4325 decodedIndexBufferView.byteStride = 0;
4326 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4327 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4328
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004329 model->accessors[primitive->indices].bufferView =
4330 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004331 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004332 }
4333
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004334 for (const auto &attribute : attributesObject) {
4335 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004336 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004337 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004338
4339 int dracoAttributeIndex = attribute.second.Get<int>();
4340 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
4341 const auto pBuffer = pAttribute->buffer();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004342 const auto componentType =
4343 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004344
4345 // Create a new buffer for this decoded buffer
4346 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004347 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4348 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004349 decodedBuffer.data.resize(bufferSize);
4350
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004351 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4352 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004353 return false;
4354
4355 model->buffers.emplace_back(std::move(decodedBuffer));
4356
4357 BufferView decodedBufferView;
4358 decodedBufferView.buffer = int(model->buffers.size() - 1);
4359 decodedBufferView.byteLength = bufferSize;
4360 decodedBufferView.byteOffset = pAttribute->byte_offset();
4361 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004362 decodedBufferView.target = primitive->indices >= 0
4363 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4364 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004365 model->bufferViews.emplace_back(std::move(decodedBufferView));
4366
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004367 model->accessors[primitiveAttribute->second].bufferView =
4368 int(model->bufferViews.size() - 1);
4369 model->accessors[primitiveAttribute->second].count =
4370 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004371 }
4372
4373 return true;
4374}
4375#endif
4376
4377static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004378 const json &o,
4379 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004380 int material = -1;
4381 ParseIntegerProperty(&material, err, o, "material", false);
4382 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004383
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004384 int mode = TINYGLTF_MODE_TRIANGLES;
4385 ParseIntegerProperty(&mode, err, o, "mode", false);
4386 primitive->mode = mode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004387
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004388 int indices = -1;
4389 ParseIntegerProperty(&indices, err, o, "indices", false);
4390 primitive->indices = indices;
4391 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4392 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004393 return false;
4394 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004395
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004396 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004397 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004398 if (FindMember(o, "targets", targetsObject) &&
4399 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004400 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4401 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4402 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004403 std::map<std::string, int> targetAttribues;
4404
jrkooncecba5d6c2019-08-29 11:26:22 -05004405 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004406 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004407 json_const_iterator dictIt(ObjectBegin(dict));
4408 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004409
jrkooncecba5d6c2019-08-29 11:26:22 -05004410 for (; dictIt != dictItEnd; ++dictIt) {
4411 int iVal;
4412 if (GetInt(GetValue(dictIt), iVal))
4413 targetAttribues[GetKey(dictIt)] = iVal;
4414 }
4415 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004416 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004417 }
4418 }
4419
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004420 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004421 ParseExtensionsProperty(&primitive->extensions, err, o);
4422
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004423 if (store_original_json_for_extras_and_extensions) {
4424 {
4425 json_const_iterator it;
4426 if (FindMember(o, "extensions", it)) {
4427 primitive->extensions_json_string = JsonToString(GetValue(it));
4428 }
4429 }
4430 {
4431 json_const_iterator it;
4432 if (FindMember(o, "extras", it)) {
4433 primitive->extras_json_string = JsonToString(GetValue(it));
4434 }
4435 }
4436 }
4437
Alex Wood7319db72019-01-24 15:38:16 -05004438#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004439 auto dracoExtension =
4440 primitive->extensions.find("KHR_draco_mesh_compression");
4441 if (dracoExtension != primitive->extensions.end()) {
4442 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004443 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004444#else
4445 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004446#endif
4447
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004448 return true;
4449}
4450
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004451static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4452 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004453 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004454
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004455 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004456 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004457 if (FindMember(o, "primitives", primObject) &&
4458 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004459 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4460 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4461 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004462 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004463 if (ParsePrimitive(&primitive, model, err, *i,
4464 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004465 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004466 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004467 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004468 }
4469 }
4470
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004471 // Should probably check if has targets and if dimensions fit
4472 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4473
Selmar09d2ff12018-03-15 17:30:42 +01004474 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004475 ParseExtrasProperty(&(mesh->extras), o);
4476
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004477 if (store_original_json_for_extras_and_extensions) {
4478 {
4479 json_const_iterator it;
4480 if (FindMember(o, "extensions", it)) {
4481 mesh->extensions_json_string = JsonToString(GetValue(it));
4482 }
4483 }
4484 {
4485 json_const_iterator it;
4486 if (FindMember(o, "extras", it)) {
4487 mesh->extras_json_string = JsonToString(GetValue(it));
4488 }
4489 }
4490 }
4491
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004492 return true;
4493}
4494
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004495static bool ParseNode(Node *node, std::string *err, const json &o,
4496 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004497 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004498
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004499 int skin = -1;
4500 ParseIntegerProperty(&skin, err, o, "skin", false);
4501 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004502
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004503 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004504 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004505 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4506 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4507 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4508 }
4509
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004510 int camera = -1;
4511 ParseIntegerProperty(&camera, err, o, "camera", false);
4512 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004513
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004514 int mesh = -1;
4515 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4516 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004517
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004518 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004519 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004520
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004521 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4522
Selmar09d2ff12018-03-15 17:30:42 +01004523 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004524 ParseExtrasProperty(&(node->extras), o);
4525
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004526 if (store_original_json_for_extras_and_extensions) {
4527 {
4528 json_const_iterator it;
4529 if (FindMember(o, "extensions", it)) {
4530 node->extensions_json_string = JsonToString(GetValue(it));
4531 }
4532 }
4533 {
4534 json_const_iterator it;
4535 if (FindMember(o, "extras", it)) {
4536 node->extras_json_string = JsonToString(GetValue(it));
4537 }
4538 }
4539 }
4540
Emanuel Schrade186322b2017-11-06 11:14:41 +01004541 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004542}
4543
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004544static bool ParsePbrMetallicRoughness(
4545 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4546 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004547 if (pbr == nullptr) {
4548 return false;
4549 }
4550
4551 std::vector<double> baseColorFactor;
4552 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4553 /* required */ false)) {
4554 if (baseColorFactor.size() != 4) {
4555 if (err) {
4556 (*err) +=
4557 "Array length of `baseColorFactor` parameter in "
4558 "pbrMetallicRoughness must be 4, but got " +
4559 std::to_string(baseColorFactor.size()) + "\n";
4560 }
4561 return false;
4562 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004563 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004564 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004565
4566 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004567 json_const_iterator it;
4568 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004569 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4570 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004571 }
4572 }
4573
4574 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004575 json_const_iterator it;
4576 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004577 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4578 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004579 }
4580 }
4581
4582 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4583 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4584
4585 ParseExtensionsProperty(&pbr->extensions, err, o);
4586 ParseExtrasProperty(&pbr->extras, o);
4587
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004588 if (store_original_json_for_extras_and_extensions) {
4589 {
4590 json_const_iterator it;
4591 if (FindMember(o, "extensions", it)) {
4592 pbr->extensions_json_string = JsonToString(GetValue(it));
4593 }
4594 }
4595 {
4596 json_const_iterator it;
4597 if (FindMember(o, "extras", it)) {
4598 pbr->extras_json_string = JsonToString(GetValue(it));
4599 }
4600 }
4601 }
4602
Syoyo Fujita046400b2019-07-24 19:26:48 +09004603 return true;
4604}
4605
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004606static bool ParseMaterial(Material *material, std::string *err, const json &o,
4607 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004608 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4609
Syoyo Fujitaff515702019-08-24 16:29:14 +09004610 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4611 "emissiveFactor",
4612 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004613 if (material->emissiveFactor.size() != 3) {
4614 if (err) {
4615 (*err) +=
4616 "Array length of `emissiveFactor` parameter in "
4617 "material must be 3, but got " +
4618 std::to_string(material->emissiveFactor.size()) + "\n";
4619 }
4620 return false;
4621 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004622 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004623 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004624 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004625 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004626
4627 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4628 /* required */ false);
4629 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4630 /* required */ false);
4631 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4632 /* required */ false);
4633
4634 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004635 json_const_iterator it;
4636 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004637 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004638 GetValue(it),
4639 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004640 }
4641 }
4642
4643 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004644 json_const_iterator it;
4645 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004646 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4647 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004648 }
4649 }
4650
4651 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004652 json_const_iterator it;
4653 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004654 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4655 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004656 }
4657 }
4658
4659 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004660 json_const_iterator it;
4661 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004662 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4663 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004664 }
4665 }
4666
4667 // Old code path. For backward compatibility, we still store material values
4668 // as Parameter. This will create duplicated information for
4669 // example(pbrMetallicRoughness), but should be neglible in terms of memory
4670 // consumption.
4671 // TODO(syoyo): Remove in the next major release.
4672 material->values.clear();
4673 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004674
jrkooncecba5d6c2019-08-29 11:26:22 -05004675 json_const_iterator it(ObjectBegin(o));
4676 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004677
jrkooncecba5d6c2019-08-29 11:26:22 -05004678 for (; it != itEnd; ++it) {
4679 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004680 if (key == "pbrMetallicRoughness") {
4681 if (IsObject(GetValue(it))) {
4682 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004683
jrkoonce06c30c42019-09-03 15:56:48 -05004684 json_const_iterator itVal(ObjectBegin(values_object));
4685 json_const_iterator itValEnd(ObjectEnd(values_object));
4686
4687 for (; itVal != itValEnd; ++itVal) {
4688 Parameter param;
4689 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004690 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004691 material->values.emplace(GetKey(itVal), std::move(param));
4692 }
4693 }
4694 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004695 } else if (key == "extensions" || key == "extras") {
4696 // done later, skip, otherwise poorly parsed contents will be saved in the
4697 // parametermap and serialized again later
4698 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004699 Parameter param;
4700 if (ParseParameterProperty(&param, err, o, key, false)) {
4701 // names of materials have already been parsed. Putting it in this map
4702 // doesn't correctly reflext the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004703 if (key != "name")
4704 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004705 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004706 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004707 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004708
Syoyo Fujita046400b2019-07-24 19:26:48 +09004709 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004710 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004711 ParseExtrasProperty(&(material->extras), o);
4712
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004713 if (store_original_json_for_extras_and_extensions) {
4714 {
4715 json_const_iterator eit;
4716 if (FindMember(o, "extensions", eit)) {
4717 material->extensions_json_string = JsonToString(GetValue(eit));
4718 }
4719 }
4720 {
4721 json_const_iterator eit;
4722 if (FindMember(o, "extras", eit)) {
4723 material->extras_json_string = JsonToString(GetValue(eit));
4724 }
4725 }
4726 }
4727
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004728 return true;
4729}
4730
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004731static bool ParseAnimationChannel(
4732 AnimationChannel *channel, std::string *err, const json &o,
4733 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004734 int samplerIndex = -1;
4735 int targetIndex = -1;
4736 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4737 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004738 if (err) {
4739 (*err) += "`sampler` field is missing in animation channels\n";
4740 }
4741 return false;
4742 }
4743
jrkooncecba5d6c2019-08-29 11:26:22 -05004744 json_const_iterator targetIt;
4745 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4746 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004747
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004748 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004749 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004750 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004751 }
4752 return false;
4753 }
4754
4755 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4756 true)) {
4757 if (err) {
4758 (*err) += "`path` field is missing in animation.channels.target\n";
4759 }
4760 return false;
4761 }
Selmar Kok973d9b32020-01-21 18:45:24 +01004762 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4763 if (store_original_json_for_extras_and_extensions) {
4764 json_const_iterator it;
4765 if (FindMember(target_object, "extensions", it)) {
4766 channel->target_extensions_json_string = JsonToString(GetValue(it));
4767 }
4768 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004769 }
4770
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004771 channel->sampler = samplerIndex;
4772 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004773
Selmar Kok4e2988e2019-08-16 14:08:08 +02004774 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004775 ParseExtrasProperty(&(channel->extras), o);
4776
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004777 if (store_original_json_for_extras_and_extensions) {
4778 {
4779 json_const_iterator it;
4780 if (FindMember(o, "extensions", it)) {
4781 channel->extensions_json_string = JsonToString(GetValue(it));
4782 }
4783 }
4784 {
4785 json_const_iterator it;
4786 if (FindMember(o, "extras", it)) {
4787 channel->extras_json_string = JsonToString(GetValue(it));
4788 }
4789 }
4790 }
4791
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004792 return true;
4793}
4794
4795static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004796 const json &o,
4797 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004798 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004799 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004800 if (FindMember(o, "channels", channelsIt) &&
4801 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004802 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
4803 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
4804 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004805 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004806 if (ParseAnimationChannel(
4807 &channel, err, *i,
4808 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004809 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05004810 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004811 }
4812 }
4813 }
4814 }
4815
4816 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004817 json_const_iterator samplerIt;
4818 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
4819 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004820
jrkooncecba5d6c2019-08-29 11:26:22 -05004821 json_const_array_iterator it = ArrayBegin(sampler_array);
4822 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004823
jrkooncecba5d6c2019-08-29 11:26:22 -05004824 for (; it != itEnd; ++it) {
4825 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004826
4827 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004828 int inputIndex = -1;
4829 int outputIndex = -1;
4830 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004831 if (err) {
4832 (*err) += "`input` field is missing in animation.sampler\n";
4833 }
4834 return false;
4835 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08004836 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
4837 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004838 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004839 if (err) {
4840 (*err) += "`output` field is missing in animation.sampler\n";
4841 }
4842 return false;
4843 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004844 sampler.input = inputIndex;
4845 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004846 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02004847 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004848
4849 if (store_original_json_for_extras_and_extensions) {
4850 {
4851 json_const_iterator eit;
4852 if (FindMember(o, "extensions", eit)) {
4853 sampler.extensions_json_string = JsonToString(GetValue(eit));
4854 }
4855 }
4856 {
4857 json_const_iterator eit;
4858 if (FindMember(o, "extras", eit)) {
4859 sampler.extras_json_string = JsonToString(GetValue(eit));
4860 }
4861 }
4862 }
4863
jrkooncecba5d6c2019-08-29 11:26:22 -05004864 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004865 }
4866 }
4867 }
4868
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004869 ParseStringProperty(&animation->name, err, o, "name", false);
4870
Selmar Kok4e2988e2019-08-16 14:08:08 +02004871 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004872 ParseExtrasProperty(&(animation->extras), o);
4873
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004874 if (store_original_json_for_extras_and_extensions) {
4875 {
4876 json_const_iterator it;
4877 if (FindMember(o, "extensions", it)) {
4878 animation->extensions_json_string = JsonToString(GetValue(it));
4879 }
4880 }
4881 {
4882 json_const_iterator it;
4883 if (FindMember(o, "extras", it)) {
4884 animation->extras_json_string = JsonToString(GetValue(it));
4885 }
4886 }
4887 }
4888
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004889 return true;
4890}
4891
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004892static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
4893 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09004894 ParseStringProperty(&sampler->name, err, o, "name", false);
4895
Syoyo Fujitaee179b22019-08-16 13:11:30 +09004896 int minFilter = -1;
4897 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004898 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
4899 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujitaee179b22019-08-16 13:11:30 +09004900 int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004901 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
4902 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
4903 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
4904 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujitaee179b22019-08-16 13:11:30 +09004905 ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
4906
4907 // TODO(syoyo): Check the value is alloed one.
4908 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09004909
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004910 sampler->minFilter = minFilter;
4911 sampler->magFilter = magFilter;
4912 sampler->wrapS = wrapS;
4913 sampler->wrapT = wrapT;
Syoyo Fujitaee179b22019-08-16 13:11:30 +09004914 sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09004915
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004916 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004917 ParseExtrasProperty(&(sampler->extras), o);
4918
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004919 if (store_original_json_for_extras_and_extensions) {
4920 {
4921 json_const_iterator it;
4922 if (FindMember(o, "extensions", it)) {
4923 sampler->extensions_json_string = JsonToString(GetValue(it));
4924 }
4925 }
4926 {
4927 json_const_iterator it;
4928 if (FindMember(o, "extras", it)) {
4929 sampler->extras_json_string = JsonToString(GetValue(it));
4930 }
4931 }
4932 }
4933
Syoyo Fujitac2615632016-06-19 21:56:06 +09004934 return true;
4935}
4936
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004937static bool ParseSkin(Skin *skin, std::string *err, const json &o,
4938 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004939 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004940
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004941 std::vector<int> joints;
4942 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004943 return false;
4944 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004945 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004946
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004947 int skeleton = -1;
4948 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
4949 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004950
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004951 int invBind = -1;
4952 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
4953 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004954
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004955 ParseExtensionsProperty(&(skin->extensions), err, o);
4956 ParseExtrasProperty(&(skin->extras), o);
4957
4958 if (store_original_json_for_extras_and_extensions) {
4959 {
4960 json_const_iterator it;
4961 if (FindMember(o, "extensions", it)) {
4962 skin->extensions_json_string = JsonToString(GetValue(it));
4963 }
4964 }
4965 {
4966 json_const_iterator it;
4967 if (FindMember(o, "extras", it)) {
4968 skin->extras_json_string = JsonToString(GetValue(it));
4969 }
4970 }
4971 }
4972
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004973 return true;
4974}
4975
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004976static bool ParsePerspectiveCamera(
4977 PerspectiveCamera *camera, std::string *err, const json &o,
4978 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09004979 double yfov = 0.0;
4980 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
4981 return false;
4982 }
4983
4984 double znear = 0.0;
4985 if (!ParseNumberProperty(&znear, err, o, "znear", true,
4986 "PerspectiveCamera")) {
4987 return false;
4988 }
4989
4990 double aspectRatio = 0.0; // = invalid
4991 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
4992 "PerspectiveCamera");
4993
4994 double zfar = 0.0; // = invalid
4995 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
4996
Selmar Kok31cb7f92018-10-03 15:39:05 +02004997 camera->aspectRatio = aspectRatio;
4998 camera->zfar = zfar;
4999 camera->yfov = yfov;
5000 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005001
Selmar09d2ff12018-03-15 17:30:42 +01005002 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005003 ParseExtrasProperty(&(camera->extras), o);
5004
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005005 if (store_original_json_for_extras_and_extensions) {
5006 {
5007 json_const_iterator it;
5008 if (FindMember(o, "extensions", it)) {
5009 camera->extensions_json_string = JsonToString(GetValue(it));
5010 }
5011 }
5012 {
5013 json_const_iterator it;
5014 if (FindMember(o, "extras", it)) {
5015 camera->extras_json_string = JsonToString(GetValue(it));
5016 }
5017 }
5018 }
5019
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005020 // TODO(syoyo): Validate parameter values.
5021
5022 return true;
5023}
5024
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005025static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5026 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005027 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5028 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005029
Johan Bowald52936a02019-07-17 09:06:45 +02005030 ParseExtensionsProperty(&light->extensions, err, o);
5031 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005032
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005033 if (store_original_json_for_extras_and_extensions) {
5034 {
5035 json_const_iterator it;
5036 if (FindMember(o, "extensions", it)) {
5037 light->extensions_json_string = JsonToString(GetValue(it));
5038 }
5039 }
5040 {
5041 json_const_iterator it;
5042 if (FindMember(o, "extras", it)) {
5043 light->extras_json_string = JsonToString(GetValue(it));
5044 }
5045 }
5046 }
5047
Johan Bowald52936a02019-07-17 09:06:45 +02005048 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005049
Johan Bowald52936a02019-07-17 09:06:45 +02005050 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005051}
5052
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005053static bool ParseOrthographicCamera(
5054 OrthographicCamera *camera, std::string *err, const json &o,
5055 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005056 double xmag = 0.0;
5057 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5058 return false;
5059 }
5060
5061 double ymag = 0.0;
5062 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5063 return false;
5064 }
5065
5066 double zfar = 0.0;
5067 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5068 return false;
5069 }
5070
5071 double znear = 0.0;
5072 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5073 "OrthographicCamera")) {
5074 return false;
5075 }
5076
Selmar09d2ff12018-03-15 17:30:42 +01005077 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005078 ParseExtrasProperty(&(camera->extras), o);
5079
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005080 if (store_original_json_for_extras_and_extensions) {
5081 {
5082 json_const_iterator it;
5083 if (FindMember(o, "extensions", it)) {
5084 camera->extensions_json_string = JsonToString(GetValue(it));
5085 }
5086 }
5087 {
5088 json_const_iterator it;
5089 if (FindMember(o, "extras", it)) {
5090 camera->extras_json_string = JsonToString(GetValue(it));
5091 }
5092 }
5093 }
5094
Selmar Kok31cb7f92018-10-03 15:39:05 +02005095 camera->xmag = xmag;
5096 camera->ymag = ymag;
5097 camera->zfar = zfar;
5098 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005099
5100 // TODO(syoyo): Validate parameter values.
5101
5102 return true;
5103}
5104
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005105static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5106 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005107 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5108 return false;
5109 }
5110
5111 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005112 json_const_iterator orthoIt;
5113 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005114 if (err) {
5115 std::stringstream ss;
5116 ss << "Orhographic camera description not found." << std::endl;
5117 (*err) += ss.str();
5118 }
5119 return false;
5120 }
5121
jrkooncecba5d6c2019-08-29 11:26:22 -05005122 const json &v = GetValue(orthoIt);
5123 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005124 if (err) {
5125 std::stringstream ss;
5126 ss << "\"orthographic\" is not a JSON object." << std::endl;
5127 (*err) += ss.str();
5128 }
5129 return false;
5130 }
5131
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005132 if (!ParseOrthographicCamera(
5133 &camera->orthographic, err, v,
5134 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005135 return false;
5136 }
5137 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005138 json_const_iterator perspIt;
5139 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005140 if (err) {
5141 std::stringstream ss;
5142 ss << "Perspective camera description not found." << std::endl;
5143 (*err) += ss.str();
5144 }
5145 return false;
5146 }
5147
jrkooncecba5d6c2019-08-29 11:26:22 -05005148 const json &v = GetValue(perspIt);
5149 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005150 if (err) {
5151 std::stringstream ss;
5152 ss << "\"perspective\" is not a JSON object." << std::endl;
5153 (*err) += ss.str();
5154 }
5155 return false;
5156 }
5157
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005158 if (!ParsePerspectiveCamera(
5159 &camera->perspective, err, v,
5160 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005161 return false;
5162 }
5163 } else {
5164 if (err) {
5165 std::stringstream ss;
5166 ss << "Invalid camera type: \"" << camera->type
5167 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5168 (*err) += ss.str();
5169 }
5170 return false;
5171 }
5172
5173 ParseStringProperty(&camera->name, err, o, "name", false);
5174
Selmar09d2ff12018-03-15 17:30:42 +01005175 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005176 ParseExtrasProperty(&(camera->extras), o);
5177
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005178 if (store_original_json_for_extras_and_extensions) {
5179 {
5180 json_const_iterator it;
5181 if (FindMember(o, "extensions", it)) {
5182 camera->extensions_json_string = JsonToString(GetValue(it));
5183 }
5184 }
5185 {
5186 json_const_iterator it;
5187 if (FindMember(o, "extras", it)) {
5188 camera->extras_json_string = JsonToString(GetValue(it));
5189 }
5190 }
5191 }
5192
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005193 return true;
5194}
5195
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005196static bool ParseLight(Light *light, std::string *err, const json &o,
5197 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005198 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5199 return false;
5200 }
5201
5202 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005203 json_const_iterator spotIt;
5204 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005205 if (err) {
5206 std::stringstream ss;
5207 ss << "Spot light description not found." << std::endl;
5208 (*err) += ss.str();
5209 }
5210 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005211 }
5212
jrkooncecba5d6c2019-08-29 11:26:22 -05005213 const json &v = GetValue(spotIt);
5214 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005215 if (err) {
5216 std::stringstream ss;
5217 ss << "\"spot\" is not a JSON object." << std::endl;
5218 (*err) += ss.str();
5219 }
5220 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005221 }
5222
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005223 if (!ParseSpotLight(&light->spot, err, v,
5224 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005225 return false;
5226 }
5227 }
5228
5229 ParseStringProperty(&light->name, err, o, "name", false);
5230 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5231 ParseNumberProperty(&light->range, err, o, "range", false);
5232 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5233 ParseExtensionsProperty(&light->extensions, err, o);
5234 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005235
5236 if (store_original_json_for_extras_and_extensions) {
5237 {
5238 json_const_iterator it;
5239 if (FindMember(o, "extensions", it)) {
5240 light->extensions_json_string = JsonToString(GetValue(it));
5241 }
5242 }
5243 {
5244 json_const_iterator it;
5245 if (FindMember(o, "extras", it)) {
5246 light->extras_json_string = JsonToString(GetValue(it));
5247 }
5248 }
5249 }
5250
Johan Bowald52936a02019-07-17 09:06:45 +02005251 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005252}
5253
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005254bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005255 const char *json_str,
5256 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005257 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005258 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005259 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005260 if (err) {
5261 (*err) = "JSON string too short.\n";
5262 }
5263 return false;
5264 }
5265
jrkooncecba5d6c2019-08-29 11:26:22 -05005266 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005267
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005268#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5269 defined(_CPPUNWIND)) && \
Syoyo Fujita06d2fbd2019-11-17 02:11:59 +09005270 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005271 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005272 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005273
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005274 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005275 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005276 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005277 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005278 return false;
5279 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005280#else
5281 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005282 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005283
jrkooncecba5d6c2019-08-29 11:26:22 -05005284 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005285 // Assume parsing was failed.
5286 if (err) {
5287 (*err) = "Failed to parse JSON object\n";
5288 }
5289 return false;
5290 }
5291 }
5292#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005293
jrkooncecba5d6c2019-08-29 11:26:22 -05005294 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005295 // root is not an object.
5296 if (err) {
5297 (*err) = "Root element is not a JSON object\n";
5298 }
5299 return false;
5300 }
5301
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005302 {
5303 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005304 json_const_iterator it;
5305 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005306 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005307 json_const_iterator version_it;
5308 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005309 if (FindMember(itObj, "version", version_it) &&
5310 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005311 version_found = true;
5312 }
5313 }
5314 if (version_found) {
5315 // OK
5316 } else if (check_sections & REQUIRE_VERSION) {
5317 if (err) {
5318 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5319 }
5320 return false;
5321 }
5322 }
5323
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005324 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005325 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005326
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005327 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005328 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005329 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005330 };
5331
Syoyo Fujita83675312017-12-02 21:14:13 +09005332 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005333 if ((check_sections & REQUIRE_SCENES) &&
5334 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005335 if (err) {
5336 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5337 }
5338 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005339 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005340 }
5341
Syoyo Fujita83675312017-12-02 21:14:13 +09005342 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005343 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005344 if (err) {
5345 (*err) += "\"nodes\" object not found in .gltf\n";
5346 }
5347 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005348 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005349 }
5350
Syoyo Fujita83675312017-12-02 21:14:13 +09005351 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005352 if ((check_sections & REQUIRE_ACCESSORS) &&
5353 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005354 if (err) {
5355 (*err) += "\"accessors\" object not found in .gltf\n";
5356 }
5357 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005358 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005359 }
5360
Syoyo Fujita83675312017-12-02 21:14:13 +09005361 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005362 if ((check_sections & REQUIRE_BUFFERS) &&
5363 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005364 if (err) {
5365 (*err) += "\"buffers\" object not found in .gltf\n";
5366 }
5367 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005368 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005369 }
5370
Syoyo Fujita83675312017-12-02 21:14:13 +09005371 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005372 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5373 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005374 if (err) {
5375 (*err) += "\"bufferViews\" object not found in .gltf\n";
5376 }
5377 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005378 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005379 }
5380
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005381 model->buffers.clear();
5382 model->bufferViews.clear();
5383 model->accessors.clear();
5384 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005385 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005386 model->nodes.clear();
5387 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005388 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005389 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005390 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005391
Syoyo Fujita83675312017-12-02 21:14:13 +09005392 // 1. Parse Asset
5393 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005394 json_const_iterator it;
5395 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5396 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005397
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005398 ParseAsset(&model->asset, err, root,
5399 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005400 }
5401 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005402
jrkoonce51453942019-09-03 09:48:30 -05005403#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005404 auto ForEachInArray = [](const json &_v, const char *member,
5405 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005406#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005407 // The std::function<> implementation can be less efficient because it will
5408 // allocate heap when the size of the captured lambda is above 16 bytes with
5409 // clang and gcc, but it does not require C++14.
5410 auto ForEachInArray = [](const json &_v, const char *member,
5411 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005412#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005413 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005414 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005415 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005416 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005417 auto it = ArrayBegin(root);
5418 auto end = ArrayEnd(root);
5419 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005420 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005421 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005422 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005423 return true;
5424 };
5425
jrkooncecba5d6c2019-08-29 11:26:22 -05005426 // 2. Parse extensionUsed
5427 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005428 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005429 std::string str;
5430 GetString(o, str);
5431 model->extensionsUsed.emplace_back(std::move(str));
5432 return true;
5433 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005434 }
5435
Syoyo Fujita83675312017-12-02 21:14:13 +09005436 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005437 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005438 std::string str;
5439 GetString(o, str);
5440 model->extensionsRequired.emplace_back(std::move(str));
5441 return true;
5442 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005443 }
5444
Syoyo Fujita83675312017-12-02 21:14:13 +09005445 // 3. Parse Buffer
5446 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005447 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005448 if (!IsObject(o)) {
5449 if (err) {
5450 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005451 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005452 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005453 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005454 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005455 if (!ParseBuffer(&buffer, err, o,
5456 store_original_json_for_extras_and_extensions_, &fs,
5457 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005458 return false;
5459 }
5460
5461 model->buffers.emplace_back(std::move(buffer));
5462 return true;
5463 });
5464
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005465 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005466 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005467 }
5468 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005469 // 4. Parse BufferView
5470 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005471 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005472 if (!IsObject(o)) {
5473 if (err) {
5474 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005475 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005476 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005477 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005478 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005479 if (!ParseBufferView(&bufferView, err, o,
5480 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005481 return false;
5482 }
5483
5484 model->bufferViews.emplace_back(std::move(bufferView));
5485 return true;
5486 });
5487
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005488 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005489 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005490 }
5491 }
5492
Syoyo Fujita83675312017-12-02 21:14:13 +09005493 // 5. Parse Accessor
5494 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005495 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005496 if (!IsObject(o)) {
5497 if (err) {
5498 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005499 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005500 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005501 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005502 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005503 if (!ParseAccessor(&accessor, err, o,
5504 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005505 return false;
5506 }
5507
5508 model->accessors.emplace_back(std::move(accessor));
5509 return true;
5510 });
5511
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005512 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005513 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005514 }
5515 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005516
Syoyo Fujita83675312017-12-02 21:14:13 +09005517 // 6. Parse Mesh
5518 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005519 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005520 if (!IsObject(o)) {
5521 if (err) {
5522 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005523 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005524 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005525 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005526 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005527 if (!ParseMesh(&mesh, model, err, o,
5528 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005529 return false;
5530 }
5531
5532 model->meshes.emplace_back(std::move(mesh));
5533 return true;
5534 });
5535
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005536 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005537 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005538 }
5539 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005540
viperscape9df05802018-12-05 14:11:01 -05005541 // Assign missing bufferView target types
5542 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005543 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005544 for (auto &mesh : model->meshes) {
5545 for (auto &primitive : mesh.primitives) {
5546 if (primitive.indices >
5547 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005548 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005549 if (size_t(primitive.indices) >= model->accessors.size()) {
5550 if (err) {
5551 (*err) += "primitive indices accessor out of bounds";
5552 }
5553 return false;
5554 }
5555
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005556 auto bufferView =
5557 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005558 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5559 if (err) {
5560 (*err) += "accessor[" + std::to_string(primitive.indices) +
5561 "] invalid bufferView";
5562 }
5563 return false;
5564 }
5565
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005566 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005567 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005568 // we could optionally check if acessors' bufferView type is Scalar, as
5569 // it should be
viperscape9df05802018-12-05 14:11:01 -05005570 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005571
5572 for (auto &attribute : primitive.attributes) {
Syoyo Fujita72f4a552020-01-08 00:40:41 +09005573 model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)]
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005574 .target = TINYGLTF_TARGET_ARRAY_BUFFER;
5575 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005576
5577 for(auto &target : primitive.targets) {
5578 for(auto &attribute : target) {
5579 model->bufferViews[size_t(model->accessors[size_t(attribute.second)].bufferView)]
5580 .target = TINYGLTF_TARGET_ARRAY_BUFFER;
5581 }
5582 }
viperscape9df05802018-12-05 14:11:01 -05005583 }
5584 }
5585
Syoyo Fujita83675312017-12-02 21:14:13 +09005586 // 7. Parse Node
5587 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005588 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005589 if (!IsObject(o)) {
5590 if (err) {
5591 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005592 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005593 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005594 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005595 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005596 if (!ParseNode(&node, err, o,
5597 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005598 return false;
5599 }
5600
5601 model->nodes.emplace_back(std::move(node));
5602 return true;
5603 });
5604
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005605 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005606 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005607 }
5608 }
5609
5610 // 8. Parse scenes.
5611 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005612 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005613 if (!IsObject(o)) {
5614 if (err) {
5615 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005616 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005617 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005618 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005619 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005620 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005621
5622 Scene scene;
5623 scene.nodes = std::move(nodes);
5624
5625 ParseStringProperty(&scene.name, err, o, "name", false);
5626
5627 ParseExtensionsProperty(&scene.extensions, err, o);
5628 ParseExtrasProperty(&scene.extras, o);
5629
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005630 if (store_original_json_for_extras_and_extensions_) {
5631 {
5632 json_const_iterator it;
5633 if (FindMember(o, "extensions", it)) {
5634 model->extensions_json_string = JsonToString(GetValue(it));
5635 }
5636 }
5637 {
5638 json_const_iterator it;
5639 if (FindMember(o, "extras", it)) {
5640 model->extras_json_string = JsonToString(GetValue(it));
5641 }
5642 }
5643 }
5644
jrkooncecba5d6c2019-08-29 11:26:22 -05005645 model->scenes.emplace_back(std::move(scene));
5646 return true;
5647 });
5648
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005649 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005650 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005651 }
5652 }
5653
5654 // 9. Parse default scenes.
5655 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005656 json_const_iterator rootIt;
5657 int iVal;
5658 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5659 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005660 }
5661 }
5662
5663 // 10. Parse Material
5664 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005665 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005666 if (!IsObject(o)) {
5667 if (err) {
5668 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005669 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005670 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005671 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005672 Material material;
5673 ParseStringProperty(&material.name, err, o, "name", false);
5674
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005675 if (!ParseMaterial(&material, err, o,
5676 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005677 return false;
5678 }
5679
5680 model->materials.emplace_back(std::move(material));
5681 return true;
5682 });
5683
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005684 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005685 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005686 }
5687 }
5688
5689 // 11. Parse Image
5690 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005691 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005692 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005693 if (!IsObject(o)) {
5694 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005695 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005696 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005697 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005698 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005699 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005700 if (!ParseImage(&image, idx, err, warn, o,
5701 store_original_json_for_extras_and_extensions_, base_dir,
5702 &fs, &this->LoadImageData, load_image_user_data_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005703 return false;
5704 }
5705
5706 if (image.bufferView != -1) {
5707 // Load image from the buffer view.
5708 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5709 if (err) {
5710 std::stringstream ss;
5711 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005712 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005713 (*err) += ss.str();
5714 }
5715 return false;
5716 }
5717
5718 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005719 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005720 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5721 if (err) {
5722 std::stringstream ss;
5723 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005724 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005725 (*err) += ss.str();
5726 }
5727 return false;
5728 }
5729 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5730
5731 if (*LoadImageData == nullptr) {
5732 if (err) {
5733 (*err) += "No LoadImageData callback specified.\n";
5734 }
5735 return false;
5736 }
5737 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005738 &image, idx, err, warn, image.width, image.height,
5739 &buffer.data[bufferView.byteOffset],
5740 static_cast<int>(bufferView.byteLength), load_image_user_data_);
jrkooncecba5d6c2019-08-29 11:26:22 -05005741 if (!ret) {
5742 return false;
5743 }
5744 }
5745
5746 model->images.emplace_back(std::move(image));
5747 ++idx;
5748 return true;
5749 });
5750
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005751 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005752 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005753 }
5754 }
5755
5756 // 12. Parse Texture
5757 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005758 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005759 if (!IsObject(o)) {
5760 if (err) {
5761 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005762 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005763 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005764 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005765 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005766 if (!ParseTexture(&texture, err, o,
5767 store_original_json_for_extras_and_extensions_,
5768 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005769 return false;
5770 }
5771
5772 model->textures.emplace_back(std::move(texture));
5773 return true;
5774 });
5775
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005776 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005777 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005778 }
5779 }
5780
5781 // 13. Parse Animation
5782 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005783 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005784 if (!IsObject(o)) {
5785 if (err) {
5786 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005787 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005788 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005789 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005790 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005791 if (!ParseAnimation(&animation, err, o,
5792 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005793 return false;
5794 }
5795
5796 model->animations.emplace_back(std::move(animation));
5797 return true;
5798 });
5799
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005800 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005801 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005802 }
5803 }
5804
5805 // 14. Parse Skin
5806 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005807 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005808 if (!IsObject(o)) {
5809 if (err) {
5810 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005811 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005812 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005813 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005814 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005815 if (!ParseSkin(&skin, err, o,
5816 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005817 return false;
5818 }
5819
5820 model->skins.emplace_back(std::move(skin));
5821 return true;
5822 });
5823
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005824 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005825 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005826 }
5827 }
5828
5829 // 15. Parse Sampler
5830 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005831 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005832 if (!IsObject(o)) {
5833 if (err) {
5834 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005835 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005836 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005837 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005838 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005839 if (!ParseSampler(&sampler, err, o,
5840 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005841 return false;
5842 }
5843
5844 model->samplers.emplace_back(std::move(sampler));
5845 return true;
5846 });
5847
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005848 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005849 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005850 }
5851 }
5852
5853 // 16. Parse Camera
5854 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005855 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005856 if (!IsObject(o)) {
5857 if (err) {
5858 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005859 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005860 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005861 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005862 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005863 if (!ParseCamera(&camera, err, o,
5864 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005865 return false;
5866 }
5867
5868 model->cameras.emplace_back(std::move(camera));
5869 return true;
5870 });
5871
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005872 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005873 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005874 }
5875 }
5876
5877 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01005878 ParseExtensionsProperty(&model->extensions, err, v);
5879
5880 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09005881 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005882 json_const_iterator rootIt;
5883 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
5884 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09005885
jrkooncecba5d6c2019-08-29 11:26:22 -05005886 json_const_iterator it(ObjectBegin(root));
5887 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09005888 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005889 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05005890 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005891 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005892 const json &object = GetValue(it);
5893 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005894 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005895 const json &lights = GetValue(itLight);
5896 if (!IsArray(lights)) {
5897 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09005898 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005899
5900 auto arrayIt(ArrayBegin(lights));
5901 auto arrayItEnd(ArrayEnd(lights));
5902 for (; arrayIt != arrayItEnd; ++arrayIt) {
5903 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005904 if (!ParseLight(&light, err, *arrayIt,
5905 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005906 return false;
5907 }
5908 model->lights.emplace_back(std::move(light));
5909 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005910 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005911 }
5912 }
5913 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005914 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005915
mynzc0d4d1c2018-06-28 23:06:00 +09005916 // 19. Parse Extras
5917 ParseExtrasProperty(&model->extras, v);
5918
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005919 if (store_original_json_for_extras_and_extensions_) {
5920 model->extras_json_string = JsonToString(v["extras"]);
5921 model->extensions_json_string = JsonToString(v["extensions"]);
5922 }
5923
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005924 return true;
5925}
5926
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005927bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005928 std::string *warn, const char *str,
5929 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005930 const std::string &base_dir,
5931 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09005932 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09005933 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09005934 bin_size_ = 0;
5935
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005936 return LoadFromString(model, err, warn, str, length, base_dir,
5937 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09005938}
5939
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005940bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005941 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005942 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09005943 std::stringstream ss;
5944
Paolo Jovone6601bf2018-07-07 20:43:33 +02005945 if (fs.ReadWholeFile == nullptr) {
5946 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005947 ss << "Failed to read file: " << filename
5948 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005949 if (err) {
5950 (*err) = ss.str();
5951 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09005952 return false;
5953 }
5954
Paolo Jovone6601bf2018-07-07 20:43:33 +02005955 std::vector<unsigned char> data;
5956 std::string fileerr;
5957 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005958 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02005959 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
5960 if (err) {
5961 (*err) = ss.str();
5962 }
5963 return false;
5964 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09005965
Paolo Jovone6601bf2018-07-07 20:43:33 +02005966 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04005967 if (sz == 0) {
5968 if (err) {
5969 (*err) = "Empty file.";
5970 }
5971 return false;
5972 }
5973
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09005974 std::string basedir = GetBaseDir(filename);
5975
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005976 bool ret = LoadASCIIFromString(
5977 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
5978 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04005979
Syoyo Fujitaec39a522016-05-01 18:33:31 +09005980 return ret;
5981}
5982
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005983bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005984 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005985 const unsigned char *bytes,
5986 unsigned int size,
5987 const std::string &base_dir,
5988 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09005989 if (size < 20) {
5990 if (err) {
5991 (*err) = "Too short data size for glTF Binary.";
5992 }
5993 return false;
5994 }
5995
Syoyo Fujitabeded612016-05-01 20:03:43 +09005996 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
5997 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09005998 // ok
5999 } else {
6000 if (err) {
6001 (*err) = "Invalid magic.";
6002 }
6003 return false;
6004 }
6005
Syoyo Fujitabeded612016-05-01 20:03:43 +09006006 unsigned int version; // 4 bytes
6007 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006008 unsigned int model_length; // 4 bytes
6009 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006010
6011 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09006012 memcpy(&version, bytes + 4, 4);
6013 swap4(&version);
6014 memcpy(&length, bytes + 8, 4);
6015 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006016 memcpy(&model_length, bytes + 12, 4);
6017 swap4(&model_length);
6018 memcpy(&model_format, bytes + 16, 4);
6019 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006020
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006021 // In case the Bin buffer is not present, the size is exactly 20 + size of
6022 // JSON contents,
6023 // so use "greater than" operator.
Jeff McGlynn89152522019-04-25 16:33:56 -07006024 if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
Doug Muircf668682019-10-28 09:51:13 -07006025 (20 + model_length > length) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09006026 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006027 if (err) {
6028 (*err) = "Invalid glTF binary.";
6029 }
6030 return false;
6031 }
6032
6033 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006034 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006035 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006036
6037 is_binary_ = true;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006038 bin_data_ = bytes + 20 + model_length +
6039 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09006040 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006041 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006042
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006043 bool ret = LoadFromString(model, err, warn,
6044 reinterpret_cast<const char *>(&bytes[20]),
6045 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006046 if (!ret) {
6047 return ret;
6048 }
6049
6050 return true;
6051}
6052
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006053bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006054 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006055 const std::string &filename,
6056 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006057 std::stringstream ss;
6058
Paolo Jovone6601bf2018-07-07 20:43:33 +02006059 if (fs.ReadWholeFile == nullptr) {
6060 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006061 ss << "Failed to read file: " << filename
6062 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006063 if (err) {
6064 (*err) = ss.str();
6065 }
6066 return false;
6067 }
6068
Paolo Jovone6601bf2018-07-07 20:43:33 +02006069 std::vector<unsigned char> data;
6070 std::string fileerr;
6071 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006072 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006073 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6074 if (err) {
6075 (*err) = ss.str();
6076 }
6077 return false;
6078 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006079
Syoyo Fujitabeded612016-05-01 20:03:43 +09006080 std::string basedir = GetBaseDir(filename);
6081
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006082 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6083 static_cast<unsigned int>(data.size()),
6084 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006085
6086 return ret;
6087}
6088
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006089///////////////////////
6090// GLTF Serialization
6091///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006092namespace {
6093json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006094#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006095 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006096#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006097 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006098#endif
jrkoonce63419a12019-09-03 17:06:41 -05006099}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006100
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006101void JsonAssign(json &dest, const json &src) {
6102#ifdef TINYGLTF_USE_RAPIDJSON
6103 dest.CopyFrom(src, GetAllocator());
6104#else
6105 dest = src;
6106#endif
6107}
6108
6109void JsonAddMember(json &o, const char *key, json &&value) {
6110#ifdef TINYGLTF_USE_RAPIDJSON
6111 if (!o.IsObject()) {
6112 o.SetObject();
6113 }
6114 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6115#else
6116 o[key] = std::move(value);
6117#endif
6118}
6119
6120void JsonPushBack(json &o, json &&value) {
6121#ifdef TINYGLTF_USE_RAPIDJSON
6122 o.PushBack(std::move(value), GetAllocator());
6123#else
6124 o.push_back(std::move(value));
6125#endif
6126}
6127
6128bool JsonIsNull(const json &o) {
6129#ifdef TINYGLTF_USE_RAPIDJSON
6130 return o.IsNull();
6131#else
6132 return o.is_null();
6133#endif
6134}
6135
6136void JsonSetObject(json &o) {
6137#ifdef TINYGLTF_USE_RAPIDJSON
6138 o.SetObject();
6139#else
6140 o = o.object({});
6141#endif
6142}
6143
6144void JsonReserveArray(json &o, size_t s) {
6145#ifdef TINYGLTF_USE_RAPIDJSON
6146 o.SetArray();
6147 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6148#endif
6149 (void)(o);
6150 (void)(s);
6151}
6152} // namespace
6153
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006154// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006155
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006156template <typename T>
6157static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006158 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006159 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006160 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006161 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006162 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006163}
6164
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006165template <typename T>
6166static void SerializeNumberArrayProperty(const std::string &key,
6167 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006168 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006169 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006170
jrkooncecba5d6c2019-08-29 11:26:22 -05006171 json ary;
6172 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006173 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006174 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006175 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006176 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006177}
6178
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006179static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006180 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006181 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006182}
6183
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006184static void SerializeStringArrayProperty(const std::string &key,
6185 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006186 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006187 json ary;
6188 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006189 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006190 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006191 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006192 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006193}
6194
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006195static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006196 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006197#ifdef TINYGLTF_USE_RAPIDJSON
6198 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006199 case REAL_TYPE:
6200 obj.SetDouble(value.Get<double>());
6201 break;
6202 case INT_TYPE:
6203 obj.SetInt(value.Get<int>());
6204 break;
6205 case BOOL_TYPE:
6206 obj.SetBool(value.Get<bool>());
6207 break;
6208 case STRING_TYPE:
6209 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6210 break;
6211 case ARRAY_TYPE: {
6212 obj.SetArray();
6213 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6214 GetAllocator());
6215 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6216 Value elementValue = value.Get(int(i));
6217 json elementJson;
6218 if (ValueToJson(value.Get(int(i)), &elementJson))
6219 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006220 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006221 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006222 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006223 case BINARY_TYPE:
6224 // TODO
6225 // obj = json(value.Get<std::vector<unsigned char>>());
6226 return false;
6227 break;
6228 case OBJECT_TYPE: {
6229 obj.SetObject();
6230 Value::Object objMap = value.Get<Value::Object>();
6231 for (auto &it : objMap) {
6232 json elementJson;
6233 if (ValueToJson(it.second, &elementJson)) {
6234 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6235 std::move(elementJson), GetAllocator());
6236 }
6237 }
6238 break;
6239 }
6240 case NULL_TYPE:
6241 default:
6242 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006243 }
6244#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006245 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006246 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006247 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006248 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006249 case INT_TYPE:
6250 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006251 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006252 case BOOL_TYPE:
6253 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006254 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006255 case STRING_TYPE:
6256 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006257 break;
6258 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006259 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6260 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006261 json elementJson;
6262 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006263 obj.push_back(elementJson);
6264 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006265 break;
6266 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006267 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006268 // TODO
6269 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006270 return false;
6271 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006272 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006273 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006274 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006275 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006276 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006277 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006278 break;
6279 }
6280 case NULL_TYPE:
6281 default:
6282 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006283 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006284#endif
6285 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006286 return true;
6287}
6288
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006289static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006290 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006291 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006292 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006293 JsonAddMember(obj, key.c_str(), std::move(ret));
6294 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006295}
6296
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006297static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006298 json &o) {
6299 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006300 if (data.size() > 0) {
6301 std::string encodedData =
6302 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6303 SerializeStringProperty("uri", header + encodedData, o);
6304 } else {
6305 // Issue #229
6306 // size 0 is allowd. Just emit mime header.
6307 SerializeStringProperty("uri", header, o);
6308 }
johan bowald30c53472018-03-30 11:49:36 +02006309}
6310
Selmar Koke4677492018-10-25 16:45:49 +02006311static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006312 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006313#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09006314#if defined(__GLIBCXX__) // mingw
6315 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), _O_WRONLY | _O_BINARY);
6316 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
6317 std::ostream output(&wfile_buf);
6318 if (!wfile_buf.is_open()) return false;
6319#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006320 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006321 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006322#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006323 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006324 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006325#endif
6326#else
6327 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6328 if (!output.is_open()) return false;
6329#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006330 if (data.size() > 0) {
6331 output.write(reinterpret_cast<const char *>(&data[0]),
6332 std::streamsize(data.size()));
6333 } else {
6334 // Issue #229
6335 // size 0 will be still valid buffer data.
6336 // write empty file.
6337 }
Selmar Koke4677492018-10-25 16:45:49 +02006338 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006339}
6340
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006341#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006342static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006343 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6344 ++paramIt) {
6345 if (paramIt->second.number_array.size()) {
6346 SerializeNumberArrayProperty<double>(paramIt->first,
6347 paramIt->second.number_array, o);
6348 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006349 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006350 for (std::map<std::string, double>::iterator it =
6351 paramIt->second.json_double_value.begin();
6352 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006353 if (it->first == "index") {
6354 json_double_value[it->first] = paramIt->second.TextureIndex();
6355 } else {
6356 json_double_value[it->first] = it->second;
6357 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006358 }
6359
Syoyo Fujita83675312017-12-02 21:14:13 +09006360 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006361 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006362 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006363 } else if (paramIt->second.has_number_value) {
6364 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006365 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006366 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006367 }
6368 }
6369}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006370#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006371
Selmar Kok81b672b2019-10-18 16:08:44 +02006372static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006373 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006374
6375 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006376 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006377 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006378 // Allow an empty object for extension(#97)
6379 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006380 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006381 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006382 isNull = JsonIsNull(ret);
6383 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006384 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006385 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006386 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6387 // create empty object so that an extension name is still included in
6388 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006389 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006390 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006391 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006392 }
6393 }
Selmar09d2ff12018-03-15 17:30:42 +01006394 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006395 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006396}
6397
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006398static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006399 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006400
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006401 if (accessor.byteOffset != 0.0)
6402 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006403
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006404 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6405 SerializeNumberProperty<size_t>("count", accessor.count, o);
6406 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6407 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006408 if (accessor.normalized)
6409 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006410 std::string type;
6411 switch (accessor.type) {
6412 case TINYGLTF_TYPE_SCALAR:
6413 type = "SCALAR";
6414 break;
6415 case TINYGLTF_TYPE_VEC2:
6416 type = "VEC2";
6417 break;
6418 case TINYGLTF_TYPE_VEC3:
6419 type = "VEC3";
6420 break;
6421 case TINYGLTF_TYPE_VEC4:
6422 type = "VEC4";
6423 break;
6424 case TINYGLTF_TYPE_MAT2:
6425 type = "MAT2";
6426 break;
6427 case TINYGLTF_TYPE_MAT3:
6428 type = "MAT3";
6429 break;
6430 case TINYGLTF_TYPE_MAT4:
6431 type = "MAT4";
6432 break;
6433 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006434
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006435 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006436 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006437
6438 if (accessor.extras.Type() != NULL_TYPE) {
6439 SerializeValue("extras", accessor.extras, o);
6440 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006441}
6442
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006443static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006444 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006445 {
6446 json target;
6447 SerializeNumberProperty("node", channel.target_node, target);
6448 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006449
Selmar Kok973d9b32020-01-21 18:45:24 +01006450 SerializeExtensionMap(channel.target_extensions, target);
6451
jrkooncecba5d6c2019-08-29 11:26:22 -05006452 JsonAddMember(o, "target", std::move(target));
6453 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006454
6455 if (channel.extras.Type() != NULL_TYPE) {
6456 SerializeValue("extras", channel.extras, o);
6457 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006458
Selmar Kok4e2988e2019-08-16 14:08:08 +02006459 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006460}
6461
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006462static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006463 SerializeNumberProperty("input", sampler.input, o);
6464 SerializeNumberProperty("output", sampler.output, o);
6465 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006466
6467 if (sampler.extras.Type() != NULL_TYPE) {
6468 SerializeValue("extras", sampler.extras, o);
6469 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006470}
6471
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006472static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006473 if (!animation.name.empty())
6474 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006475
jrkooncecba5d6c2019-08-29 11:26:22 -05006476 {
6477 json channels;
6478 JsonReserveArray(channels, animation.channels.size());
6479 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6480 json channel;
6481 AnimationChannel gltfChannel = animation.channels[i];
6482 SerializeGltfAnimationChannel(gltfChannel, channel);
6483 JsonPushBack(channels, std::move(channel));
6484 }
6485
6486 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006487 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006488
jrkooncecba5d6c2019-08-29 11:26:22 -05006489 {
6490 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006491 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006492 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6493 json sampler;
6494 AnimationSampler gltfSampler = animation.samplers[i];
6495 SerializeGltfAnimationSampler(gltfSampler, sampler);
6496 JsonPushBack(samplers, std::move(sampler));
6497 }
6498 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006499 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006500
Jens Olssonb3af2f12018-06-04 10:17:49 +02006501 if (animation.extras.Type() != NULL_TYPE) {
6502 SerializeValue("extras", animation.extras, o);
6503 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006504
Selmar Kok4e2988e2019-08-16 14:08:08 +02006505 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006506}
6507
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006508static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006509 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006510 SerializeStringProperty("generator", asset.generator, o);
6511 }
6512
Christophe820ede82019-07-04 15:21:21 +09006513 if (!asset.copyright.empty()) {
6514 SerializeStringProperty("copyright", asset.copyright, o);
6515 }
6516
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006517 if (!asset.version.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006518 SerializeStringProperty("version", asset.version, o);
6519 }
6520
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006521 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006522 SerializeValue("extras", asset.extras, o);
6523 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006524
Selmar09d2ff12018-03-15 17:30:42 +01006525 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006526}
6527
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006528 static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6529 std::vector<unsigned char> &binBuffer) {
6530 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6531 binBuffer=buffer.data;
6532
6533 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6534
6535 if (buffer.extras.Type() != NULL_TYPE) {
6536 SerializeValue("extras", buffer.extras, o);
6537 }
6538}
6539
johan bowald30c53472018-03-30 11:49:36 +02006540static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6541 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6542 SerializeGltfBufferData(buffer.data, o);
6543
6544 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006545
6546 if (buffer.extras.Type() != NULL_TYPE) {
6547 SerializeValue("extras", buffer.extras, o);
6548 }
johan bowald30c53472018-03-30 11:49:36 +02006549}
6550
Selmar Koke4677492018-10-25 16:45:49 +02006551static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006552 const std::string &binFilename,
6553 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006554 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006555 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006556 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006557
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006558 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006559
6560 if (buffer.extras.Type() != NULL_TYPE) {
6561 SerializeValue("extras", buffer.extras, o);
6562 }
Selmar Koke4677492018-10-25 16:45:49 +02006563 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006564}
6565
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006566static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006567 SerializeNumberProperty("buffer", bufferView.buffer, o);
6568 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006569
Johan Bowaldfaa27222018-03-28 14:44:45 +02006570 // byteStride is optional, minimum allowed is 4
6571 if (bufferView.byteStride >= 4) {
6572 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6573 }
6574 // byteOffset is optional, default is 0
6575 if (bufferView.byteOffset > 0) {
6576 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6577 }
6578 // Target is optional, check if it contains a valid value
6579 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6580 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6581 SerializeNumberProperty("target", bufferView.target, o);
6582 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006583 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006584 SerializeStringProperty("name", bufferView.name, o);
6585 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006586
6587 if (bufferView.extras.Type() != NULL_TYPE) {
6588 SerializeValue("extras", bufferView.extras, o);
6589 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006590}
6591
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006592static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006593 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08006594 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006595 SerializeStringProperty("mimeType", image.mimeType, o);
6596 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
6597 } else {
6598 SerializeStringProperty("uri", image.uri, o);
6599 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006600
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006601 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006602 SerializeStringProperty("name", image.name, o);
6603 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006604
6605 if (image.extras.Type() != NULL_TYPE) {
6606 SerializeValue("extras", image.extras, o);
6607 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09006608
6609 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006610}
6611
Syoyo Fujita046400b2019-07-24 19:26:48 +09006612static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
6613 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006614
Syoyo Fujita046400b2019-07-24 19:26:48 +09006615 if (texinfo.texCoord != 0) {
6616 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6617 }
6618
6619 if (texinfo.extras.Type() != NULL_TYPE) {
6620 SerializeValue("extras", texinfo.extras, o);
6621 }
6622
6623 SerializeExtensionMap(texinfo.extensions, o);
6624}
6625
6626static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
6627 json &o) {
6628 SerializeNumberProperty("index", texinfo.index, o);
6629
6630 if (texinfo.texCoord != 0) {
6631 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6632 }
6633
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006634 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006635 SerializeNumberProperty("scale", texinfo.scale, o);
6636 }
6637
6638 if (texinfo.extras.Type() != NULL_TYPE) {
6639 SerializeValue("extras", texinfo.extras, o);
6640 }
6641
6642 SerializeExtensionMap(texinfo.extensions, o);
6643}
6644
6645static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
6646 json &o) {
6647 SerializeNumberProperty("index", texinfo.index, o);
6648
6649 if (texinfo.texCoord != 0) {
6650 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6651 }
6652
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006653 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006654 SerializeNumberProperty("strength", texinfo.strength, o);
6655 }
6656
6657 if (texinfo.extras.Type() != NULL_TYPE) {
6658 SerializeValue("extras", texinfo.extras, o);
6659 }
6660
6661 SerializeExtensionMap(texinfo.extensions, o);
6662}
6663
6664static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
6665 json &o) {
6666 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
6667 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
6668 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
6669 o);
6670 }
6671
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006672 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006673 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
6674 }
6675
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006676 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006677 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
6678 }
6679
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006680 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006681 json texinfo;
6682 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006683 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006684 }
6685
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006686 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006687 json texinfo;
6688 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006689 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006690 }
6691
6692 SerializeExtensionMap(pbr.extensions, o);
6693
6694 if (pbr.extras.Type() != NULL_TYPE) {
6695 SerializeValue("extras", pbr.extras, o);
6696 }
6697}
6698
6699static void SerializeGltfMaterial(Material &material, json &o) {
6700 if (material.name.size()) {
6701 SerializeStringProperty("name", material.name, o);
6702 }
6703
6704 // QUESTION(syoyo): Write material parameters regardless of its default value?
6705
6706 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
6707 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
6708 }
6709
Patrick Härtld9a468b2019-08-14 14:14:07 +02006710 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006711 SerializeStringProperty("alphaMode", material.alphaMode, o);
6712 }
6713
Selmar Kok58ab95b2019-10-31 15:08:03 +01006714 if(material.doubleSided != false)
6715 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006716
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006717 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006718 json texinfo;
6719 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006720 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006721 }
6722
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006723 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006724 json texinfo;
6725 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006726 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006727 }
6728
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006729 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006730 json texinfo;
6731 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006732 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006733 }
6734
6735 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
6736 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
6737 SerializeNumberArrayProperty<double>("emissiveFactor",
6738 material.emissiveFactor, o);
6739 }
6740
6741 {
6742 json pbrMetallicRoughness;
6743 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
6744 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09006745 // Issue 204
6746 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
6747 // default values(json is null). Otherwise it will serialize to
6748 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
6749 // importers(and validators).
6750 //
6751 if (!JsonIsNull(pbrMetallicRoughness)) {
6752 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
6753 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09006754 }
6755
6756#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006757 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006758 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006759 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05006760 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006761 }
6762
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006763 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09006764#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006765
Syoyo Fujita046400b2019-07-24 19:26:48 +09006766#endif
6767
6768 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006769
6770 if (material.extras.Type() != NULL_TYPE) {
6771 SerializeValue("extras", material.extras, o);
6772 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006773}
6774
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006775static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006776 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05006777 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006778 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006779 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006780 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05006781 {
6782 json attributes;
6783 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006784 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006785 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
6786 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006787
jrkooncecba5d6c2019-08-29 11:26:22 -05006788 JsonAddMember(primitive, "attributes", std::move(attributes));
6789 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02006790
6791 // Indicies is optional
6792 if (gltfPrimitive.indices > -1) {
6793 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
6794 }
6795 // Material is optional
6796 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09006797 SerializeNumberProperty<int>("material", gltfPrimitive.material,
6798 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02006799 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006800 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006801
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006802 // Morph targets
6803 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006804 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05006805 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006806 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006807 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006808 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
6809 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
6810 attrIt != targetData.end(); ++attrIt) {
6811 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
6812 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006813 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006814 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006815 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006816 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006817 }
6818
zhaozhiquan3eb65e22019-12-18 11:28:57 +08006819 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02006820
Jens Olssonb3af2f12018-06-04 10:17:49 +02006821 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
6822 SerializeValue("extras", gltfPrimitive.extras, primitive);
6823 }
6824
jrkooncecba5d6c2019-08-29 11:26:22 -05006825 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006826 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006827
jrkooncecba5d6c2019-08-29 11:26:22 -05006828 JsonAddMember(o, "primitives", std::move(primitives));
6829
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006830 if (mesh.weights.size()) {
6831 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
6832 }
6833
6834 if (mesh.name.size()) {
6835 SerializeStringProperty("name", mesh.name, o);
6836 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006837
Selmar Kok81b672b2019-10-18 16:08:44 +02006838 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006839 if (mesh.extras.Type() != NULL_TYPE) {
6840 SerializeValue("extras", mesh.extras, o);
6841 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006842}
6843
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02006844static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02006845 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
6846 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
6847 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02006848 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006849 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02006850 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02006851}
6852
Syoyo Fujita83675312017-12-02 21:14:13 +09006853static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006854 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02006855 SerializeNumberProperty("intensity", light.intensity, o);
6856 SerializeNumberProperty("range", light.range, o);
Emanuel Schrade186322b2017-11-06 11:14:41 +01006857 SerializeNumberArrayProperty("color", light.color, o);
6858 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02006859 if (light.type == "spot") {
6860 json spot;
6861 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05006862 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02006863 }
Selmar Kok81b672b2019-10-18 16:08:44 +02006864 SerializeExtensionMap(light.extensions, o);
6865 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006866 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02006867 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006868}
6869
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006870static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006871 if (node.translation.size() > 0) {
6872 SerializeNumberArrayProperty<double>("translation", node.translation, o);
6873 }
6874 if (node.rotation.size() > 0) {
6875 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
6876 }
6877 if (node.scale.size() > 0) {
6878 SerializeNumberArrayProperty<double>("scale", node.scale, o);
6879 }
6880 if (node.matrix.size() > 0) {
6881 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
6882 }
6883 if (node.mesh != -1) {
6884 SerializeNumberProperty<int>("mesh", node.mesh, o);
6885 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006886
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006887 if (node.skin != -1) {
6888 SerializeNumberProperty<int>("skin", node.skin, o);
6889 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006890
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006891 if (node.camera != -1) {
6892 SerializeNumberProperty<int>("camera", node.camera, o);
6893 }
6894
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02006895 if (node.weights.size() > 0) {
6896 SerializeNumberArrayProperty<double>("weights", node.weights, o);
6897 }
6898
Jens Olssona9718662018-05-24 15:48:49 +02006899 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02006900 SerializeValue("extras", node.extras, o);
6901 }
6902
Selmar09d2ff12018-03-15 17:30:42 +01006903 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006904 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006905 SerializeNumberArrayProperty<int>("children", node.children, o);
6906}
6907
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006908static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09006909 if (sampler.magFilter != -1) {
6910 SerializeNumberProperty("magFilter", sampler.magFilter, o);
6911 }
6912 if (sampler.minFilter != -1) {
6913 SerializeNumberProperty("minFilter", sampler.minFilter, o);
6914 }
Selmar Koka3595482018-11-09 10:34:39 +01006915 SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006916 SerializeNumberProperty("wrapS", sampler.wrapS, o);
6917 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006918
6919 if (sampler.extras.Type() != NULL_TYPE) {
6920 SerializeValue("extras", sampler.extras, o);
6921 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006922}
6923
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006924static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006925 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006926 SerializeNumberProperty("zfar", camera.zfar, o);
6927 SerializeNumberProperty("znear", camera.znear, o);
6928 SerializeNumberProperty("xmag", camera.xmag, o);
6929 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006930
6931 if (camera.extras.Type() != NULL_TYPE) {
6932 SerializeValue("extras", camera.extras, o);
6933 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006934}
6935
6936static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006937 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006938 SerializeNumberProperty("zfar", camera.zfar, o);
6939 SerializeNumberProperty("znear", camera.znear, o);
6940 if (camera.aspectRatio > 0) {
6941 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
6942 }
6943
6944 if (camera.yfov > 0) {
6945 SerializeNumberProperty("yfov", camera.yfov, o);
6946 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006947
6948 if (camera.extras.Type() != NULL_TYPE) {
6949 SerializeValue("extras", camera.extras, o);
6950 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006951}
6952
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006953static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006954 SerializeStringProperty("type", camera.type, o);
6955 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02006956 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006957 }
6958
6959 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006960 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006961 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05006962 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006963 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006964 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006965 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05006966 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006967 } else {
6968 // ???
6969 }
6970}
6971
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006972static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006973 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
6974
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006975 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006976 SerializeStringProperty("name", scene.name, o);
6977 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006978 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01006979 SerializeValue("extras", scene.extras, o);
6980 }
6981 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006982}
6983
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006984static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006985 if (skin.inverseBindMatrices != -1)
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006986 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
6987
6988 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
6989 SerializeNumberProperty("skeleton", skin.skeleton, o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006990 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006991 SerializeStringProperty("name", skin.name, o);
6992 }
6993}
6994
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006995static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02006996 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02006997 SerializeNumberProperty("sampler", texture.sampler, o);
6998 }
Selmar Kok8eb39042018-10-05 14:29:35 +02006999 if (texture.source > -1) {
7000 SerializeNumberProperty("source", texture.source, o);
7001 }
Christophe820ede82019-07-04 15:21:21 +09007002 if (texture.name.size()) {
7003 SerializeStringProperty("name", texture.name, o);
7004 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007005 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007006 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007007 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007008 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007009}
7010
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007011///
7012/// Serialize all properties except buffers and images.
7013///
7014static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007015 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007016 if (model->accessors.size()) {
7017 json accessors;
7018 JsonReserveArray(accessors, model->accessors.size());
7019 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7020 json accessor;
7021 SerializeGltfAccessor(model->accessors[i], accessor);
7022 JsonPushBack(accessors, std::move(accessor));
7023 }
7024 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007025 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007026
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007027 // ANIMATIONS
7028 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007029 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007030 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007031 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7032 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007033 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007034 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007035 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007036 }
7037 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007038
jrkooncecba5d6c2019-08-29 11:26:22 -05007039 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007040 }
7041
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007042 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007043 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007044 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007045 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007046
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007047 // BUFFERVIEWS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007048 if(model->bufferViews.size()) {
7049 json bufferViews;
7050 JsonReserveArray(bufferViews, model->bufferViews.size());
7051 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7052 json bufferView;
7053 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7054 JsonPushBack(bufferViews, std::move(bufferView));
7055 }
7056 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007057 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007058
7059 // Extensions used
7060 if (model->extensionsUsed.size()) {
Johan Bowald52936a02019-07-17 09:06:45 +02007061 SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007062 }
7063
7064 // Extensions required
7065 if (model->extensionsRequired.size()) {
7066 SerializeStringArrayProperty("extensionsRequired",
7067 model->extensionsRequired, o);
7068 }
7069
7070 // MATERIALS
7071 if (model->materials.size()) {
7072 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007073 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007074 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7075 json material;
7076 SerializeGltfMaterial(model->materials[i], material);
jrkooncecba5d6c2019-08-29 11:26:22 -05007077 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007078 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007079 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007080 }
7081
7082 // MESHES
7083 if (model->meshes.size()) {
7084 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007085 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007086 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7087 json mesh;
7088 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007089 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007090 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007091 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007092 }
7093
7094 // NODES
7095 if (model->nodes.size()) {
7096 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007097 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007098 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7099 json node;
7100 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007101 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007102 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007103 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007104 }
7105
7106 // SCENE
7107 if (model->defaultScene > -1) {
7108 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7109 }
7110
7111 // SCENES
7112 if (model->scenes.size()) {
7113 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007114 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007115 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7116 json currentScene;
7117 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007118 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007119 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007120 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007121 }
7122
7123 // SKINS
7124 if (model->skins.size()) {
7125 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007126 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007127 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7128 json skin;
7129 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007130 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007131 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007132 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007133 }
7134
7135 // TEXTURES
7136 if (model->textures.size()) {
7137 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007138 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007139 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7140 json texture;
7141 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007142 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007143 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007144 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007145 }
7146
7147 // SAMPLERS
7148 if (model->samplers.size()) {
7149 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007150 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007151 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7152 json sampler;
7153 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007154 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007155 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007156 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007157 }
7158
7159 // CAMERAS
7160 if (model->cameras.size()) {
7161 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007162 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007163 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7164 json camera;
7165 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007166 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007167 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007168 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007169 }
7170
7171 // EXTENSIONS
7172 SerializeExtensionMap(model->extensions, o);
7173
7174 // LIGHTS as KHR_lights_cmn
7175 if (model->lights.size()) {
7176 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007177 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007178 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7179 json light;
7180 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007181 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007182 }
7183 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007184 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007185 json ext_j;
7186
jrkooncecba5d6c2019-08-29 11:26:22 -05007187 {
7188 json_const_iterator it;
7189 if (!FindMember(o, "extensions", it)) {
7190 JsonAssign(ext_j, GetValue(it));
7191 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007192 }
7193
jrkooncecba5d6c2019-08-29 11:26:22 -05007194 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007195
jrkooncecba5d6c2019-08-29 11:26:22 -05007196 JsonAddMember(o, "extensions", std::move(ext_j));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007197 }
7198
7199 // EXTRAS
7200 if (model->extras.Type() != NULL_TYPE) {
7201 SerializeValue("extras", model->extras, o);
7202 }
7203}
7204
Johan Bowald52936a02019-07-17 09:06:45 +02007205static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007206 stream << content << std::endl;
7207 return true;
7208}
7209
7210static bool WriteGltfFile(const std::string &output,
7211 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007212#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007213#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007214 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007215#elif defined(__GLIBCXX__)
7216 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
7217 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
7218 std::ostream gltfFile(&wfile_buf);
7219 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007220#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007221 std::ofstream gltfFile(output.c_str());
7222 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007223#endif
7224#else
7225 std::ofstream gltfFile(output.c_str());
7226 if (!gltfFile.is_open()) return false;
7227#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007228 return WriteGltfStream(gltfFile, content);
7229}
7230
7231static void WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007232 const std::string &content,
7233 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007234 const std::string header = "glTF";
7235 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007236
Syoyo Fujita1d205202019-11-16 17:00:17 +09007237 // https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
7238 auto roundUp = [](uint32_t numToRound, uint32_t multiple)
7239 {
7240 if (multiple == 0)
7241 return numToRound;
7242
7243 uint32_t remainder = numToRound % multiple;
7244 if (remainder == 0)
7245 return numToRound;
7246
7247 return numToRound + multiple - remainder;
7248 };
7249
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007250 const uint32_t padding_size = roundUp(uint32_t(content.size()), 4) - uint32_t(content.size());
Syoyo Fujita1d205202019-11-16 17:00:17 +09007251
7252 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
7253 // Chunk data must be located at 4-byte boundary.
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007254 const uint32_t length = 12 + 8 + roundUp(uint32_t(content.size()), 4)+
7255 (binBuffer.size()?(8+roundUp(uint32_t(binBuffer.size()),4)) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007256
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007257 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007258 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7259 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7260
7261 // JSON chunk info, then JSON data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007262 const uint32_t model_length = uint32_t(content.size()) + padding_size;
7263 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007264 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007265 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007266 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007267 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007268 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007269
7270 // Chunk must be multiplies of 4, so pad with spaces
7271 if (padding_size > 0) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007272 const std::string padding = std::string(size_t(padding_size), ' ');
7273 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007274 }
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007275 if (binBuffer.size() > 0){
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007276 const uint32_t bin_padding_size = roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size());
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007277 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007278 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7279 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007280 stream.write(reinterpret_cast<const char *>(&bin_length),
7281 sizeof(bin_length));
7282 stream.write(reinterpret_cast<const char *>(&bin_format),
7283 sizeof(bin_format));
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007284 stream.write(reinterpret_cast<const char *>(binBuffer.data()), std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007285 // Chunksize must be multiplies of 4, so pad with zeroes
7286 if (bin_padding_size > 0) {
7287 const std::vector<unsigned char> padding = std::vector<unsigned char>(size_t(bin_padding_size), 0);
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007288 stream.write(reinterpret_cast<const char *>(padding.data()), std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007289 }
7290 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007291}
7292
7293static void WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007294 const std::string &content,
7295 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007296#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007297#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007298 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007299#elif defined(__GLIBCXX__)
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007300 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), _O_WRONLY | _O_BINARY);
7301 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
7302 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007303#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007304 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007305#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007306#else
7307 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007308#endif
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007309 WriteBinaryGltfStream(gltfFile, content,binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007310}
7311
7312bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7313 bool prettyPrint = true,
7314 bool writeBinary = false) {
7315 JsonDocument output;
7316
7317 /// Serialize all properties except buffers and images.
7318 SerializeGltfModel(model, output);
7319
7320 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007321 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007322 if(model->buffers.size()) {
7323 json buffers;
7324 JsonReserveArray(buffers, model->buffers.size());
7325 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7326 json buffer;
7327 if (writeBinary && i==0 && model->buffers[i].uri.empty()){
7328 SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
7329 } else {
7330 SerializeGltfBuffer(model->buffers[i], buffer);
7331 }
7332 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007333 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007334 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007335 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007336
7337 // IMAGES
7338 if (model->images.size()) {
7339 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007340 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007341 for (unsigned int i = 0; i < model->images.size(); ++i) {
7342 json image;
7343
7344 std::string dummystring = "";
FsiGuy00015623db855c62020-03-09 16:57:21 -05007345 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7346 // enabled, since we won't write separate images when writing to a stream we
7347 UpdateImageObject(model->images[i], dummystring, int(i), false,
7348 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007349 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007350 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007351 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007352 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007353 }
7354
7355 if (writeBinary) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007356 WriteBinaryGltfStream(stream, JsonToString(output),binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007357 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05007358 WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007359 }
7360
7361 return true;
7362}
7363
7364bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7365 bool embedImages = false,
7366 bool embedBuffers = false,
7367 bool prettyPrint = true,
7368 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007369 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007370 std::string defaultBinFilename = GetBaseFilename(filename);
7371 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007372 std::string::size_type pos =
7373 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007374
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007375 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007376 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007377 }
johan bowald642a3432018-04-01 12:37:18 +02007378 std::string baseDir = GetBaseDir(filename);
7379 if (baseDir.empty()) {
7380 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007381 }
Johan Bowald52936a02019-07-17 09:06:45 +02007382 /// Serialize all properties except buffers and images.
7383 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007384
Selmar Kok7cb31e42018-10-05 16:02:29 +02007385 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007386 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007387 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007388 if (model->buffers.size()) {
7389 json buffers;
7390 JsonReserveArray(buffers, model->buffers.size());
7391 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7392 json buffer;
7393 if (writeBinary && i==0 && model->buffers[i].uri.empty()){
7394 SerializeGltfBufferBin(model->buffers[i], buffer,binBuffer);
7395 } else if (embedBuffers) {
7396 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007397 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007398 std::string binSavePath;
7399 std::string binUri;
7400 if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
7401 binUri = model->buffers[i].uri;
7402 } else {
7403 binUri = defaultBinFilename + defaultBinFileExt;
7404 bool inUse = true;
7405 int numUsed = 0;
7406 while (inUse) {
7407 inUse = false;
7408 for (const std::string &usedName : usedUris) {
7409 if (binUri.compare(usedName) != 0) continue;
7410 inUse = true;
7411 binUri = defaultBinFilename + std::to_string(numUsed++) +
7412 defaultBinFileExt;
7413 break;
7414 }
Selmar Kokc884e582018-10-05 16:25:54 +02007415 }
7416 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007417 usedUris.push_back(binUri);
7418 binSavePath = JoinPath(baseDir, binUri);
7419 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7420 binUri)) {
7421 return false;
7422 }
Selmar Kokc884e582018-10-05 16:25:54 +02007423 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007424 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007425 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007426 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007427 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007428
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007429 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007430 if (model->images.size()) {
7431 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007432 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007433 for (unsigned int i = 0; i < model->images.size(); ++i) {
7434 json image;
johan bowald642a3432018-04-01 12:37:18 +02007435
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007436 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007437 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007438 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007439 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007440 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007441 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007442 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007443
David Harmonda9eac22018-08-30 08:06:05 -04007444 if (writeBinary) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007445 WriteBinaryGltfFile(filename, JsonToString(output),binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007446 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05007447 WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007448 }
7449
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007450 return true;
7451}
7452
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007453} // namespace tinygltf
7454
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007455#ifdef __clang__
7456#pragma clang diagnostic pop
7457#endif
7458
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007459#endif // TINYGLTF_IMPLEMENTATION