tiny_gltf 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.travis.yml +7 -0
  4. data/.yardopts +2 -0
  5. data/Gemfile +6 -0
  6. data/Gemfile.lock +31 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +63 -0
  9. data/Rakefile +24 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/ext/tiny_gltf/extconf.rb +5 -0
  13. data/ext/tiny_gltf/json.hpp +14722 -0
  14. data/ext/tiny_gltf/rb_tiny_gltf.cpp +55 -0
  15. data/ext/tiny_gltf/rb_tiny_gltf.h +118 -0
  16. data/ext/tiny_gltf/rb_tiny_gltf_accessor.cpp +42 -0
  17. data/ext/tiny_gltf/rb_tiny_gltf_animation.cpp +21 -0
  18. data/ext/tiny_gltf/rb_tiny_gltf_animation_channel.cpp +13 -0
  19. data/ext/tiny_gltf/rb_tiny_gltf_animation_sampler.cpp +16 -0
  20. data/ext/tiny_gltf/rb_tiny_gltf_asset.cpp +15 -0
  21. data/ext/tiny_gltf/rb_tiny_gltf_buffer.cpp +16 -0
  22. data/ext/tiny_gltf/rb_tiny_gltf_buffer_view.cpp +16 -0
  23. data/ext/tiny_gltf/rb_tiny_gltf_camera.cpp +29 -0
  24. data/ext/tiny_gltf/rb_tiny_gltf_extension_map.cpp +12 -0
  25. data/ext/tiny_gltf/rb_tiny_gltf_image.cpp +25 -0
  26. data/ext/tiny_gltf/rb_tiny_gltf_init.c +81 -0
  27. data/ext/tiny_gltf/rb_tiny_gltf_light.cpp +16 -0
  28. data/ext/tiny_gltf/rb_tiny_gltf_material.cpp +14 -0
  29. data/ext/tiny_gltf/rb_tiny_gltf_mesh.cpp +37 -0
  30. data/ext/tiny_gltf/rb_tiny_gltf_model.cpp +52 -0
  31. data/ext/tiny_gltf/rb_tiny_gltf_node.cpp +67 -0
  32. data/ext/tiny_gltf/rb_tiny_gltf_parameter_map.cpp +39 -0
  33. data/ext/tiny_gltf/rb_tiny_gltf_primitive.cpp +35 -0
  34. data/ext/tiny_gltf/rb_tiny_gltf_sampler.cpp +16 -0
  35. data/ext/tiny_gltf/rb_tiny_gltf_scene.cpp +17 -0
  36. data/ext/tiny_gltf/rb_tiny_gltf_skin.cpp +17 -0
  37. data/ext/tiny_gltf/rb_tiny_gltf_texture.cpp +14 -0
  38. data/ext/tiny_gltf/rb_tiny_gltf_types.cpp +229 -0
  39. data/ext/tiny_gltf/rb_tiny_gltf_value.cpp +32 -0
  40. data/ext/tiny_gltf/stb_image.h +6509 -0
  41. data/ext/tiny_gltf/stb_image_write.h +1831 -0
  42. data/ext/tiny_gltf/tiny_gltf.h +4830 -0
  43. data/lib/tiny_gltf.rb +260 -0
  44. data/lib/tiny_gltf/version.rb +3 -0
  45. data/tiny_gltf.gemspec +43 -0
  46. metadata +189 -0
@@ -0,0 +1,4830 @@
1
+ //
2
+ // Header-only tiny glTF 2.0 loader and serializer.
3
+ //
4
+ //
5
+ // The MIT License (MIT)
6
+ //
7
+ // Copyright (c) 2015 - 2018 Syoyo Fujita, Aurélien Chatelain and many
8
+ // contributors.
9
+ //
10
+ // 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:
16
+ //
17
+ // The above copyright notice and this permission notice shall be included in
18
+ // all copies or substantial portions of the Software.
19
+ //
20
+ // 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
+
28
+ // Version:
29
+ // - v2.0.1 Add comparsion feature(Thanks to @Selmar).
30
+ // - v2.0.0 glTF 2.0!.
31
+ //
32
+ // Tiny glTF loader is using following third party libraries:
33
+ //
34
+ // - jsonhpp: C++ JSON library.
35
+ // - base64: base64 decode/encode library.
36
+ // - stb_image: Image loading library.
37
+ //
38
+ #ifndef TINY_GLTF_H_
39
+ #define TINY_GLTF_H_
40
+
41
+ #include <array>
42
+ #include <cassert>
43
+ #include <cstdint>
44
+ #include <cstdlib>
45
+ #include <cstring>
46
+ #include <map>
47
+ #include <string>
48
+ #include <vector>
49
+
50
+ namespace tinygltf {
51
+
52
+ #define TINYGLTF_MODE_POINTS (0)
53
+ #define TINYGLTF_MODE_LINE (1)
54
+ #define TINYGLTF_MODE_LINE_LOOP (2)
55
+ #define TINYGLTF_MODE_TRIANGLES (4)
56
+ #define TINYGLTF_MODE_TRIANGLE_STRIP (5)
57
+ #define TINYGLTF_MODE_TRIANGLE_FAN (6)
58
+
59
+ #define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
60
+ #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
61
+ #define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
62
+ #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
63
+ #define TINYGLTF_COMPONENT_TYPE_INT (5124)
64
+ #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
65
+ #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
66
+ #define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
67
+
68
+ #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
69
+ #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
70
+ #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
71
+ #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
72
+ #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
73
+ #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
74
+
75
+ #define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
76
+ #define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
77
+ #define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
78
+
79
+ // Redeclarations of the above for technique.parameters.
80
+ #define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
81
+ #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
82
+ #define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
83
+ #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
84
+ #define TINYGLTF_PARAMETER_TYPE_INT (5124)
85
+ #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
86
+ #define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
87
+
88
+ #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
89
+ #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
90
+ #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
91
+
92
+ #define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
93
+ #define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
94
+ #define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
95
+
96
+ #define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
97
+ #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
98
+ #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
99
+ #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
100
+
101
+ #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
102
+ #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
103
+ #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
104
+
105
+ #define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
106
+
107
+ // End parameter types
108
+
109
+ #define TINYGLTF_TYPE_VEC2 (2)
110
+ #define TINYGLTF_TYPE_VEC3 (3)
111
+ #define TINYGLTF_TYPE_VEC4 (4)
112
+ #define TINYGLTF_TYPE_MAT2 (32 + 2)
113
+ #define TINYGLTF_TYPE_MAT3 (32 + 3)
114
+ #define TINYGLTF_TYPE_MAT4 (32 + 4)
115
+ #define TINYGLTF_TYPE_SCALAR (64 + 1)
116
+ #define TINYGLTF_TYPE_VECTOR (64 + 4)
117
+ #define TINYGLTF_TYPE_MATRIX (64 + 16)
118
+
119
+ #define TINYGLTF_IMAGE_FORMAT_JPEG (0)
120
+ #define TINYGLTF_IMAGE_FORMAT_PNG (1)
121
+ #define TINYGLTF_IMAGE_FORMAT_BMP (2)
122
+ #define TINYGLTF_IMAGE_FORMAT_GIF (3)
123
+
124
+ #define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
125
+ #define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
126
+ #define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
127
+ #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
128
+ #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
129
+
130
+ #define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
131
+ #define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
132
+
133
+ #define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
134
+ #define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
135
+
136
+ #define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
137
+ #define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
138
+
139
+ #define TINYGLTF_DOUBLE_EPS (1.e-12)
140
+ #define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
141
+
142
+ typedef enum {
143
+ NULL_TYPE = 0,
144
+ NUMBER_TYPE = 1,
145
+ INT_TYPE = 2,
146
+ BOOL_TYPE = 3,
147
+ STRING_TYPE = 4,
148
+ ARRAY_TYPE = 5,
149
+ BINARY_TYPE = 6,
150
+ OBJECT_TYPE = 7
151
+ } Type;
152
+
153
+ static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
154
+ if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
155
+ return 1;
156
+ } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
157
+ return 1;
158
+ } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
159
+ return 2;
160
+ } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
161
+ return 2;
162
+ } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
163
+ return 4;
164
+ } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
165
+ return 4;
166
+ } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
167
+ return 4;
168
+ } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
169
+ return 8;
170
+ } else {
171
+ // Unknown componenty type
172
+ return -1;
173
+ }
174
+ }
175
+
176
+ static inline int32_t GetTypeSizeInBytes(uint32_t ty) {
177
+ if (ty == TINYGLTF_TYPE_SCALAR) {
178
+ return 1;
179
+ } else if (ty == TINYGLTF_TYPE_VEC2) {
180
+ return 2;
181
+ } else if (ty == TINYGLTF_TYPE_VEC3) {
182
+ return 3;
183
+ } else if (ty == TINYGLTF_TYPE_VEC4) {
184
+ return 4;
185
+ } else if (ty == TINYGLTF_TYPE_MAT2) {
186
+ return 4;
187
+ } else if (ty == TINYGLTF_TYPE_MAT3) {
188
+ return 9;
189
+ } else if (ty == TINYGLTF_TYPE_MAT4) {
190
+ return 16;
191
+ } else {
192
+ // Unknown componenty type
193
+ return -1;
194
+ }
195
+ }
196
+
197
+ bool IsDataURI(const std::string &in);
198
+ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
199
+ const std::string &in, size_t reqBytes, bool checkSize);
200
+
201
+ #ifdef __clang__
202
+ #pragma clang diagnostic push
203
+ // Suppress warning for : static Value null_value
204
+ // https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
205
+ #pragma clang diagnostic ignored "-Wexit-time-destructors"
206
+ #pragma clang diagnostic ignored "-Wpadded"
207
+ #endif
208
+
209
+ // Simple class to represent JSON object
210
+ class Value {
211
+ public:
212
+ typedef std::vector<Value> Array;
213
+ typedef std::map<std::string, Value> Object;
214
+
215
+ Value() : type_(NULL_TYPE) {}
216
+
217
+ explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
218
+ explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; }
219
+ explicit Value(double n) : type_(NUMBER_TYPE) { number_value_ = n; }
220
+ explicit Value(const std::string &s) : type_(STRING_TYPE) {
221
+ string_value_ = s;
222
+ }
223
+ explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
224
+ binary_value_.resize(n);
225
+ memcpy(binary_value_.data(), p, n);
226
+ }
227
+ explicit Value(const Array &a) : type_(ARRAY_TYPE) {
228
+ array_value_ = Array(a);
229
+ }
230
+ explicit Value(const Object &o) : type_(OBJECT_TYPE) {
231
+ object_value_ = Object(o);
232
+ }
233
+
234
+ char Type() const { return static_cast<const char>(type_); }
235
+
236
+ bool IsBool() const { return (type_ == BOOL_TYPE); }
237
+
238
+ bool IsInt() const { return (type_ == INT_TYPE); }
239
+
240
+ bool IsNumber() const { return (type_ == NUMBER_TYPE); }
241
+
242
+ bool IsString() const { return (type_ == STRING_TYPE); }
243
+
244
+ bool IsBinary() const { return (type_ == BINARY_TYPE); }
245
+
246
+ bool IsArray() const { return (type_ == ARRAY_TYPE); }
247
+
248
+ bool IsObject() const { return (type_ == OBJECT_TYPE); }
249
+
250
+ // Accessor
251
+ template <typename T>
252
+ const T &Get() const;
253
+ template <typename T>
254
+ T &Get();
255
+
256
+ // Lookup value from an array
257
+ const Value &Get(int idx) const {
258
+ static Value null_value;
259
+ assert(IsArray());
260
+ assert(idx >= 0);
261
+ return (static_cast<size_t>(idx) < array_value_.size())
262
+ ? array_value_[static_cast<size_t>(idx)]
263
+ : null_value;
264
+ }
265
+
266
+ // Lookup value from a key-value pair
267
+ const Value &Get(const std::string &key) const {
268
+ static Value null_value;
269
+ assert(IsObject());
270
+ Object::const_iterator it = object_value_.find(key);
271
+ return (it != object_value_.end()) ? it->second : null_value;
272
+ }
273
+
274
+ size_t ArrayLen() const {
275
+ if (!IsArray()) return 0;
276
+ return array_value_.size();
277
+ }
278
+
279
+ // Valid only for object type.
280
+ bool Has(const std::string &key) const {
281
+ if (!IsObject()) return false;
282
+ Object::const_iterator it = object_value_.find(key);
283
+ return (it != object_value_.end()) ? true : false;
284
+ }
285
+
286
+ // List keys
287
+ std::vector<std::string> Keys() const {
288
+ std::vector<std::string> keys;
289
+ if (!IsObject()) return keys; // empty
290
+
291
+ for (Object::const_iterator it = object_value_.begin();
292
+ it != object_value_.end(); ++it) {
293
+ keys.push_back(it->first);
294
+ }
295
+
296
+ return keys;
297
+ }
298
+
299
+ size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
300
+
301
+ bool operator==(const tinygltf::Value &other) const;
302
+
303
+ protected:
304
+ int type_;
305
+
306
+ int int_value_;
307
+ double number_value_;
308
+ std::string string_value_;
309
+ std::vector<unsigned char> binary_value_;
310
+ Array array_value_;
311
+ Object object_value_;
312
+ bool boolean_value_;
313
+ };
314
+
315
+ #ifdef __clang__
316
+ #pragma clang diagnostic pop
317
+ #endif
318
+
319
+ #define TINYGLTF_VALUE_GET(ctype, var) \
320
+ template <> \
321
+ inline const ctype &Value::Get<ctype>() const { \
322
+ return var; \
323
+ } \
324
+ template <> \
325
+ inline ctype &Value::Get<ctype>() { \
326
+ return var; \
327
+ }
328
+ TINYGLTF_VALUE_GET(bool, boolean_value_)
329
+ TINYGLTF_VALUE_GET(double, number_value_)
330
+ TINYGLTF_VALUE_GET(int, int_value_)
331
+ TINYGLTF_VALUE_GET(std::string, string_value_)
332
+ TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
333
+ TINYGLTF_VALUE_GET(Value::Array, array_value_)
334
+ TINYGLTF_VALUE_GET(Value::Object, object_value_)
335
+ #undef TINYGLTF_VALUE_GET
336
+
337
+ #ifdef __clang__
338
+ #pragma clang diagnostic push
339
+ #pragma clang diagnostic ignored "-Wc++98-compat"
340
+ #pragma clang diagnostic ignored "-Wpadded"
341
+ #endif
342
+
343
+ /// Agregate object for representing a color
344
+ using ColorValue = std::array<double, 4>;
345
+
346
+ struct Parameter {
347
+ bool bool_value = false;
348
+ bool has_number_value = false;
349
+ std::string string_value;
350
+ std::vector<double> number_array;
351
+ std::map<std::string, double> json_double_value;
352
+ double number_value = 0.0;
353
+ // context sensitive methods. depending the type of the Parameter you are
354
+ // accessing, these are either valid or not
355
+ // If this parameter represent a texture map in a material, will return the
356
+ // texture index
357
+
358
+ /// Return the index of a texture if this Parameter is a texture map.
359
+ /// Returned value is only valid if the parameter represent a texture from a
360
+ /// material
361
+ int TextureIndex() const {
362
+ const auto it = json_double_value.find("index");
363
+ if (it != std::end(json_double_value)) {
364
+ return int(it->second);
365
+ }
366
+ return -1;
367
+ }
368
+
369
+ /// Material factor, like the roughness or metalness of a material
370
+ /// Returned value is only valid if the parameter represent a texture from a
371
+ /// material
372
+ double Factor() const { return number_value; }
373
+
374
+ /// Return the color of a material
375
+ /// Returned value is only valid if the parameter represent a texture from a
376
+ /// material
377
+ ColorValue ColorFactor() const {
378
+ return {
379
+ {// this agregate intialize the std::array object, and uses C++11 RVO.
380
+ number_array[0], number_array[1], number_array[2],
381
+ (number_array.size() > 3 ? number_array[3] : 1.0)}};
382
+ }
383
+
384
+ bool operator==(const Parameter &) const;
385
+ };
386
+
387
+ #ifdef __clang__
388
+ #pragma clang diagnostic pop
389
+ #endif
390
+
391
+ #ifdef __clang__
392
+ #pragma clang diagnostic push
393
+ #pragma clang diagnostic ignored "-Wpadded"
394
+ #endif
395
+
396
+ typedef std::map<std::string, Parameter> ParameterMap;
397
+ typedef std::map<std::string, Value> ExtensionMap;
398
+
399
+ struct AnimationChannel {
400
+ int sampler; // required
401
+ int target_node; // required (index of the node to target)
402
+ std::string target_path; // required in ["translation", "rotation", "scale",
403
+ // "weights"]
404
+ Value extras;
405
+
406
+ AnimationChannel() : sampler(-1), target_node(-1) {}
407
+ bool operator==(const AnimationChannel &) const;
408
+ };
409
+
410
+ struct AnimationSampler {
411
+ int input; // required
412
+ int output; // required
413
+ std::string interpolation; // in ["LINEAR", "STEP", "CATMULLROMSPLINE",
414
+ // "CUBICSPLINE"], default "LINEAR"
415
+ Value extras;
416
+
417
+ AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
418
+ bool operator==(const AnimationSampler &) const;
419
+ };
420
+
421
+ struct Animation {
422
+ std::string name;
423
+ std::vector<AnimationChannel> channels;
424
+ std::vector<AnimationSampler> samplers;
425
+ Value extras;
426
+
427
+ bool operator==(const Animation &) const;
428
+ };
429
+
430
+ struct Skin {
431
+ std::string name;
432
+ int inverseBindMatrices; // required here but not in the spec
433
+ int skeleton; // The index of the node used as a skeleton root
434
+ std::vector<int> joints; // Indices of skeleton nodes
435
+
436
+ Skin() {
437
+ inverseBindMatrices = -1;
438
+ skeleton = -1;
439
+ }
440
+ bool operator==(const Skin &) const;
441
+ };
442
+
443
+ struct Sampler {
444
+ std::string name;
445
+ int minFilter; // ["NEAREST", "LINEAR", "NEAREST_MIPMAP_LINEAR",
446
+ // "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR",
447
+ // "LINEAR_MIPMAP_LINEAR"]
448
+ int magFilter; // ["NEAREST", "LINEAR"]
449
+ int wrapS; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
450
+ // "REPEAT"
451
+ int wrapT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
452
+ // "REPEAT"
453
+ int wrapR; // TinyGLTF extension
454
+ Value extras;
455
+
456
+ Sampler()
457
+ : wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
458
+ wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
459
+ bool operator==(const Sampler &) const;
460
+ };
461
+
462
+ struct Image {
463
+ std::string name;
464
+ int width;
465
+ int height;
466
+ int component;
467
+ std::vector<unsigned char> image;
468
+ int bufferView; // (required if no uri)
469
+ std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
470
+ // "image/bmp", "image/gif"]
471
+ std::string uri; // (required if no mimeType)
472
+ Value extras;
473
+ ExtensionMap extensions;
474
+
475
+ // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
476
+ // compressed for "image/jpeg" mime) This feature is good if you use custom
477
+ // image loader function. (e.g. delayed decoding of images for faster glTF
478
+ // parsing) Default parser for Image does not provide as-is loading feature at
479
+ // the moment. (You can manipulate this by providing your own LoadImageData
480
+ // function)
481
+ bool as_is;
482
+
483
+ Image() : as_is(false) {
484
+ bufferView = -1;
485
+ width = -1;
486
+ height = -1;
487
+ component = -1;
488
+ }
489
+ bool operator==(const Image &) const;
490
+ };
491
+
492
+ struct Texture {
493
+ std::string name;
494
+
495
+ int sampler;
496
+ int source;
497
+ Value extras;
498
+ ExtensionMap extensions;
499
+
500
+ Texture() : sampler(-1), source(-1) {}
501
+ bool operator==(const Texture &) const;
502
+ };
503
+
504
+ // Each extension should be stored in a ParameterMap.
505
+ // members not in the values could be included in the ParameterMap
506
+ // to keep a single material model
507
+ struct Material {
508
+ std::string name;
509
+
510
+ ParameterMap values; // PBR metal/roughness workflow
511
+ ParameterMap additionalValues; // normal/occlusion/emissive values
512
+
513
+ ExtensionMap extensions;
514
+ Value extras;
515
+
516
+ bool operator==(const Material &) const;
517
+ };
518
+
519
+ struct BufferView {
520
+ std::string name;
521
+ int buffer; // Required
522
+ size_t byteOffset; // minimum 0, default 0
523
+ size_t byteLength; // required, minimum 1
524
+ size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
525
+ // understood to be tightly packed
526
+ int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
527
+ Value extras;
528
+
529
+ BufferView() : byteOffset(0), byteStride(0) {}
530
+ bool operator==(const BufferView &) const;
531
+ };
532
+
533
+ struct Accessor {
534
+ int bufferView; // optional in spec but required here since sparse accessor
535
+ // are not supported
536
+ std::string name;
537
+ size_t byteOffset;
538
+ bool normalized; // optinal.
539
+ int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
540
+ size_t count; // required
541
+ int type; // (required) One of TINYGLTF_TYPE_*** ..
542
+ Value extras;
543
+
544
+ std::vector<double> minValues; // optional
545
+ std::vector<double> maxValues; // optional
546
+
547
+ // TODO(syoyo): "sparse"
548
+
549
+ ///
550
+ /// Utility function to compute byteStride for a given bufferView object.
551
+ /// Returns -1 upon invalid glTF value or parameter configuration.
552
+ ///
553
+ int ByteStride(const BufferView &bufferViewObject) const {
554
+ if (bufferViewObject.byteStride == 0) {
555
+ // Assume data is tightly packed.
556
+ int componentSizeInBytes =
557
+ GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
558
+ if (componentSizeInBytes <= 0) {
559
+ return -1;
560
+ }
561
+
562
+ int typeSizeInBytes = GetTypeSizeInBytes(static_cast<uint32_t>(type));
563
+ if (typeSizeInBytes <= 0) {
564
+ return -1;
565
+ }
566
+
567
+ return componentSizeInBytes * typeSizeInBytes;
568
+ } else {
569
+ // Check if byteStride is a mulple of the size of the accessor's component
570
+ // type.
571
+ int componentSizeInBytes =
572
+ GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
573
+ if (componentSizeInBytes <= 0) {
574
+ return -1;
575
+ }
576
+
577
+ if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
578
+ return -1;
579
+ }
580
+ return static_cast<int>(bufferViewObject.byteStride);
581
+ }
582
+
583
+ return 0;
584
+ }
585
+
586
+ Accessor() { bufferView = -1; }
587
+ bool operator==(const tinygltf::Accessor &) const;
588
+ };
589
+
590
+ struct PerspectiveCamera {
591
+ double aspectRatio; // min > 0
592
+ double yfov; // required. min > 0
593
+ double zfar; // min > 0
594
+ double znear; // required. min > 0
595
+
596
+ PerspectiveCamera()
597
+ : aspectRatio(0.0),
598
+ yfov(0.0),
599
+ zfar(0.0) // 0 = use infinite projecton matrix
600
+ ,
601
+ znear(0.0) {}
602
+ bool operator==(const PerspectiveCamera &) const;
603
+
604
+ ExtensionMap extensions;
605
+ Value extras;
606
+ };
607
+
608
+ struct OrthographicCamera {
609
+ double xmag; // required. must not be zero.
610
+ double ymag; // required. must not be zero.
611
+ double zfar; // required. `zfar` must be greater than `znear`.
612
+ double znear; // required
613
+
614
+ OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
615
+ bool operator==(const OrthographicCamera &) const;
616
+
617
+ ExtensionMap extensions;
618
+ Value extras;
619
+ };
620
+
621
+ struct Camera {
622
+ std::string type; // required. "perspective" or "orthographic"
623
+ std::string name;
624
+
625
+ PerspectiveCamera perspective;
626
+ OrthographicCamera orthographic;
627
+
628
+ Camera() {}
629
+ bool operator==(const Camera &) const;
630
+
631
+ ExtensionMap extensions;
632
+ Value extras;
633
+ };
634
+
635
+ struct Primitive {
636
+ std::map<std::string, int> attributes; // (required) A dictionary object of
637
+ // integer, where each integer
638
+ // is the index of the accessor
639
+ // containing an attribute.
640
+ int material; // The index of the material to apply to this primitive
641
+ // when rendering.
642
+ int indices; // The index of the accessor that contains the indices.
643
+ int mode; // one of TINYGLTF_MODE_***
644
+ std::vector<std::map<std::string, int> > targets; // array of morph targets,
645
+ // where each target is a dict with attribues in ["POSITION, "NORMAL",
646
+ // "TANGENT"] pointing
647
+ // to their corresponding accessors
648
+ Value extras;
649
+
650
+ Primitive() {
651
+ material = -1;
652
+ indices = -1;
653
+ }
654
+ bool operator==(const Primitive &) const;
655
+ };
656
+
657
+ struct Mesh {
658
+ std::string name;
659
+ std::vector<Primitive> primitives;
660
+ std::vector<double> weights; // weights to be applied to the Morph Targets
661
+ std::vector<std::map<std::string, int> > targets;
662
+ ExtensionMap extensions;
663
+ Value extras;
664
+
665
+ bool operator==(const Mesh &) const;
666
+ };
667
+
668
+ class Node {
669
+ public:
670
+ Node() : camera(-1), skin(-1), mesh(-1) {}
671
+
672
+ Node(const Node &rhs) {
673
+ camera = rhs.camera;
674
+
675
+ name = rhs.name;
676
+ skin = rhs.skin;
677
+ mesh = rhs.mesh;
678
+ children = rhs.children;
679
+ rotation = rhs.rotation;
680
+ scale = rhs.scale;
681
+ translation = rhs.translation;
682
+ matrix = rhs.matrix;
683
+ weights = rhs.weights;
684
+
685
+ extensions = rhs.extensions;
686
+ extras = rhs.extras;
687
+ }
688
+ ~Node() {}
689
+ bool operator==(const Node &) const;
690
+
691
+ int camera; // the index of the camera referenced by this node
692
+
693
+ std::string name;
694
+ int skin;
695
+ int mesh;
696
+ std::vector<int> children;
697
+ std::vector<double> rotation; // length must be 0 or 4
698
+ std::vector<double> scale; // length must be 0 or 3
699
+ std::vector<double> translation; // length must be 0 or 3
700
+ std::vector<double> matrix; // length must be 0 or 16
701
+ std::vector<double> weights; // The weights of the instantiated Morph Target
702
+
703
+ ExtensionMap extensions;
704
+ Value extras;
705
+ };
706
+
707
+ struct Buffer {
708
+ std::string name;
709
+ std::vector<unsigned char> data;
710
+ std::string
711
+ uri; // considered as required here but not in the spec (need to clarify)
712
+ Value extras;
713
+
714
+ bool operator==(const Buffer &) const;
715
+ };
716
+
717
+ struct Asset {
718
+ std::string version; // required
719
+ std::string generator;
720
+ std::string minVersion;
721
+ std::string copyright;
722
+ ExtensionMap extensions;
723
+ Value extras;
724
+
725
+ bool operator==(const Asset &) const;
726
+ };
727
+
728
+ struct Scene {
729
+ std::string name;
730
+ std::vector<int> nodes;
731
+
732
+ ExtensionMap extensions;
733
+ Value extras;
734
+
735
+ bool operator==(const Scene &) const;
736
+ };
737
+
738
+ struct Light {
739
+ std::string name;
740
+ std::vector<double> color;
741
+ std::string type;
742
+
743
+ bool operator==(const Light &) const;
744
+ };
745
+
746
+ class Model {
747
+ public:
748
+ Model() {}
749
+ ~Model() {}
750
+ bool operator==(const Model &) const;
751
+
752
+ std::vector<Accessor> accessors;
753
+ std::vector<Animation> animations;
754
+ std::vector<Buffer> buffers;
755
+ std::vector<BufferView> bufferViews;
756
+ std::vector<Material> materials;
757
+ std::vector<Mesh> meshes;
758
+ std::vector<Node> nodes;
759
+ std::vector<Texture> textures;
760
+ std::vector<Image> images;
761
+ std::vector<Skin> skins;
762
+ std::vector<Sampler> samplers;
763
+ std::vector<Camera> cameras;
764
+ std::vector<Scene> scenes;
765
+ std::vector<Light> lights;
766
+ ExtensionMap extensions;
767
+
768
+ int defaultScene;
769
+ std::vector<std::string> extensionsUsed;
770
+ std::vector<std::string> extensionsRequired;
771
+
772
+ Asset asset;
773
+
774
+ Value extras;
775
+ };
776
+
777
+ enum SectionCheck {
778
+ NO_REQUIRE = 0x00,
779
+ REQUIRE_SCENE = 0x01,
780
+ REQUIRE_SCENES = 0x02,
781
+ REQUIRE_NODES = 0x04,
782
+ REQUIRE_ACCESSORS = 0x08,
783
+ REQUIRE_BUFFERS = 0x10,
784
+ REQUIRE_BUFFER_VIEWS = 0x20,
785
+ REQUIRE_ALL = 0x3f
786
+ };
787
+
788
+ ///
789
+ /// LoadImageDataFunction type. Signature for custom image loading callbacks.
790
+ ///
791
+ typedef bool (*LoadImageDataFunction)(Image *, std::string *, std::string *,
792
+ int, int, const unsigned char *, int,
793
+ void *);
794
+
795
+ ///
796
+ /// WriteImageDataFunction type. Signature for custom image writing callbacks.
797
+ ///
798
+ typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
799
+ Image *, bool, void *);
800
+
801
+ #ifndef TINYGLTF_NO_STB_IMAGE
802
+ // Declaration of default image loader callback
803
+ bool LoadImageData(Image *image, std::string *err, std::string *warn,
804
+ int req_width, int req_height, const unsigned char *bytes,
805
+ int size, void *);
806
+ #endif
807
+
808
+ #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
809
+ // Declaration of default image writer callback
810
+ bool WriteImageData(const std::string *basepath, const std::string *filename,
811
+ Image *image, bool embedImages, void *);
812
+ #endif
813
+
814
+ ///
815
+ /// FilExistsFunction type. Signature for custom filesystem callbacks.
816
+ ///
817
+ typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
818
+
819
+ ///
820
+ /// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
821
+ ///
822
+ typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
823
+
824
+ ///
825
+ /// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
826
+ ///
827
+ typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
828
+ std::string *, const std::string &,
829
+ void *);
830
+
831
+ ///
832
+ /// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
833
+ ///
834
+ typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
835
+ const std::vector<unsigned char> &,
836
+ void *);
837
+
838
+ ///
839
+ /// A structure containing all required filesystem callbacks and a pointer to
840
+ /// their user data.
841
+ ///
842
+ struct FsCallbacks {
843
+ FileExistsFunction FileExists;
844
+ ExpandFilePathFunction ExpandFilePath;
845
+ ReadWholeFileFunction ReadWholeFile;
846
+ WriteWholeFileFunction WriteWholeFile;
847
+
848
+ void *user_data; // An argument that is passed to all fs callbacks
849
+ };
850
+
851
+ #ifndef TINYGLTF_NO_FS
852
+ // Declaration of default filesystem callbacks
853
+
854
+ bool FileExists(const std::string &abs_filename, void *);
855
+
856
+ std::string ExpandFilePath(const std::string &filepath, void *);
857
+
858
+ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
859
+ const std::string &filepath, void *);
860
+
861
+ bool WriteWholeFile(std::string *err, const std::string &filepath,
862
+ const std::vector<unsigned char> &contents, void *);
863
+ #endif
864
+
865
+ class TinyGLTF {
866
+ public:
867
+ #ifdef __clang__
868
+ #pragma clang diagnostic push
869
+ #pragma clang diagnostic ignored "-Wc++98-compat"
870
+ #endif
871
+
872
+ TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
873
+
874
+ #ifdef __clang__
875
+ #pragma clang diagnostic pop
876
+ #endif
877
+
878
+ ~TinyGLTF() {}
879
+
880
+ ///
881
+ /// Loads glTF ASCII asset from a file.
882
+ /// Set warning message to `warn` for example it fails to load asserts.
883
+ /// Returns false and set error string to `err` if there's an error.
884
+ ///
885
+ bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
886
+ const std::string &filename,
887
+ unsigned int check_sections = REQUIRE_ALL);
888
+
889
+ ///
890
+ /// Loads glTF ASCII asset from string(memory).
891
+ /// `length` = strlen(str);
892
+ /// Set warning message to `warn` for example it fails to load asserts.
893
+ /// Returns false and set error string to `err` if there's an error.
894
+ ///
895
+ bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
896
+ const char *str, const unsigned int length,
897
+ const std::string &base_dir,
898
+ unsigned int check_sections = REQUIRE_ALL);
899
+
900
+ ///
901
+ /// Loads glTF binary asset from a file.
902
+ /// Set warning message to `warn` for example it fails to load asserts.
903
+ /// Returns false and set error string to `err` if there's an error.
904
+ ///
905
+ bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
906
+ const std::string &filename,
907
+ unsigned int check_sections = REQUIRE_ALL);
908
+
909
+ ///
910
+ /// Loads glTF binary asset from memory.
911
+ /// `length` = strlen(str);
912
+ /// Set warning message to `warn` for example it fails to load asserts.
913
+ /// Returns false and set error string to `err` if there's an error.
914
+ ///
915
+ bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
916
+ const unsigned char *bytes,
917
+ const unsigned int length,
918
+ const std::string &base_dir = "",
919
+ unsigned int check_sections = REQUIRE_ALL);
920
+
921
+ ///
922
+ /// Write glTF to file.
923
+ ///
924
+ bool WriteGltfSceneToFile(Model *model, const std::string &filename,
925
+ bool embedImages,
926
+ bool embedBuffers,
927
+ bool prettyPrint,
928
+ bool writeBinary);
929
+
930
+ ///
931
+ /// Set callback to use for loading image data
932
+ ///
933
+ void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
934
+
935
+ ///
936
+ /// Set callback to use for writing image data
937
+ ///
938
+ void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
939
+
940
+ ///
941
+ /// Set callbacks to use for filesystem (fs) access and their user data
942
+ ///
943
+ void SetFsCallbacks(FsCallbacks callbacks);
944
+
945
+ private:
946
+ ///
947
+ /// Loads glTF asset from string(memory).
948
+ /// `length` = strlen(str);
949
+ /// Set warning message to `warn` for example it fails to load asserts
950
+ /// Returns false and set error string to `err` if there's an error.
951
+ ///
952
+ bool LoadFromString(Model *model, std::string *err, std::string *warn,
953
+ const char *str, const unsigned int length,
954
+ const std::string &base_dir, unsigned int check_sections);
955
+
956
+ const unsigned char *bin_data_;
957
+ size_t bin_size_;
958
+ bool is_binary_;
959
+
960
+ FsCallbacks fs = {
961
+ #ifndef TINYGLTF_NO_FS
962
+ &tinygltf::FileExists, &tinygltf::ExpandFilePath,
963
+ &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
964
+
965
+ nullptr // Fs callback user data
966
+ #else
967
+ nullptr, nullptr, nullptr, nullptr,
968
+
969
+ nullptr // Fs callback user data
970
+ #endif
971
+ };
972
+
973
+ LoadImageDataFunction LoadImageData =
974
+ #ifndef TINYGLTF_NO_STB_IMAGE
975
+ &tinygltf::LoadImageData;
976
+ #else
977
+ nullptr;
978
+ #endif
979
+ void *load_image_user_data_ = reinterpret_cast<void *>(&fs);
980
+
981
+ WriteImageDataFunction WriteImageData =
982
+ #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
983
+ &tinygltf::WriteImageData;
984
+ #else
985
+ nullptr;
986
+ #endif
987
+ void *write_image_user_data_ = reinterpret_cast<void *>(&fs);
988
+ };
989
+
990
+ #ifdef __clang__
991
+ #pragma clang diagnostic pop // -Wpadded
992
+ #endif
993
+
994
+ } // namespace tinygltf
995
+
996
+ #endif // TINY_GLTF_H_
997
+
998
+ #if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
999
+ #include <algorithm>
1000
+ //#include <cassert>
1001
+ #ifndef TINYGLTF_NO_FS
1002
+ #include <fstream>
1003
+ #endif
1004
+ #include <sstream>
1005
+
1006
+ #ifdef __clang__
1007
+ // Disable some warnings for external files.
1008
+ #pragma clang diagnostic push
1009
+ #pragma clang diagnostic ignored "-Wfloat-equal"
1010
+ #pragma clang diagnostic ignored "-Wexit-time-destructors"
1011
+ #pragma clang diagnostic ignored "-Wconversion"
1012
+ #pragma clang diagnostic ignored "-Wold-style-cast"
1013
+ #pragma clang diagnostic ignored "-Wglobal-constructors"
1014
+ #pragma clang diagnostic ignored "-Wreserved-id-macro"
1015
+ #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1016
+ #pragma clang diagnostic ignored "-Wpadded"
1017
+ #pragma clang diagnostic ignored "-Wc++98-compat"
1018
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1019
+ #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1020
+ #pragma clang diagnostic ignored "-Wswitch-enum"
1021
+ #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
1022
+ #pragma clang diagnostic ignored "-Wweak-vtables"
1023
+ #pragma clang diagnostic ignored "-Wcovered-switch-default"
1024
+ #if __has_warning("-Wdouble-promotion")
1025
+ #pragma clang diagnostic ignored "-Wdouble-promotion"
1026
+ #endif
1027
+ #if __has_warning("-Wcomma")
1028
+ #pragma clang diagnostic ignored "-Wcomma"
1029
+ #endif
1030
+ #if __has_warning("-Wzero-as-null-pointer-constant")
1031
+ #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1032
+ #endif
1033
+ #if __has_warning("-Wcast-qual")
1034
+ #pragma clang diagnostic ignored "-Wcast-qual"
1035
+ #endif
1036
+ #if __has_warning("-Wmissing-variable-declarations")
1037
+ #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1038
+ #endif
1039
+ #if __has_warning("-Wmissing-prototypes")
1040
+ #pragma clang diagnostic ignored "-Wmissing-prototypes"
1041
+ #endif
1042
+ #if __has_warning("-Wcast-align")
1043
+ #pragma clang diagnostic ignored "-Wcast-align"
1044
+ #endif
1045
+ #if __has_warning("-Wnewline-eof")
1046
+ #pragma clang diagnostic ignored "-Wnewline-eof"
1047
+ #endif
1048
+ #endif
1049
+
1050
+ #include "./json.hpp"
1051
+
1052
+ #ifndef TINYGLTF_NO_STB_IMAGE
1053
+ #include "./stb_image.h"
1054
+ #endif
1055
+
1056
+ #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1057
+ #include "./stb_image_write.h"
1058
+ #endif
1059
+
1060
+ #ifdef __clang__
1061
+ #pragma clang diagnostic pop
1062
+ #endif
1063
+
1064
+ #ifdef _WIN32
1065
+ #include <windows.h>
1066
+ #elif !defined(__ANDROID__)
1067
+ #include <wordexp.h>
1068
+ #endif
1069
+
1070
+ #if defined(__sparcv9)
1071
+ // Big endian
1072
+ #else
1073
+ #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1074
+ #define TINYGLTF_LITTLE_ENDIAN 1
1075
+ #endif
1076
+ #endif
1077
+
1078
+ using nlohmann::json;
1079
+
1080
+ #ifdef __APPLE__
1081
+ #include "TargetConditionals.h"
1082
+ #endif
1083
+
1084
+ #ifdef __clang__
1085
+ #pragma clang diagnostic push
1086
+ #pragma clang diagnostic ignored "-Wc++98-compat"
1087
+ #endif
1088
+
1089
+ namespace tinygltf {
1090
+
1091
+ // Equals function for Value, for recursivity
1092
+ static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1093
+ if (one.Type() != other.Type()) return false;
1094
+
1095
+ switch (one.Type()) {
1096
+ case NULL_TYPE:
1097
+ return true;
1098
+ case BOOL_TYPE:
1099
+ return one.Get<bool>() == other.Get<bool>();
1100
+ case NUMBER_TYPE:
1101
+ return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1102
+ case INT_TYPE:
1103
+ return one.Get<int>() == other.Get<int>();
1104
+ case OBJECT_TYPE: {
1105
+ auto oneObj = one.Get<tinygltf::Value::Object>();
1106
+ auto otherObj = other.Get<tinygltf::Value::Object>();
1107
+ if (oneObj.size() != otherObj.size()) return false;
1108
+ for (auto &it : oneObj) {
1109
+ auto otherIt = otherObj.find(it.first);
1110
+ if (otherIt == otherObj.end()) return false;
1111
+
1112
+ if (!Equals(it.second, otherIt->second)) return false;
1113
+ }
1114
+ return true;
1115
+ }
1116
+ case ARRAY_TYPE: {
1117
+ if (one.Size() != other.Size()) return false;
1118
+ for (int i = 0; i < int(one.Size()); ++i)
1119
+ if (Equals(one.Get(i), other.Get(i))) return false;
1120
+ return true;
1121
+ }
1122
+ case STRING_TYPE:
1123
+ return one.Get<std::string>() == other.Get<std::string>();
1124
+ case BINARY_TYPE:
1125
+ return one.Get<std::vector<unsigned char> >() ==
1126
+ other.Get<std::vector<unsigned char> >();
1127
+ default: {
1128
+ // unhandled type
1129
+ return false;
1130
+ }
1131
+ }
1132
+ }
1133
+
1134
+ // Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
1135
+ static bool Equals(const std::vector<double> &one,
1136
+ const std::vector<double> &other) {
1137
+ if (one.size() != other.size()) return false;
1138
+ for (int i = 0; i < int(one.size()); ++i) {
1139
+ if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1140
+ }
1141
+ return true;
1142
+ }
1143
+
1144
+ bool Accessor::operator==(const Accessor &other) const {
1145
+ return this->bufferView == other.bufferView &&
1146
+ this->byteOffset == other.byteOffset &&
1147
+ this->componentType == other.componentType &&
1148
+ this->count == other.count && this->extras == other.extras &&
1149
+ Equals(this->maxValues, other.maxValues) &&
1150
+ Equals(this->minValues, other.minValues) && this->name == other.name &&
1151
+ this->normalized == other.normalized && this->type == other.type;
1152
+ }
1153
+ bool Animation::operator==(const Animation &other) const {
1154
+ return this->channels == other.channels && this->extras == other.extras &&
1155
+ this->name == other.name && this->samplers == other.samplers;
1156
+ }
1157
+ bool AnimationChannel::operator==(const AnimationChannel &other) const {
1158
+ return this->extras == other.extras &&
1159
+ this->target_node == other.target_node &&
1160
+ this->target_path == other.target_path &&
1161
+ this->sampler == other.sampler;
1162
+ }
1163
+ bool AnimationSampler::operator==(const AnimationSampler &other) const {
1164
+ return this->extras == other.extras && this->input == other.input &&
1165
+ this->interpolation == other.interpolation &&
1166
+ this->output == other.output;
1167
+ }
1168
+ bool Asset::operator==(const Asset &other) const {
1169
+ return this->copyright == other.copyright &&
1170
+ this->extensions == other.extensions && this->extras == other.extras &&
1171
+ this->generator == other.generator &&
1172
+ this->minVersion == other.minVersion && this->version == other.version;
1173
+ }
1174
+ bool Buffer::operator==(const Buffer &other) const {
1175
+ return this->data == other.data && this->extras == other.extras &&
1176
+ this->name == other.name && this->uri == other.uri;
1177
+ }
1178
+ bool BufferView::operator==(const BufferView &other) const {
1179
+ return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1180
+ this->byteOffset == other.byteOffset &&
1181
+ this->byteStride == other.byteStride && this->name == other.name &&
1182
+ this->target == other.target && this->extras == other.extras;
1183
+ }
1184
+ bool Camera::operator==(const Camera &other) const {
1185
+ return this->name == other.name && this->extensions == other.extensions &&
1186
+ this->extras == other.extras &&
1187
+ this->orthographic == other.orthographic &&
1188
+ this->perspective == other.perspective && this->type == other.type;
1189
+ }
1190
+ bool Image::operator==(const Image &other) const {
1191
+ return this->bufferView == other.bufferView &&
1192
+ this->component == other.component && this->extras == other.extras &&
1193
+ this->height == other.height && this->image == other.image &&
1194
+ this->mimeType == other.mimeType && this->name == other.name &&
1195
+ this->uri == other.uri && this->width == other.width;
1196
+ }
1197
+ bool Light::operator==(const Light &other) const {
1198
+ return Equals(this->color, other.color) && this->name == other.name &&
1199
+ this->type == other.type;
1200
+ }
1201
+ bool Material::operator==(const Material &other) const {
1202
+ return this->additionalValues == other.additionalValues &&
1203
+ this->extensions == other.extensions && this->extras == other.extras &&
1204
+ this->name == other.name && this->values == other.values;
1205
+ }
1206
+ bool Mesh::operator==(const Mesh &other) const {
1207
+ return this->extensions == other.extensions && this->extras == other.extras &&
1208
+ this->name == other.name && this->primitives == other.primitives &&
1209
+ this->targets == other.targets && Equals(this->weights, other.weights);
1210
+ }
1211
+ bool Model::operator==(const Model &other) const {
1212
+ return this->accessors == other.accessors &&
1213
+ this->animations == other.animations && this->asset == other.asset &&
1214
+ this->buffers == other.buffers &&
1215
+ this->bufferViews == other.bufferViews &&
1216
+ this->cameras == other.cameras &&
1217
+ this->defaultScene == other.defaultScene &&
1218
+ this->extensions == other.extensions &&
1219
+ this->extensionsRequired == other.extensionsRequired &&
1220
+ this->extensionsUsed == other.extensionsUsed &&
1221
+ this->extras == other.extras && this->images == other.images &&
1222
+ this->lights == other.lights && this->materials == other.materials &&
1223
+ this->meshes == other.meshes && this->nodes == other.nodes &&
1224
+ this->samplers == other.samplers && this->scenes == other.scenes &&
1225
+ this->skins == other.skins && this->textures == other.textures;
1226
+ }
1227
+ bool Node::operator==(const Node &other) const {
1228
+ return this->camera == other.camera && this->children == other.children &&
1229
+ this->extensions == other.extensions && this->extras == other.extras &&
1230
+ Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1231
+ this->name == other.name && Equals(this->rotation, other.rotation) &&
1232
+ Equals(this->scale, other.scale) && this->skin == other.skin &&
1233
+ Equals(this->translation, other.translation) &&
1234
+ Equals(this->weights, other.weights);
1235
+ }
1236
+ bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1237
+ return this->extensions == other.extensions && this->extras == other.extras &&
1238
+ TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1239
+ TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1240
+ TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1241
+ TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
1242
+ }
1243
+ bool Parameter::operator==(const Parameter &other) const {
1244
+ if (this->bool_value != other.bool_value ||
1245
+ this->has_number_value != other.has_number_value)
1246
+ return false;
1247
+
1248
+ if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1249
+ return false;
1250
+
1251
+ if (this->json_double_value.size() != other.json_double_value.size())
1252
+ return false;
1253
+ for (auto &it : this->json_double_value) {
1254
+ auto otherIt = other.json_double_value.find(it.first);
1255
+ if (otherIt == other.json_double_value.end()) return false;
1256
+
1257
+ if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1258
+ }
1259
+
1260
+ if (!Equals(this->number_array, other.number_array)) return false;
1261
+
1262
+ if (this->string_value != other.string_value) return false;
1263
+
1264
+ return true;
1265
+ }
1266
+ bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1267
+ return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1268
+ this->extensions == other.extensions && this->extras == other.extras &&
1269
+ TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1270
+ TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1271
+ TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
1272
+ }
1273
+ bool Primitive::operator==(const Primitive &other) const {
1274
+ return this->attributes == other.attributes && this->extras == other.extras &&
1275
+ this->indices == other.indices && this->material == other.material &&
1276
+ this->mode == other.mode && this->targets == other.targets;
1277
+ }
1278
+ bool Sampler::operator==(const Sampler &other) const {
1279
+ return this->extras == other.extras && this->magFilter == other.magFilter &&
1280
+ this->minFilter == other.minFilter && this->name == other.name &&
1281
+ this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
1282
+ this->wrapT == other.wrapT;
1283
+ }
1284
+ bool Scene::operator==(const Scene &other) const {
1285
+ return this->extensions == other.extensions && this->extras == other.extras &&
1286
+ this->name == other.name && this->nodes == other.nodes;
1287
+ ;
1288
+ }
1289
+ bool Skin::operator==(const Skin &other) const {
1290
+ return this->inverseBindMatrices == other.inverseBindMatrices &&
1291
+ this->joints == other.joints && this->name == other.name &&
1292
+ this->skeleton == other.skeleton;
1293
+ }
1294
+ bool Texture::operator==(const Texture &other) const {
1295
+ return this->extensions == other.extensions && this->extras == other.extras &&
1296
+ this->name == other.name && this->sampler == other.sampler &&
1297
+ this->source == other.source;
1298
+ }
1299
+ bool Value::operator==(const Value &other) const {
1300
+ return Equals(*this, other);
1301
+ }
1302
+
1303
+ static void swap4(unsigned int *val) {
1304
+ #ifdef TINYGLTF_LITTLE_ENDIAN
1305
+ (void)val;
1306
+ #else
1307
+ unsigned int tmp = *val;
1308
+ unsigned char *dst = reinterpret_cast<unsigned char *>(val);
1309
+ unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
1310
+
1311
+ dst[0] = src[3];
1312
+ dst[1] = src[2];
1313
+ dst[2] = src[1];
1314
+ dst[3] = src[0];
1315
+ #endif
1316
+ }
1317
+
1318
+ static std::string JoinPath(const std::string &path0,
1319
+ const std::string &path1) {
1320
+ if (path0.empty()) {
1321
+ return path1;
1322
+ } else {
1323
+ // check '/'
1324
+ char lastChar = *path0.rbegin();
1325
+ if (lastChar != '/') {
1326
+ return path0 + std::string("/") + path1;
1327
+ } else {
1328
+ return path0 + path1;
1329
+ }
1330
+ }
1331
+ }
1332
+
1333
+ static std::string FindFile(const std::vector<std::string> &paths,
1334
+ const std::string &filepath, FsCallbacks *fs) {
1335
+ if (fs == nullptr || fs->ExpandFilePath == nullptr ||
1336
+ fs->FileExists == nullptr) {
1337
+ // Error, fs callback[s] missing
1338
+ return std::string();
1339
+ }
1340
+
1341
+ for (size_t i = 0; i < paths.size(); i++) {
1342
+ std::string absPath =
1343
+ fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
1344
+ if (fs->FileExists(absPath, fs->user_data)) {
1345
+ return absPath;
1346
+ }
1347
+ }
1348
+
1349
+ return std::string();
1350
+ }
1351
+
1352
+ static std::string GetFilePathExtension(const std::string &FileName) {
1353
+ if (FileName.find_last_of(".") != std::string::npos)
1354
+ return FileName.substr(FileName.find_last_of(".") + 1);
1355
+ return "";
1356
+ }
1357
+
1358
+ static std::string GetBaseDir(const std::string &filepath) {
1359
+ if (filepath.find_last_of("/\\") != std::string::npos)
1360
+ return filepath.substr(0, filepath.find_last_of("/\\"));
1361
+ return "";
1362
+ }
1363
+
1364
+ // https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
1365
+ static std::string GetBaseFilename(const std::string &filepath) {
1366
+ return filepath.substr(filepath.find_last_of("/\\") + 1);
1367
+ }
1368
+
1369
+ std::string base64_encode(unsigned char const *, unsigned int len);
1370
+ std::string base64_decode(std::string const &s);
1371
+
1372
+ /*
1373
+ base64.cpp and base64.h
1374
+
1375
+ Copyright (C) 2004-2008 René Nyffenegger
1376
+
1377
+ This source code is provided 'as-is', without any express or implied
1378
+ warranty. In no event will the author be held liable for any damages
1379
+ arising from the use of this software.
1380
+
1381
+ Permission is granted to anyone to use this software for any purpose,
1382
+ including commercial applications, and to alter it and redistribute it
1383
+ freely, subject to the following restrictions:
1384
+
1385
+ 1. The origin of this source code must not be misrepresented; you must not
1386
+ claim that you wrote the original source code. If you use this source code
1387
+ in a product, an acknowledgment in the product documentation would be
1388
+ appreciated but is not required.
1389
+
1390
+ 2. Altered source versions must be plainly marked as such, and must not be
1391
+ misrepresented as being the original source code.
1392
+
1393
+ 3. This notice may not be removed or altered from any source distribution.
1394
+
1395
+ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
1396
+
1397
+ */
1398
+
1399
+ #ifdef __clang__
1400
+ #pragma clang diagnostic push
1401
+ #pragma clang diagnostic ignored "-Wexit-time-destructors"
1402
+ #pragma clang diagnostic ignored "-Wglobal-constructors"
1403
+ #pragma clang diagnostic ignored "-Wsign-conversion"
1404
+ #pragma clang diagnostic ignored "-Wconversion"
1405
+ #endif
1406
+ static const std::string base64_chars =
1407
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1408
+ "abcdefghijklmnopqrstuvwxyz"
1409
+ "0123456789+/";
1410
+
1411
+ static inline bool is_base64(unsigned char c) {
1412
+ return (isalnum(c) || (c == '+') || (c == '/'));
1413
+ }
1414
+
1415
+ std::string base64_encode(unsigned char const *bytes_to_encode,
1416
+ unsigned int in_len) {
1417
+ std::string ret;
1418
+ int i = 0;
1419
+ int j = 0;
1420
+ unsigned char char_array_3[3];
1421
+ unsigned char char_array_4[4];
1422
+
1423
+ while (in_len--) {
1424
+ char_array_3[i++] = *(bytes_to_encode++);
1425
+ if (i == 3) {
1426
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1427
+ char_array_4[1] =
1428
+ ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1429
+ char_array_4[2] =
1430
+ ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
1431
+ char_array_4[3] = char_array_3[2] & 0x3f;
1432
+
1433
+ for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
1434
+ i = 0;
1435
+ }
1436
+ }
1437
+
1438
+ if (i) {
1439
+ for (j = i; j < 3; j++) char_array_3[j] = '\0';
1440
+
1441
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1442
+ char_array_4[1] =
1443
+ ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1444
+ char_array_4[2] =
1445
+ ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
1446
+
1447
+ for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
1448
+
1449
+ while ((i++ < 3)) ret += '=';
1450
+ }
1451
+
1452
+ return ret;
1453
+ }
1454
+
1455
+ std::string base64_decode(std::string const &encoded_string) {
1456
+ int in_len = static_cast<int>(encoded_string.size());
1457
+ int i = 0;
1458
+ int j = 0;
1459
+ int in_ = 0;
1460
+ unsigned char char_array_4[4], char_array_3[3];
1461
+ std::string ret;
1462
+
1463
+ while (in_len-- && (encoded_string[in_] != '=') &&
1464
+ is_base64(encoded_string[in_])) {
1465
+ char_array_4[i++] = encoded_string[in_];
1466
+ in_++;
1467
+ if (i == 4) {
1468
+ for (i = 0; i < 4; i++)
1469
+ char_array_4[i] =
1470
+ static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
1471
+
1472
+ char_array_3[0] =
1473
+ (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1474
+ char_array_3[1] =
1475
+ ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1476
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1477
+
1478
+ for (i = 0; (i < 3); i++) ret += char_array_3[i];
1479
+ i = 0;
1480
+ }
1481
+ }
1482
+
1483
+ if (i) {
1484
+ for (j = i; j < 4; j++) char_array_4[j] = 0;
1485
+
1486
+ for (j = 0; j < 4; j++)
1487
+ char_array_4[j] =
1488
+ static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
1489
+
1490
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1491
+ char_array_3[1] =
1492
+ ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1493
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1494
+
1495
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
1496
+ }
1497
+
1498
+ return ret;
1499
+ }
1500
+ #ifdef __clang__
1501
+ #pragma clang diagnostic pop
1502
+ #endif
1503
+
1504
+ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
1505
+ std::string *warn, const std::string &filename,
1506
+ const std::string &basedir, bool required,
1507
+ size_t reqBytes, bool checkSize, FsCallbacks *fs) {
1508
+ if (fs == nullptr || fs->FileExists == nullptr ||
1509
+ fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
1510
+ // This is a developer error, assert() ?
1511
+ if (err) {
1512
+ (*err) += "FS callback[s] not set\n";
1513
+ }
1514
+ return false;
1515
+ }
1516
+
1517
+ std::string *failMsgOut = required ? err : warn;
1518
+
1519
+ out->clear();
1520
+
1521
+ std::vector<std::string> paths;
1522
+ paths.push_back(basedir);
1523
+ paths.push_back(".");
1524
+
1525
+ std::string filepath = FindFile(paths, filename, fs);
1526
+ if (filepath.empty() || filename.empty()) {
1527
+ if (failMsgOut) {
1528
+ (*failMsgOut) += "File not found : " + filename + "\n";
1529
+ }
1530
+ return false;
1531
+ }
1532
+
1533
+ std::vector<unsigned char> buf;
1534
+ std::string fileReadErr;
1535
+ bool fileRead =
1536
+ fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
1537
+ if (!fileRead) {
1538
+ if (failMsgOut) {
1539
+ (*failMsgOut) +=
1540
+ "File read error : " + filepath + " : " + fileReadErr + "\n";
1541
+ }
1542
+ return false;
1543
+ }
1544
+
1545
+ size_t sz = buf.size();
1546
+ if (sz == 0) {
1547
+ if (failMsgOut) {
1548
+ (*failMsgOut) += "File is empty : " + filepath + "\n";
1549
+ }
1550
+ return false;
1551
+ }
1552
+
1553
+ if (checkSize) {
1554
+ if (reqBytes == sz) {
1555
+ out->swap(buf);
1556
+ return true;
1557
+ } else {
1558
+ std::stringstream ss;
1559
+ ss << "File size mismatch : " << filepath << ", requestedBytes "
1560
+ << reqBytes << ", but got " << sz << std::endl;
1561
+ if (failMsgOut) {
1562
+ (*failMsgOut) += ss.str();
1563
+ }
1564
+ return false;
1565
+ }
1566
+ }
1567
+
1568
+ out->swap(buf);
1569
+ return true;
1570
+ }
1571
+
1572
+ void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
1573
+ LoadImageData = func;
1574
+ load_image_user_data_ = user_data;
1575
+ }
1576
+
1577
+ #ifndef TINYGLTF_NO_STB_IMAGE
1578
+ bool LoadImageData(Image *image, std::string *err, std::string *warn,
1579
+ int req_width, int req_height, const unsigned char *bytes,
1580
+ int size, void *) {
1581
+ (void)warn;
1582
+
1583
+ int w, h, comp, req_comp;
1584
+
1585
+ // force 32-bit textures for common Vulkan compatibility. It appears that
1586
+ // some GPU drivers do not support 24-bit images for Vulkan
1587
+ req_comp = 4;
1588
+
1589
+ // if image cannot be decoded, ignore parsing and keep it by its path
1590
+ // don't break in this case
1591
+ // FIXME we should only enter this function if the image is embedded. If
1592
+ // image->uri references
1593
+ // an image file, it should be left as it is. Image loading should not be
1594
+ // mandatory (to support other formats)
1595
+ unsigned char *data =
1596
+ stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
1597
+ if (!data) {
1598
+ // NOTE: you can use `warn` instead of `err`
1599
+ if (err) {
1600
+ (*err) += "Unknown image format.\n";
1601
+ }
1602
+ return false;
1603
+ }
1604
+
1605
+ if (w < 1 || h < 1) {
1606
+ free(data);
1607
+ if (err) {
1608
+ (*err) += "Invalid image data.\n";
1609
+ }
1610
+ return false;
1611
+ }
1612
+
1613
+ if (req_width > 0) {
1614
+ if (req_width != w) {
1615
+ free(data);
1616
+ if (err) {
1617
+ (*err) += "Image width mismatch.\n";
1618
+ }
1619
+ return false;
1620
+ }
1621
+ }
1622
+
1623
+ if (req_height > 0) {
1624
+ if (req_height != h) {
1625
+ free(data);
1626
+ if (err) {
1627
+ (*err) += "Image height mismatch.\n";
1628
+ }
1629
+ return false;
1630
+ }
1631
+ }
1632
+
1633
+ image->width = w;
1634
+ image->height = h;
1635
+ image->component = req_comp;
1636
+ image->image.resize(static_cast<size_t>(w * h * req_comp));
1637
+ std::copy(data, data + w * h * req_comp, image->image.begin());
1638
+
1639
+ free(data);
1640
+
1641
+ return true;
1642
+ }
1643
+ #endif
1644
+
1645
+ void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
1646
+ WriteImageData = func;
1647
+ write_image_user_data_ = user_data;
1648
+ }
1649
+
1650
+ #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1651
+ static void WriteToMemory_stbi(void *context, void *data, int size) {
1652
+ std::vector<unsigned char> *buffer =
1653
+ reinterpret_cast<std::vector<unsigned char> *>(context);
1654
+
1655
+ unsigned char *pData = reinterpret_cast<unsigned char *>(data);
1656
+
1657
+ buffer->insert(buffer->end(), pData, pData + size);
1658
+ }
1659
+
1660
+ bool WriteImageData(const std::string *basepath, const std::string *filename,
1661
+ Image *image, bool embedImages, void *fsPtr) {
1662
+ const std::string ext = GetFilePathExtension(*filename);
1663
+
1664
+ // Write image to temporary buffer
1665
+ std::string header;
1666
+ std::vector<unsigned char> data;
1667
+
1668
+ if (ext == "png") {
1669
+ if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
1670
+ image->height, image->component,
1671
+ &image->image[0], 0)) {
1672
+ return false;
1673
+ }
1674
+ header = "data:image/png;base64,";
1675
+ } else if (ext == "jpg") {
1676
+ if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
1677
+ image->height, image->component,
1678
+ &image->image[0], 100)) {
1679
+ return false;
1680
+ }
1681
+ header = "data:image/jpeg;base64,";
1682
+ } else if (ext == "bmp") {
1683
+ if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
1684
+ image->height, image->component,
1685
+ &image->image[0])) {
1686
+ return false;
1687
+ }
1688
+ header = "data:image/bmp;base64,";
1689
+ } else if (!embedImages) {
1690
+ // Error: can't output requested format to file
1691
+ return false;
1692
+ }
1693
+
1694
+ if (embedImages) {
1695
+ // Embed base64-encoded image into URI
1696
+ if (data.size()) {
1697
+ image->uri =
1698
+ header +
1699
+ base64_encode(&data[0], static_cast<unsigned int>(data.size()));
1700
+ } else {
1701
+ // Throw error?
1702
+ }
1703
+ } else {
1704
+ // Write image to disc
1705
+ FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
1706
+ if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
1707
+ const std::string imagefilepath = JoinPath(*basepath, *filename);
1708
+ std::string writeError;
1709
+ if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
1710
+ fs->user_data)) {
1711
+ // Could not write image file to disc; Throw error ?
1712
+ return false;
1713
+ }
1714
+ } else {
1715
+ // Throw error?
1716
+ }
1717
+ image->uri = *filename;
1718
+ }
1719
+
1720
+ return true;
1721
+ }
1722
+ #endif
1723
+
1724
+ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
1725
+
1726
+ #ifndef TINYGLTF_NO_FS
1727
+ // Default implementations of filesystem functions
1728
+
1729
+ bool FileExists(const std::string &abs_filename, void *) {
1730
+ bool ret;
1731
+ #ifdef _WIN32
1732
+ FILE *fp;
1733
+ errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
1734
+ if (err != 0) {
1735
+ return false;
1736
+ }
1737
+ #else
1738
+ FILE *fp = fopen(abs_filename.c_str(), "rb");
1739
+ #endif
1740
+ if (fp) {
1741
+ ret = true;
1742
+ fclose(fp);
1743
+ } else {
1744
+ ret = false;
1745
+ }
1746
+
1747
+ return ret;
1748
+ }
1749
+
1750
+ std::string ExpandFilePath(const std::string &filepath, void *) {
1751
+ #ifdef _WIN32
1752
+ DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
1753
+ char *str = new char[len];
1754
+ ExpandEnvironmentStringsA(filepath.c_str(), str, len);
1755
+
1756
+ std::string s(str);
1757
+
1758
+ delete[] str;
1759
+
1760
+ return s;
1761
+ #else
1762
+
1763
+ #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
1764
+ defined(__ANDROID__) || defined(__EMSCRIPTEN__)
1765
+ // no expansion
1766
+ std::string s = filepath;
1767
+ #else
1768
+ std::string s;
1769
+ wordexp_t p;
1770
+
1771
+ if (filepath.empty()) {
1772
+ return "";
1773
+ }
1774
+
1775
+ // char** w;
1776
+ int ret = wordexp(filepath.c_str(), &p, 0);
1777
+ if (ret) {
1778
+ // err
1779
+ s = filepath;
1780
+ return s;
1781
+ }
1782
+
1783
+ // Use first element only.
1784
+ if (p.we_wordv) {
1785
+ s = std::string(p.we_wordv[0]);
1786
+ wordfree(&p);
1787
+ } else {
1788
+ s = filepath;
1789
+ }
1790
+
1791
+ #endif
1792
+
1793
+ return s;
1794
+ #endif
1795
+ }
1796
+
1797
+ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1798
+ const std::string &filepath, void *) {
1799
+ std::ifstream f(filepath.c_str(), std::ifstream::binary);
1800
+ if (!f) {
1801
+ if (err) {
1802
+ (*err) += "File open error : " + filepath + "\n";
1803
+ }
1804
+ return false;
1805
+ }
1806
+
1807
+ f.seekg(0, f.end);
1808
+ size_t sz = static_cast<size_t>(f.tellg());
1809
+ f.seekg(0, f.beg);
1810
+
1811
+ if (int(sz) < 0) {
1812
+ if (err) {
1813
+ (*err) += "Invalid file size : " + filepath +
1814
+ " (does the path point to a directory?)";
1815
+ }
1816
+ return false;
1817
+ } else if (sz == 0) {
1818
+ if (err) {
1819
+ (*err) += "File is empty : " + filepath + "\n";
1820
+ }
1821
+ return false;
1822
+ }
1823
+
1824
+ out->resize(sz);
1825
+ f.read(reinterpret_cast<char *>(&out->at(0)),
1826
+ static_cast<std::streamsize>(sz));
1827
+ f.close();
1828
+
1829
+ return true;
1830
+ }
1831
+
1832
+ bool WriteWholeFile(std::string *err, const std::string &filepath,
1833
+ const std::vector<unsigned char> &contents, void *) {
1834
+ std::ofstream f(filepath.c_str(), std::ofstream::binary);
1835
+ if (!f) {
1836
+ if (err) {
1837
+ (*err) += "File open error for writing : " + filepath + "\n";
1838
+ }
1839
+ return false;
1840
+ }
1841
+
1842
+ f.write(reinterpret_cast<const char *>(&contents.at(0)),
1843
+ static_cast<std::streamsize>(contents.size()));
1844
+ if (!f) {
1845
+ if (err) {
1846
+ (*err) += "File write error: " + filepath + "\n";
1847
+ }
1848
+ return false;
1849
+ }
1850
+
1851
+ f.close();
1852
+ return true;
1853
+ }
1854
+
1855
+ #endif // TINYGLTF_NO_FS
1856
+
1857
+ static std::string MimeToExt(const std::string &mimeType) {
1858
+ if (mimeType == "image/jpeg") {
1859
+ return "jpg";
1860
+ } else if (mimeType == "image/png") {
1861
+ return "png";
1862
+ } else if (mimeType == "image/bmp") {
1863
+ return "bmp";
1864
+ } else if (mimeType == "image/gif") {
1865
+ return "gif";
1866
+ }
1867
+
1868
+ return "";
1869
+ }
1870
+
1871
+ static void UpdateImageObject(Image &image, std::string &baseDir, int index,
1872
+ bool embedImages,
1873
+ WriteImageDataFunction *WriteImageData = nullptr,
1874
+ void *user_data = nullptr) {
1875
+ std::string filename;
1876
+ std::string ext;
1877
+
1878
+ // If image have uri. Use it it as a filename
1879
+ if (image.uri.size()) {
1880
+ filename = GetBaseFilename(image.uri);
1881
+ ext = GetFilePathExtension(filename);
1882
+
1883
+ } else if (image.name.size()) {
1884
+ ext = MimeToExt(image.mimeType);
1885
+ // Otherwise use name as filename
1886
+ filename = image.name + "." + ext;
1887
+ } else {
1888
+ ext = MimeToExt(image.mimeType);
1889
+ // Fallback to index of image as filename
1890
+ filename = std::to_string(index) + "." + ext;
1891
+ }
1892
+
1893
+ // If callback is set, modify image data object
1894
+ if (*WriteImageData != nullptr) {
1895
+ std::string uri;
1896
+ (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
1897
+ }
1898
+ }
1899
+
1900
+ bool IsDataURI(const std::string &in) {
1901
+ std::string header = "data:application/octet-stream;base64,";
1902
+ if (in.find(header) == 0) {
1903
+ return true;
1904
+ }
1905
+
1906
+ header = "data:image/jpeg;base64,";
1907
+ if (in.find(header) == 0) {
1908
+ return true;
1909
+ }
1910
+
1911
+ header = "data:image/png;base64,";
1912
+ if (in.find(header) == 0) {
1913
+ return true;
1914
+ }
1915
+
1916
+ header = "data:image/bmp;base64,";
1917
+ if (in.find(header) == 0) {
1918
+ return true;
1919
+ }
1920
+
1921
+ header = "data:image/gif;base64,";
1922
+ if (in.find(header) == 0) {
1923
+ return true;
1924
+ }
1925
+
1926
+ header = "data:text/plain;base64,";
1927
+ if (in.find(header) == 0) {
1928
+ return true;
1929
+ }
1930
+
1931
+ header = "data:application/gltf-buffer;base64,";
1932
+ if (in.find(header) == 0) {
1933
+ return true;
1934
+ }
1935
+
1936
+ return false;
1937
+ }
1938
+
1939
+ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
1940
+ const std::string &in, size_t reqBytes, bool checkSize) {
1941
+ std::string header = "data:application/octet-stream;base64,";
1942
+ std::string data;
1943
+ if (in.find(header) == 0) {
1944
+ data = base64_decode(in.substr(header.size())); // cut mime string.
1945
+ }
1946
+
1947
+ if (data.empty()) {
1948
+ header = "data:image/jpeg;base64,";
1949
+ if (in.find(header) == 0) {
1950
+ mime_type = "image/jpeg";
1951
+ data = base64_decode(in.substr(header.size())); // cut mime string.
1952
+ }
1953
+ }
1954
+
1955
+ if (data.empty()) {
1956
+ header = "data:image/png;base64,";
1957
+ if (in.find(header) == 0) {
1958
+ mime_type = "image/png";
1959
+ data = base64_decode(in.substr(header.size())); // cut mime string.
1960
+ }
1961
+ }
1962
+
1963
+ if (data.empty()) {
1964
+ header = "data:image/bmp;base64,";
1965
+ if (in.find(header) == 0) {
1966
+ mime_type = "image/bmp";
1967
+ data = base64_decode(in.substr(header.size())); // cut mime string.
1968
+ }
1969
+ }
1970
+
1971
+ if (data.empty()) {
1972
+ header = "data:image/gif;base64,";
1973
+ if (in.find(header) == 0) {
1974
+ mime_type = "image/gif";
1975
+ data = base64_decode(in.substr(header.size())); // cut mime string.
1976
+ }
1977
+ }
1978
+
1979
+ if (data.empty()) {
1980
+ header = "data:text/plain;base64,";
1981
+ if (in.find(header) == 0) {
1982
+ mime_type = "text/plain";
1983
+ data = base64_decode(in.substr(header.size()));
1984
+ }
1985
+ }
1986
+
1987
+ if (data.empty()) {
1988
+ header = "data:application/gltf-buffer;base64,";
1989
+ if (in.find(header) == 0) {
1990
+ data = base64_decode(in.substr(header.size()));
1991
+ }
1992
+ }
1993
+
1994
+ if (data.empty()) {
1995
+ return false;
1996
+ }
1997
+
1998
+ if (checkSize) {
1999
+ if (data.size() != reqBytes) {
2000
+ return false;
2001
+ }
2002
+ out->resize(reqBytes);
2003
+ } else {
2004
+ out->resize(data.size());
2005
+ }
2006
+ std::copy(data.begin(), data.end(), out->begin());
2007
+ return true;
2008
+ }
2009
+
2010
+ static bool ParseJsonAsValue(Value *ret, const json &o) {
2011
+ Value val{};
2012
+ switch (o.type()) {
2013
+ case json::value_t::object: {
2014
+ Value::Object value_object;
2015
+ for (auto it = o.begin(); it != o.end(); it++) {
2016
+ Value entry;
2017
+ ParseJsonAsValue(&entry, it.value());
2018
+ if (entry.Type() != NULL_TYPE) value_object[it.key()] = entry;
2019
+ }
2020
+ if (value_object.size() > 0) val = Value(value_object);
2021
+ } break;
2022
+ case json::value_t::array: {
2023
+ Value::Array value_array;
2024
+ for (auto it = o.begin(); it != o.end(); it++) {
2025
+ Value entry;
2026
+ ParseJsonAsValue(&entry, it.value());
2027
+ if (entry.Type() != NULL_TYPE) value_array.push_back(entry);
2028
+ }
2029
+ if (value_array.size() > 0) val = Value(value_array);
2030
+ } break;
2031
+ case json::value_t::string:
2032
+ val = Value(o.get<std::string>());
2033
+ break;
2034
+ case json::value_t::boolean:
2035
+ val = Value(o.get<bool>());
2036
+ break;
2037
+ case json::value_t::number_integer:
2038
+ case json::value_t::number_unsigned:
2039
+ val = Value(static_cast<int>(o.get<int64_t>()));
2040
+ break;
2041
+ case json::value_t::number_float:
2042
+ val = Value(o.get<double>());
2043
+ break;
2044
+ case json::value_t::null:
2045
+ case json::value_t::discarded:
2046
+ // default:
2047
+ break;
2048
+ }
2049
+ if (ret) *ret = val;
2050
+
2051
+ return val.Type() != NULL_TYPE;
2052
+ }
2053
+
2054
+ static bool ParseExtrasProperty(Value *ret, const json &o) {
2055
+ json::const_iterator it = o.find("extras");
2056
+ if (it == o.end()) {
2057
+ return false;
2058
+ }
2059
+
2060
+ return ParseJsonAsValue(ret, it.value());
2061
+ }
2062
+
2063
+ static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
2064
+ const std::string &property,
2065
+ const bool required,
2066
+ const std::string &parent_node = "") {
2067
+ json::const_iterator it = o.find(property);
2068
+ if (it == o.end()) {
2069
+ if (required) {
2070
+ if (err) {
2071
+ (*err) += "'" + property + "' property is missing";
2072
+ if (!parent_node.empty()) {
2073
+ (*err) += " in " + parent_node;
2074
+ }
2075
+ (*err) += ".\n";
2076
+ }
2077
+ }
2078
+ return false;
2079
+ }
2080
+
2081
+ if (!it.value().is_boolean()) {
2082
+ if (required) {
2083
+ if (err) {
2084
+ (*err) += "'" + property + "' property is not a bool type.\n";
2085
+ }
2086
+ }
2087
+ return false;
2088
+ }
2089
+
2090
+ if (ret) {
2091
+ (*ret) = it.value().get<bool>();
2092
+ }
2093
+
2094
+ return true;
2095
+ }
2096
+
2097
+ static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
2098
+ const std::string &property,
2099
+ const bool required,
2100
+ const std::string &parent_node = "") {
2101
+ json::const_iterator it = o.find(property);
2102
+ if (it == o.end()) {
2103
+ if (required) {
2104
+ if (err) {
2105
+ (*err) += "'" + property + "' property is missing";
2106
+ if (!parent_node.empty()) {
2107
+ (*err) += " in " + parent_node;
2108
+ }
2109
+ (*err) += ".\n";
2110
+ }
2111
+ }
2112
+ return false;
2113
+ }
2114
+
2115
+ if (!it.value().is_number()) {
2116
+ if (required) {
2117
+ if (err) {
2118
+ (*err) += "'" + property + "' property is not a number type.\n";
2119
+ }
2120
+ }
2121
+ return false;
2122
+ }
2123
+
2124
+ if (ret) {
2125
+ (*ret) = it.value().get<double>();
2126
+ }
2127
+
2128
+ return true;
2129
+ }
2130
+
2131
+ static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
2132
+ const json &o, const std::string &property,
2133
+ bool required,
2134
+ const std::string &parent_node = "") {
2135
+ json::const_iterator it = o.find(property);
2136
+ if (it == o.end()) {
2137
+ if (required) {
2138
+ if (err) {
2139
+ (*err) += "'" + property + "' property is missing";
2140
+ if (!parent_node.empty()) {
2141
+ (*err) += " in " + parent_node;
2142
+ }
2143
+ (*err) += ".\n";
2144
+ }
2145
+ }
2146
+ return false;
2147
+ }
2148
+
2149
+ if (!it.value().is_array()) {
2150
+ if (required) {
2151
+ if (err) {
2152
+ (*err) += "'" + property + "' property is not an array";
2153
+ if (!parent_node.empty()) {
2154
+ (*err) += " in " + parent_node;
2155
+ }
2156
+ (*err) += ".\n";
2157
+ }
2158
+ }
2159
+ return false;
2160
+ }
2161
+
2162
+ ret->clear();
2163
+ for (json::const_iterator i = it.value().begin(); i != it.value().end();
2164
+ i++) {
2165
+ if (!i.value().is_number()) {
2166
+ if (required) {
2167
+ if (err) {
2168
+ (*err) += "'" + property + "' property is not a number.\n";
2169
+ if (!parent_node.empty()) {
2170
+ (*err) += " in " + parent_node;
2171
+ }
2172
+ (*err) += ".\n";
2173
+ }
2174
+ }
2175
+ return false;
2176
+ }
2177
+ ret->push_back(i.value());
2178
+ }
2179
+
2180
+ return true;
2181
+ }
2182
+
2183
+ static bool ParseStringProperty(
2184
+ std::string *ret, std::string *err, const json &o,
2185
+ const std::string &property, bool required,
2186
+ const std::string &parent_node = std::string()) {
2187
+ json::const_iterator it = o.find(property);
2188
+ if (it == o.end()) {
2189
+ if (required) {
2190
+ if (err) {
2191
+ (*err) += "'" + property + "' property is missing";
2192
+ if (parent_node.empty()) {
2193
+ (*err) += ".\n";
2194
+ } else {
2195
+ (*err) += " in `" + parent_node + "'.\n";
2196
+ }
2197
+ }
2198
+ }
2199
+ return false;
2200
+ }
2201
+
2202
+ if (!it.value().is_string()) {
2203
+ if (required) {
2204
+ if (err) {
2205
+ (*err) += "'" + property + "' property is not a string type.\n";
2206
+ }
2207
+ }
2208
+ return false;
2209
+ }
2210
+
2211
+ if (ret) {
2212
+ (*ret) = it.value().get<std::string>();
2213
+ }
2214
+
2215
+ return true;
2216
+ }
2217
+
2218
+ static bool ParseStringIntProperty(std::map<std::string, int> *ret,
2219
+ std::string *err, const json &o,
2220
+ const std::string &property, bool required,
2221
+ const std::string &parent = "") {
2222
+ json::const_iterator it = o.find(property);
2223
+ if (it == o.end()) {
2224
+ if (required) {
2225
+ if (err) {
2226
+ if (!parent.empty()) {
2227
+ (*err) +=
2228
+ "'" + property + "' property is missing in " + parent + ".\n";
2229
+ } else {
2230
+ (*err) += "'" + property + "' property is missing.\n";
2231
+ }
2232
+ }
2233
+ }
2234
+ return false;
2235
+ }
2236
+
2237
+ // Make sure we are dealing with an object / dictionary.
2238
+ if (!it.value().is_object()) {
2239
+ if (required) {
2240
+ if (err) {
2241
+ (*err) += "'" + property + "' property is not an object.\n";
2242
+ }
2243
+ }
2244
+ return false;
2245
+ }
2246
+
2247
+ ret->clear();
2248
+ const json &dict = it.value();
2249
+
2250
+ json::const_iterator dictIt(dict.begin());
2251
+ json::const_iterator dictItEnd(dict.end());
2252
+
2253
+ for (; dictIt != dictItEnd; ++dictIt) {
2254
+ if (!dictIt.value().is_number()) {
2255
+ if (required) {
2256
+ if (err) {
2257
+ (*err) += "'" + property + "' value is not an int.\n";
2258
+ }
2259
+ }
2260
+ return false;
2261
+ }
2262
+
2263
+ // Insert into the list.
2264
+ (*ret)[dictIt.key()] = static_cast<int>(dictIt.value());
2265
+ }
2266
+ return true;
2267
+ }
2268
+
2269
+ static bool ParseJSONProperty(std::map<std::string, double> *ret,
2270
+ std::string *err, const json &o,
2271
+ const std::string &property, bool required) {
2272
+ json::const_iterator it = o.find(property);
2273
+ if (it == o.end()) {
2274
+ if (required) {
2275
+ if (err) {
2276
+ (*err) += "'" + property + "' property is missing. \n'";
2277
+ }
2278
+ }
2279
+ return false;
2280
+ }
2281
+
2282
+ if (!it.value().is_object()) {
2283
+ if (required) {
2284
+ if (err) {
2285
+ (*err) += "'" + property + "' property is not a JSON object.\n";
2286
+ }
2287
+ }
2288
+ return false;
2289
+ }
2290
+
2291
+ ret->clear();
2292
+ const json &obj = it.value();
2293
+ json::const_iterator it2(obj.begin());
2294
+ json::const_iterator itEnd(obj.end());
2295
+ for (; it2 != itEnd; it2++) {
2296
+ if (it2.value().is_number())
2297
+ ret->insert(std::pair<std::string, double>(it2.key(), it2.value()));
2298
+ }
2299
+
2300
+ return true;
2301
+ }
2302
+
2303
+ static bool ParseParameterProperty(Parameter *param, std::string *err,
2304
+ const json &o, const std::string &prop,
2305
+ bool required) {
2306
+ // A parameter value can either be a string or an array of either a boolean or
2307
+ // a number. Booleans of any kind aren't supported here. Granted, it
2308
+ // complicates the Parameter structure and breaks it semantically in the sense
2309
+ // that the client probably works off the assumption that if the string is
2310
+ // empty the vector is used, etc. Would a tagged union work?
2311
+ if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
2312
+ // Found string property.
2313
+ return true;
2314
+ } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
2315
+ false)) {
2316
+ // Found a number array.
2317
+ return true;
2318
+ } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
2319
+ return param->has_number_value = true;
2320
+ } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
2321
+ false)) {
2322
+ return true;
2323
+ } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
2324
+ return true;
2325
+ } else {
2326
+ if (required) {
2327
+ if (err) {
2328
+ (*err) += "parameter must be a string or number / number array.\n";
2329
+ }
2330
+ }
2331
+ return false;
2332
+ }
2333
+ }
2334
+
2335
+ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
2336
+ const json &o) {
2337
+ (void)err;
2338
+
2339
+ json::const_iterator it = o.find("extensions");
2340
+ if (it == o.end()) {
2341
+ return false;
2342
+ }
2343
+ if (!it.value().is_object()) {
2344
+ return false;
2345
+ }
2346
+ ExtensionMap extensions;
2347
+ json::const_iterator extIt = it.value().begin();
2348
+ for (; extIt != it.value().end(); extIt++) {
2349
+ if (!extIt.value().is_object()) continue;
2350
+ if (!ParseJsonAsValue(&extensions[extIt.key()], extIt.value())) {
2351
+ if (!extIt.key().empty()) {
2352
+ // create empty object so that an extension object is still of type object
2353
+ extensions[extIt.key()] = Value{ Value::Object{} };
2354
+ }
2355
+ }
2356
+ }
2357
+ if (ret) {
2358
+ (*ret) = extensions;
2359
+ }
2360
+ return true;
2361
+ }
2362
+
2363
+ static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
2364
+ ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
2365
+ ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
2366
+ ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
2367
+
2368
+ ParseExtensionsProperty(&asset->extensions, err, o);
2369
+
2370
+ // Unity exporter version is added as extra here
2371
+ ParseExtrasProperty(&(asset->extras), o);
2372
+
2373
+ return true;
2374
+ }
2375
+
2376
+ static bool ParseImage(Image *image, std::string *err, std::string *warn,
2377
+ const json &o, const std::string &basedir,
2378
+ FsCallbacks *fs,
2379
+ LoadImageDataFunction *LoadImageData = nullptr,
2380
+ void *load_image_user_data = nullptr) {
2381
+ // A glTF image must either reference a bufferView or an image uri
2382
+
2383
+ // schema says oneOf [`bufferView`, `uri`]
2384
+ // TODO(syoyo): Check the type of each parameters.
2385
+ bool hasBufferView = (o.find("bufferView") != o.end());
2386
+ bool hasURI = (o.find("uri") != o.end());
2387
+
2388
+ if (hasBufferView && hasURI) {
2389
+ // Should not both defined.
2390
+ if (err) {
2391
+ (*err) +=
2392
+ "Only one of `bufferView` or `uri` should be defined, but both are "
2393
+ "defined for Image.\n";
2394
+ }
2395
+ return false;
2396
+ }
2397
+
2398
+ if (!hasBufferView && !hasURI) {
2399
+ if (err) {
2400
+ (*err) += "Neither required `bufferView` nor `uri` defined for Image.\n";
2401
+ }
2402
+ return false;
2403
+ }
2404
+
2405
+ ParseStringProperty(&image->name, err, o, "name", false);
2406
+ ParseExtensionsProperty(&image->extensions, err, o);
2407
+ ParseExtrasProperty(&image->extras, o);
2408
+
2409
+ if (hasBufferView) {
2410
+ double bufferView = -1;
2411
+ if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true)) {
2412
+ if (err) {
2413
+ (*err) += "Failed to parse `bufferView` for Image.\n";
2414
+ }
2415
+ return false;
2416
+ }
2417
+
2418
+ std::string mime_type;
2419
+ ParseStringProperty(&mime_type, err, o, "mimeType", false);
2420
+
2421
+ double width = 0.0;
2422
+ ParseNumberProperty(&width, err, o, "width", false);
2423
+
2424
+ double height = 0.0;
2425
+ ParseNumberProperty(&height, err, o, "height", false);
2426
+
2427
+ // Just only save some information here. Loading actual image data from
2428
+ // bufferView is done after this `ParseImage` function.
2429
+ image->bufferView = static_cast<int>(bufferView);
2430
+ image->mimeType = mime_type;
2431
+ image->width = static_cast<int>(width);
2432
+ image->height = static_cast<int>(height);
2433
+
2434
+ return true;
2435
+ }
2436
+
2437
+ // Parse URI & Load image data.
2438
+
2439
+ std::string uri;
2440
+ std::string tmp_err;
2441
+ if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
2442
+ if (err) {
2443
+ (*err) += "Failed to parse `uri` for Image.\n";
2444
+ }
2445
+ return false;
2446
+ }
2447
+
2448
+ std::vector<unsigned char> img;
2449
+
2450
+ if (IsDataURI(uri)) {
2451
+ if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
2452
+ if (err) {
2453
+ (*err) += "Failed to decode 'uri' for image parameter.\n";
2454
+ }
2455
+ return false;
2456
+ }
2457
+ } else {
2458
+ // Assume external file
2459
+ // Keep texture path (for textures that cannot be decoded)
2460
+ image->uri = uri;
2461
+ #ifdef TINYGLTF_NO_EXTERNAL_IMAGE
2462
+ return true;
2463
+ #endif
2464
+ if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
2465
+ if (warn) {
2466
+ (*warn) += "Failed to load external 'uri' for image parameter\n";
2467
+ }
2468
+ // If the image cannot be loaded, keep uri as image->uri.
2469
+ return true;
2470
+ }
2471
+
2472
+ if (img.empty()) {
2473
+ if (warn) {
2474
+ (*warn) += "Image is empty.\n";
2475
+ }
2476
+ return false;
2477
+ }
2478
+ }
2479
+
2480
+ if (*LoadImageData == nullptr) {
2481
+ if (err) {
2482
+ (*err) += "No LoadImageData callback specified.\n";
2483
+ }
2484
+ return false;
2485
+ }
2486
+ return (*LoadImageData)(image, err, warn, 0, 0, &img.at(0),
2487
+ static_cast<int>(img.size()), load_image_user_data);
2488
+ }
2489
+
2490
+ static bool ParseTexture(Texture *texture, std::string *err, const json &o,
2491
+ const std::string &basedir) {
2492
+ (void)basedir;
2493
+ double sampler = -1.0;
2494
+ double source = -1.0;
2495
+ ParseNumberProperty(&sampler, err, o, "sampler", false);
2496
+
2497
+ ParseNumberProperty(&source, err, o, "source", false);
2498
+
2499
+ texture->sampler = static_cast<int>(sampler);
2500
+ texture->source = static_cast<int>(source);
2501
+
2502
+ ParseExtensionsProperty(&texture->extensions, err, o);
2503
+ ParseExtrasProperty(&texture->extras, o);
2504
+
2505
+ ParseStringProperty(&texture->name, err, o, "name", false);
2506
+
2507
+ return true;
2508
+ }
2509
+
2510
+ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
2511
+ FsCallbacks *fs, const std::string &basedir,
2512
+ bool is_binary = false,
2513
+ const unsigned char *bin_data = nullptr,
2514
+ size_t bin_size = 0) {
2515
+ double byteLength;
2516
+ if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true, "Buffer")) {
2517
+ return false;
2518
+ }
2519
+
2520
+ // In glTF 2.0, uri is not mandatory anymore
2521
+ buffer->uri.clear();
2522
+ ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
2523
+
2524
+ // having an empty uri for a non embedded image should not be valid
2525
+ if (!is_binary && buffer->uri.empty()) {
2526
+ if (err) {
2527
+ (*err) += "'uri' is missing from non binary glTF file buffer.\n";
2528
+ }
2529
+ }
2530
+
2531
+ json::const_iterator type = o.find("type");
2532
+ if (type != o.end()) {
2533
+ if (type.value().is_string()) {
2534
+ const std::string &ty = type.value();
2535
+ if (ty.compare("arraybuffer") == 0) {
2536
+ // buffer.type = "arraybuffer";
2537
+ }
2538
+ }
2539
+ }
2540
+
2541
+ size_t bytes = static_cast<size_t>(byteLength);
2542
+ if (is_binary) {
2543
+ // Still binary glTF accepts external dataURI.
2544
+ if (!buffer->uri.empty()) {
2545
+ // First try embedded data URI.
2546
+ if (IsDataURI(buffer->uri)) {
2547
+ std::string mime_type;
2548
+ if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, bytes,
2549
+ true)) {
2550
+ if (err) {
2551
+ (*err) +=
2552
+ "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
2553
+ }
2554
+ return false;
2555
+ }
2556
+ } else {
2557
+ // External .bin file.
2558
+ if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
2559
+ buffer->uri, basedir, true, bytes, true, fs)) {
2560
+ return false;
2561
+ }
2562
+ }
2563
+ } else {
2564
+ // load data from (embedded) binary data
2565
+
2566
+ if ((bin_size == 0) || (bin_data == nullptr)) {
2567
+ if (err) {
2568
+ (*err) += "Invalid binary data in `Buffer'.\n";
2569
+ }
2570
+ return false;
2571
+ }
2572
+
2573
+ if (byteLength > bin_size) {
2574
+ if (err) {
2575
+ std::stringstream ss;
2576
+ ss << "Invalid `byteLength'. Must be equal or less than binary size: "
2577
+ "`byteLength' = "
2578
+ << byteLength << ", binary size = " << bin_size << std::endl;
2579
+ (*err) += ss.str();
2580
+ }
2581
+ return false;
2582
+ }
2583
+
2584
+ // Read buffer data
2585
+ buffer->data.resize(static_cast<size_t>(byteLength));
2586
+ memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
2587
+ }
2588
+
2589
+ } else {
2590
+ if (IsDataURI(buffer->uri)) {
2591
+ std::string mime_type;
2592
+ if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, bytes, true)) {
2593
+ if (err) {
2594
+ (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
2595
+ }
2596
+ return false;
2597
+ }
2598
+ } else {
2599
+ // Assume external .bin file.
2600
+ if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
2601
+ basedir, true, bytes, true, fs)) {
2602
+ return false;
2603
+ }
2604
+ }
2605
+ }
2606
+
2607
+ ParseStringProperty(&buffer->name, err, o, "name", false);
2608
+
2609
+ return true;
2610
+ }
2611
+
2612
+ static bool ParseBufferView(BufferView *bufferView, std::string *err,
2613
+ const json &o) {
2614
+ double buffer = -1.0;
2615
+ if (!ParseNumberProperty(&buffer, err, o, "buffer", true, "BufferView")) {
2616
+ return false;
2617
+ }
2618
+
2619
+ double byteOffset = 0.0;
2620
+ ParseNumberProperty(&byteOffset, err, o, "byteOffset", false);
2621
+
2622
+ double byteLength = 1.0;
2623
+ if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true,
2624
+ "BufferView")) {
2625
+ return false;
2626
+ }
2627
+
2628
+ size_t byteStride = 0;
2629
+ double byteStrideValue = 0.0;
2630
+ if (!ParseNumberProperty(&byteStrideValue, err, o, "byteStride", false)) {
2631
+ // Spec says: When byteStride of referenced bufferView is not defined, it
2632
+ // means that accessor elements are tightly packed, i.e., effective stride
2633
+ // equals the size of the element.
2634
+ // We cannot determine the actual byteStride until Accessor are parsed, thus
2635
+ // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
2636
+ byteStride = 0;
2637
+ } else {
2638
+ byteStride = static_cast<size_t>(byteStrideValue);
2639
+ }
2640
+
2641
+ if ((byteStride > 252) || ((byteStride % 4) != 0)) {
2642
+ if (err) {
2643
+ std::stringstream ss;
2644
+ ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
2645
+ "4 : "
2646
+ << byteStride << std::endl;
2647
+
2648
+ (*err) += ss.str();
2649
+ }
2650
+ return false;
2651
+ }
2652
+
2653
+ double target = 0.0;
2654
+ ParseNumberProperty(&target, err, o, "target", false);
2655
+ int targetValue = static_cast<int>(target);
2656
+ if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) ||
2657
+ (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
2658
+ // OK
2659
+ } else {
2660
+ targetValue = 0;
2661
+ }
2662
+ bufferView->target = targetValue;
2663
+
2664
+ ParseStringProperty(&bufferView->name, err, o, "name", false);
2665
+
2666
+ bufferView->buffer = static_cast<int>(buffer);
2667
+ bufferView->byteOffset = static_cast<size_t>(byteOffset);
2668
+ bufferView->byteLength = static_cast<size_t>(byteLength);
2669
+ bufferView->byteStride = static_cast<size_t>(byteStride);
2670
+
2671
+ return true;
2672
+ }
2673
+
2674
+ static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
2675
+ double bufferView = -1.0;
2676
+ if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true,
2677
+ "Accessor")) {
2678
+ return false;
2679
+ }
2680
+
2681
+ double byteOffset = 0.0;
2682
+ ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
2683
+
2684
+ bool normalized = false;
2685
+ ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
2686
+
2687
+ double componentType = 0.0;
2688
+ if (!ParseNumberProperty(&componentType, err, o, "componentType", true,
2689
+ "Accessor")) {
2690
+ return false;
2691
+ }
2692
+
2693
+ double count = 0.0;
2694
+ if (!ParseNumberProperty(&count, err, o, "count", true, "Accessor")) {
2695
+ return false;
2696
+ }
2697
+
2698
+ std::string type;
2699
+ if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
2700
+ return false;
2701
+ }
2702
+
2703
+ if (type.compare("SCALAR") == 0) {
2704
+ accessor->type = TINYGLTF_TYPE_SCALAR;
2705
+ } else if (type.compare("VEC2") == 0) {
2706
+ accessor->type = TINYGLTF_TYPE_VEC2;
2707
+ } else if (type.compare("VEC3") == 0) {
2708
+ accessor->type = TINYGLTF_TYPE_VEC3;
2709
+ } else if (type.compare("VEC4") == 0) {
2710
+ accessor->type = TINYGLTF_TYPE_VEC4;
2711
+ } else if (type.compare("MAT2") == 0) {
2712
+ accessor->type = TINYGLTF_TYPE_MAT2;
2713
+ } else if (type.compare("MAT3") == 0) {
2714
+ accessor->type = TINYGLTF_TYPE_MAT3;
2715
+ } else if (type.compare("MAT4") == 0) {
2716
+ accessor->type = TINYGLTF_TYPE_MAT4;
2717
+ } else {
2718
+ std::stringstream ss;
2719
+ ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
2720
+ if (err) {
2721
+ (*err) += ss.str();
2722
+ }
2723
+ return false;
2724
+ }
2725
+
2726
+ ParseStringProperty(&accessor->name, err, o, "name", false);
2727
+
2728
+ accessor->minValues.clear();
2729
+ accessor->maxValues.clear();
2730
+ ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
2731
+ "Accessor");
2732
+
2733
+ ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
2734
+ "Accessor");
2735
+
2736
+ accessor->count = static_cast<size_t>(count);
2737
+ accessor->bufferView = static_cast<int>(bufferView);
2738
+ accessor->byteOffset = static_cast<size_t>(byteOffset);
2739
+ accessor->normalized = normalized;
2740
+ {
2741
+ int comp = static_cast<int>(componentType);
2742
+ if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE &&
2743
+ comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
2744
+ // OK
2745
+ accessor->componentType = comp;
2746
+ } else {
2747
+ std::stringstream ss;
2748
+ ss << "Invalid `componentType` in accessor. Got " << comp << "\n";
2749
+ if (err) {
2750
+ (*err) += ss.str();
2751
+ }
2752
+ return false;
2753
+ }
2754
+ }
2755
+
2756
+ ParseExtrasProperty(&(accessor->extras), o);
2757
+
2758
+ return true;
2759
+ }
2760
+
2761
+ static bool ParsePrimitive(Primitive *primitive, std::string *err,
2762
+ const json &o) {
2763
+ double material = -1.0;
2764
+ ParseNumberProperty(&material, err, o, "material", false);
2765
+ primitive->material = static_cast<int>(material);
2766
+
2767
+ double mode = static_cast<double>(TINYGLTF_MODE_TRIANGLES);
2768
+ ParseNumberProperty(&mode, err, o, "mode", false);
2769
+
2770
+ int primMode = static_cast<int>(mode);
2771
+ primitive->mode = primMode; // Why only triangled were supported ?
2772
+
2773
+ double indices = -1.0;
2774
+ ParseNumberProperty(&indices, err, o, "indices", false);
2775
+ primitive->indices = static_cast<int>(indices);
2776
+ if (!ParseStringIntProperty(&primitive->attributes, err, o, "attributes",
2777
+ true, "Primitive")) {
2778
+ return false;
2779
+ }
2780
+
2781
+ // Look for morph targets
2782
+ json::const_iterator targetsObject = o.find("targets");
2783
+ if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
2784
+ for (json::const_iterator i = targetsObject.value().begin();
2785
+ i != targetsObject.value().end(); i++) {
2786
+ std::map<std::string, int> targetAttribues;
2787
+
2788
+ const json &dict = i.value();
2789
+ json::const_iterator dictIt(dict.begin());
2790
+ json::const_iterator dictItEnd(dict.end());
2791
+
2792
+ for (; dictIt != dictItEnd; ++dictIt) {
2793
+ targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
2794
+ }
2795
+ primitive->targets.push_back(targetAttribues);
2796
+ }
2797
+ }
2798
+
2799
+ ParseExtrasProperty(&(primitive->extras), o);
2800
+
2801
+ return true;
2802
+ }
2803
+
2804
+ static bool ParseMesh(Mesh *mesh, std::string *err, const json &o) {
2805
+ ParseStringProperty(&mesh->name, err, o, "name", false);
2806
+
2807
+ mesh->primitives.clear();
2808
+ json::const_iterator primObject = o.find("primitives");
2809
+ if ((primObject != o.end()) && primObject.value().is_array()) {
2810
+ for (json::const_iterator i = primObject.value().begin();
2811
+ i != primObject.value().end(); i++) {
2812
+ Primitive primitive;
2813
+ if (ParsePrimitive(&primitive, err, i.value())) {
2814
+ // Only add the primitive if the parsing succeeds.
2815
+ mesh->primitives.push_back(primitive);
2816
+ }
2817
+ }
2818
+ }
2819
+
2820
+ // Look for morph targets
2821
+ json::const_iterator targetsObject = o.find("targets");
2822
+ if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
2823
+ for (json::const_iterator i = targetsObject.value().begin();
2824
+ i != targetsObject.value().end(); i++) {
2825
+ std::map<std::string, int> targetAttribues;
2826
+
2827
+ const json &dict = i.value();
2828
+ json::const_iterator dictIt(dict.begin());
2829
+ json::const_iterator dictItEnd(dict.end());
2830
+
2831
+ for (; dictIt != dictItEnd; ++dictIt) {
2832
+ targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
2833
+ }
2834
+ mesh->targets.push_back(targetAttribues);
2835
+ }
2836
+ }
2837
+
2838
+ // Should probably check if has targets and if dimensions fit
2839
+ ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
2840
+
2841
+ ParseExtensionsProperty(&mesh->extensions, err, o);
2842
+ ParseExtrasProperty(&(mesh->extras), o);
2843
+
2844
+ return true;
2845
+ }
2846
+
2847
+ static bool ParseLight(Light *light, std::string *err, const json &o) {
2848
+ ParseStringProperty(&light->name, err, o, "name", false);
2849
+ ParseNumberArrayProperty(&light->color, err, o, "color", false);
2850
+ ParseStringProperty(&light->type, err, o, "type", false);
2851
+ return true;
2852
+ }
2853
+
2854
+ static bool ParseNode(Node *node, std::string *err, const json &o) {
2855
+ ParseStringProperty(&node->name, err, o, "name", false);
2856
+
2857
+ double skin = -1.0;
2858
+ ParseNumberProperty(&skin, err, o, "skin", false);
2859
+ node->skin = static_cast<int>(skin);
2860
+
2861
+ // Matrix and T/R/S are exclusive
2862
+ if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
2863
+ ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
2864
+ ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
2865
+ ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
2866
+ }
2867
+
2868
+ double camera = -1.0;
2869
+ ParseNumberProperty(&camera, err, o, "camera", false);
2870
+ node->camera = static_cast<int>(camera);
2871
+
2872
+ double mesh = -1.0;
2873
+ ParseNumberProperty(&mesh, err, o, "mesh", false);
2874
+ node->mesh = int(mesh);
2875
+
2876
+ node->children.clear();
2877
+ json::const_iterator childrenObject = o.find("children");
2878
+ if ((childrenObject != o.end()) && childrenObject.value().is_array()) {
2879
+ for (json::const_iterator i = childrenObject.value().begin();
2880
+ i != childrenObject.value().end(); i++) {
2881
+ if (!i.value().is_number()) {
2882
+ if (err) {
2883
+ (*err) += "Invalid `children` array.\n";
2884
+ }
2885
+ return false;
2886
+ }
2887
+ const int &childrenNode = static_cast<int>(i.value());
2888
+ node->children.push_back(childrenNode);
2889
+ }
2890
+ }
2891
+
2892
+ ParseExtensionsProperty(&node->extensions, err, o);
2893
+ ParseExtrasProperty(&(node->extras), o);
2894
+
2895
+ return true;
2896
+ }
2897
+
2898
+ static bool ParseMaterial(Material *material, std::string *err, const json &o) {
2899
+ material->values.clear();
2900
+ material->extensions.clear();
2901
+ material->additionalValues.clear();
2902
+
2903
+ json::const_iterator it(o.begin());
2904
+ json::const_iterator itEnd(o.end());
2905
+
2906
+ for (; it != itEnd; it++) {
2907
+ if (it.key() == "pbrMetallicRoughness") {
2908
+ if (it.value().is_object()) {
2909
+ const json &values_object = it.value();
2910
+
2911
+ json::const_iterator itVal(values_object.begin());
2912
+ json::const_iterator itValEnd(values_object.end());
2913
+
2914
+ for (; itVal != itValEnd; itVal++) {
2915
+ Parameter param;
2916
+ if (ParseParameterProperty(&param, err, values_object, itVal.key(),
2917
+ false)) {
2918
+ material->values[itVal.key()] = param;
2919
+ }
2920
+ }
2921
+ }
2922
+ } else if (it.key() == "extensions" || it.key() == "extras") {
2923
+ // done later, skip, otherwise poorly parsed contents will be saved in the
2924
+ // parametermap and serialized again later
2925
+ } else {
2926
+ Parameter param;
2927
+ if (ParseParameterProperty(&param, err, o, it.key(), false)) {
2928
+ material->additionalValues[it.key()] = param;
2929
+ }
2930
+ }
2931
+ }
2932
+
2933
+ ParseExtensionsProperty(&material->extensions, err, o);
2934
+ ParseExtrasProperty(&(material->extras), o);
2935
+
2936
+ return true;
2937
+ }
2938
+
2939
+ static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
2940
+ const json &o) {
2941
+ double samplerIndex = -1.0;
2942
+ double targetIndex = -1.0;
2943
+ if (!ParseNumberProperty(&samplerIndex, err, o, "sampler", true,
2944
+ "AnimationChannel")) {
2945
+ if (err) {
2946
+ (*err) += "`sampler` field is missing in animation channels\n";
2947
+ }
2948
+ return false;
2949
+ }
2950
+
2951
+ json::const_iterator targetIt = o.find("target");
2952
+ if ((targetIt != o.end()) && targetIt.value().is_object()) {
2953
+ const json &target_object = targetIt.value();
2954
+
2955
+ if (!ParseNumberProperty(&targetIndex, err, target_object, "node", true)) {
2956
+ if (err) {
2957
+ (*err) += "`node` field is missing in animation.channels.target\n";
2958
+ }
2959
+ return false;
2960
+ }
2961
+
2962
+ if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
2963
+ true)) {
2964
+ if (err) {
2965
+ (*err) += "`path` field is missing in animation.channels.target\n";
2966
+ }
2967
+ return false;
2968
+ }
2969
+ }
2970
+
2971
+ channel->sampler = static_cast<int>(samplerIndex);
2972
+ channel->target_node = static_cast<int>(targetIndex);
2973
+
2974
+ ParseExtrasProperty(&(channel->extras), o);
2975
+
2976
+ return true;
2977
+ }
2978
+
2979
+ static bool ParseAnimation(Animation *animation, std::string *err,
2980
+ const json &o) {
2981
+ {
2982
+ json::const_iterator channelsIt = o.find("channels");
2983
+ if ((channelsIt != o.end()) && channelsIt.value().is_array()) {
2984
+ for (json::const_iterator i = channelsIt.value().begin();
2985
+ i != channelsIt.value().end(); i++) {
2986
+ AnimationChannel channel;
2987
+ if (ParseAnimationChannel(&channel, err, i.value())) {
2988
+ // Only add the channel if the parsing succeeds.
2989
+ animation->channels.push_back(channel);
2990
+ }
2991
+ }
2992
+ }
2993
+ }
2994
+
2995
+ {
2996
+ json::const_iterator samplerIt = o.find("samplers");
2997
+ if ((samplerIt != o.end()) && samplerIt.value().is_array()) {
2998
+ const json &sampler_array = samplerIt.value();
2999
+
3000
+ json::const_iterator it = sampler_array.begin();
3001
+ json::const_iterator itEnd = sampler_array.end();
3002
+
3003
+ for (; it != itEnd; it++) {
3004
+ const json &s = it->get<json>();
3005
+
3006
+ AnimationSampler sampler;
3007
+ double inputIndex = -1.0;
3008
+ double outputIndex = -1.0;
3009
+ if (!ParseNumberProperty(&inputIndex, err, s, "input", true)) {
3010
+ if (err) {
3011
+ (*err) += "`input` field is missing in animation.sampler\n";
3012
+ }
3013
+ return false;
3014
+ }
3015
+ if (!ParseStringProperty(&sampler.interpolation, err, s,
3016
+ "interpolation", true)) {
3017
+ if (err) {
3018
+ (*err) += "`interpolation` field is missing in animation.sampler\n";
3019
+ }
3020
+ return false;
3021
+ }
3022
+ if (!ParseNumberProperty(&outputIndex, err, s, "output", true)) {
3023
+ if (err) {
3024
+ (*err) += "`output` field is missing in animation.sampler\n";
3025
+ }
3026
+ return false;
3027
+ }
3028
+ sampler.input = static_cast<int>(inputIndex);
3029
+ sampler.output = static_cast<int>(outputIndex);
3030
+ ParseExtrasProperty(&(sampler.extras), s);
3031
+ animation->samplers.push_back(sampler);
3032
+ }
3033
+ }
3034
+ }
3035
+
3036
+ ParseStringProperty(&animation->name, err, o, "name", false);
3037
+
3038
+ ParseExtrasProperty(&(animation->extras), o);
3039
+
3040
+ return true;
3041
+ }
3042
+
3043
+ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) {
3044
+ ParseStringProperty(&sampler->name, err, o, "name", false);
3045
+
3046
+ double minFilter =
3047
+ static_cast<double>(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR);
3048
+ double magFilter = static_cast<double>(TINYGLTF_TEXTURE_FILTER_LINEAR);
3049
+ double wrapS = static_cast<double>(TINYGLTF_TEXTURE_WRAP_REPEAT);
3050
+ double wrapT = static_cast<double>(TINYGLTF_TEXTURE_WRAP_REPEAT);
3051
+ ParseNumberProperty(&minFilter, err, o, "minFilter", false);
3052
+ ParseNumberProperty(&magFilter, err, o, "magFilter", false);
3053
+ ParseNumberProperty(&wrapS, err, o, "wrapS", false);
3054
+ ParseNumberProperty(&wrapT, err, o, "wrapT", false);
3055
+
3056
+ sampler->minFilter = static_cast<int>(minFilter);
3057
+ sampler->magFilter = static_cast<int>(magFilter);
3058
+ sampler->wrapS = static_cast<int>(wrapS);
3059
+ sampler->wrapT = static_cast<int>(wrapT);
3060
+
3061
+ ParseExtrasProperty(&(sampler->extras), o);
3062
+
3063
+ return true;
3064
+ }
3065
+
3066
+ static bool ParseSkin(Skin *skin, std::string *err, const json &o) {
3067
+ ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
3068
+
3069
+ std::vector<double> joints;
3070
+ if (!ParseNumberArrayProperty(&joints, err, o, "joints", false, "Skin")) {
3071
+ return false;
3072
+ }
3073
+
3074
+ double skeleton = -1.0;
3075
+ ParseNumberProperty(&skeleton, err, o, "skeleton", false, "Skin");
3076
+ skin->skeleton = static_cast<int>(skeleton);
3077
+
3078
+ skin->joints.resize(joints.size());
3079
+ for (size_t i = 0; i < joints.size(); i++) {
3080
+ skin->joints[i] = static_cast<int>(joints[i]);
3081
+ }
3082
+
3083
+ double invBind = -1.0;
3084
+ ParseNumberProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
3085
+ skin->inverseBindMatrices = static_cast<int>(invBind);
3086
+
3087
+ return true;
3088
+ }
3089
+
3090
+ static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
3091
+ const json &o) {
3092
+ double yfov = 0.0;
3093
+ if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
3094
+ return false;
3095
+ }
3096
+
3097
+ double znear = 0.0;
3098
+ if (!ParseNumberProperty(&znear, err, o, "znear", true,
3099
+ "PerspectiveCamera")) {
3100
+ return false;
3101
+ }
3102
+
3103
+ double aspectRatio = 0.0; // = invalid
3104
+ ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
3105
+ "PerspectiveCamera");
3106
+
3107
+ double zfar = 0.0; // = invalid
3108
+ ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
3109
+
3110
+ camera->aspectRatio = aspectRatio;
3111
+ camera->zfar = zfar;
3112
+ camera->yfov = yfov;
3113
+ camera->znear = znear;
3114
+
3115
+ ParseExtensionsProperty(&camera->extensions, err, o);
3116
+ ParseExtrasProperty(&(camera->extras), o);
3117
+
3118
+ // TODO(syoyo): Validate parameter values.
3119
+
3120
+ return true;
3121
+ }
3122
+
3123
+ static bool ParseOrthographicCamera(OrthographicCamera *camera,
3124
+ std::string *err, const json &o) {
3125
+ double xmag = 0.0;
3126
+ if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
3127
+ return false;
3128
+ }
3129
+
3130
+ double ymag = 0.0;
3131
+ if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
3132
+ return false;
3133
+ }
3134
+
3135
+ double zfar = 0.0;
3136
+ if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
3137
+ return false;
3138
+ }
3139
+
3140
+ double znear = 0.0;
3141
+ if (!ParseNumberProperty(&znear, err, o, "znear", true,
3142
+ "OrthographicCamera")) {
3143
+ return false;
3144
+ }
3145
+
3146
+ ParseExtensionsProperty(&camera->extensions, err, o);
3147
+ ParseExtrasProperty(&(camera->extras), o);
3148
+
3149
+ camera->xmag = xmag;
3150
+ camera->ymag = ymag;
3151
+ camera->zfar = zfar;
3152
+ camera->znear = znear;
3153
+
3154
+ // TODO(syoyo): Validate parameter values.
3155
+
3156
+ return true;
3157
+ }
3158
+
3159
+ static bool ParseCamera(Camera *camera, std::string *err, const json &o) {
3160
+ if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
3161
+ return false;
3162
+ }
3163
+
3164
+ if (camera->type.compare("orthographic") == 0) {
3165
+ if (o.find("orthographic") == o.end()) {
3166
+ if (err) {
3167
+ std::stringstream ss;
3168
+ ss << "Orhographic camera description not found." << std::endl;
3169
+ (*err) += ss.str();
3170
+ }
3171
+ return false;
3172
+ }
3173
+
3174
+ const json &v = o.find("orthographic").value();
3175
+ if (!v.is_object()) {
3176
+ if (err) {
3177
+ std::stringstream ss;
3178
+ ss << "\"orthographic\" is not a JSON object." << std::endl;
3179
+ (*err) += ss.str();
3180
+ }
3181
+ return false;
3182
+ }
3183
+
3184
+ if (!ParseOrthographicCamera(&camera->orthographic, err, v.get<json>())) {
3185
+ return false;
3186
+ }
3187
+ } else if (camera->type.compare("perspective") == 0) {
3188
+ if (o.find("perspective") == o.end()) {
3189
+ if (err) {
3190
+ std::stringstream ss;
3191
+ ss << "Perspective camera description not found." << std::endl;
3192
+ (*err) += ss.str();
3193
+ }
3194
+ return false;
3195
+ }
3196
+
3197
+ const json &v = o.find("perspective").value();
3198
+ if (!v.is_object()) {
3199
+ if (err) {
3200
+ std::stringstream ss;
3201
+ ss << "\"perspective\" is not a JSON object." << std::endl;
3202
+ (*err) += ss.str();
3203
+ }
3204
+ return false;
3205
+ }
3206
+
3207
+ if (!ParsePerspectiveCamera(&camera->perspective, err, v.get<json>())) {
3208
+ return false;
3209
+ }
3210
+ } else {
3211
+ if (err) {
3212
+ std::stringstream ss;
3213
+ ss << "Invalid camera type: \"" << camera->type
3214
+ << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
3215
+ (*err) += ss.str();
3216
+ }
3217
+ return false;
3218
+ }
3219
+
3220
+ ParseStringProperty(&camera->name, err, o, "name", false);
3221
+
3222
+ ParseExtensionsProperty(&camera->extensions, err, o);
3223
+ ParseExtrasProperty(&(camera->extras), o);
3224
+
3225
+ return true;
3226
+ }
3227
+
3228
+ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
3229
+ const char *str, unsigned int length,
3230
+ const std::string &base_dir,
3231
+ unsigned int check_sections) {
3232
+ if (length < 4) {
3233
+ if (err) {
3234
+ (*err) = "JSON string too short.\n";
3235
+ }
3236
+ return false;
3237
+ }
3238
+
3239
+ json v;
3240
+
3241
+ #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
3242
+ defined(_CPPUNWIND)) && \
3243
+ not defined(TINYGLTF_NOEXCEPTION)
3244
+ try {
3245
+ v = json::parse(str, str + length);
3246
+
3247
+ } catch (const std::exception &e) {
3248
+ if (err) {
3249
+ (*err) = e.what();
3250
+ }
3251
+ return false;
3252
+ }
3253
+ #else
3254
+ {
3255
+ v = json::parse(str, str + length, nullptr, /* exception */ false);
3256
+
3257
+ if (!v.is_object()) {
3258
+ // Assume parsing was failed.
3259
+ if (err) {
3260
+ (*err) = "Failed to parse JSON object\n";
3261
+ }
3262
+ return false;
3263
+ }
3264
+ }
3265
+ #endif
3266
+
3267
+ if (!v.is_object()) {
3268
+ // root is not an object.
3269
+ if (err) {
3270
+ (*err) = "Root element is not a JSON object\n";
3271
+ }
3272
+ return false;
3273
+ }
3274
+
3275
+ // scene is not mandatory.
3276
+ // FIXME Maybe a better way to handle it than removing the code
3277
+
3278
+ {
3279
+ json::const_iterator it = v.find("scenes");
3280
+ if ((it != v.end()) && it.value().is_array()) {
3281
+ // OK
3282
+ } else if (check_sections & REQUIRE_SCENES) {
3283
+ if (err) {
3284
+ (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
3285
+ }
3286
+ return false;
3287
+ }
3288
+ }
3289
+
3290
+ {
3291
+ json::const_iterator it = v.find("nodes");
3292
+ if ((it != v.end()) && it.value().is_array()) {
3293
+ // OK
3294
+ } else if (check_sections & REQUIRE_NODES) {
3295
+ if (err) {
3296
+ (*err) += "\"nodes\" object not found in .gltf\n";
3297
+ }
3298
+ return false;
3299
+ }
3300
+ }
3301
+
3302
+ {
3303
+ json::const_iterator it = v.find("accessors");
3304
+ if ((it != v.end()) && it.value().is_array()) {
3305
+ // OK
3306
+ } else if (check_sections & REQUIRE_ACCESSORS) {
3307
+ if (err) {
3308
+ (*err) += "\"accessors\" object not found in .gltf\n";
3309
+ }
3310
+ return false;
3311
+ }
3312
+ }
3313
+
3314
+ {
3315
+ json::const_iterator it = v.find("buffers");
3316
+ if ((it != v.end()) && it.value().is_array()) {
3317
+ // OK
3318
+ } else if (check_sections & REQUIRE_BUFFERS) {
3319
+ if (err) {
3320
+ (*err) += "\"buffers\" object not found in .gltf\n";
3321
+ }
3322
+ return false;
3323
+ }
3324
+ }
3325
+
3326
+ {
3327
+ json::const_iterator it = v.find("bufferViews");
3328
+ if ((it != v.end()) && it.value().is_array()) {
3329
+ // OK
3330
+ } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
3331
+ if (err) {
3332
+ (*err) += "\"bufferViews\" object not found in .gltf\n";
3333
+ }
3334
+ return false;
3335
+ }
3336
+ }
3337
+
3338
+ model->buffers.clear();
3339
+ model->bufferViews.clear();
3340
+ model->accessors.clear();
3341
+ model->meshes.clear();
3342
+ model->cameras.clear();
3343
+ model->nodes.clear();
3344
+ model->extensionsUsed.clear();
3345
+ model->extensionsRequired.clear();
3346
+ model->extensions.clear();
3347
+ model->defaultScene = -1;
3348
+
3349
+ // 1. Parse Asset
3350
+ {
3351
+ json::const_iterator it = v.find("asset");
3352
+ if ((it != v.end()) && it.value().is_object()) {
3353
+ const json &root = it.value();
3354
+
3355
+ ParseAsset(&model->asset, err, root);
3356
+ }
3357
+ }
3358
+
3359
+ // 2. Parse extensionUsed
3360
+ {
3361
+ json::const_iterator it = v.find("extensionsUsed");
3362
+ if ((it != v.end()) && it.value().is_array()) {
3363
+ const json &root = it.value();
3364
+ for (unsigned int i = 0; i < root.size(); ++i) {
3365
+ model->extensionsUsed.push_back(root[i].get<std::string>());
3366
+ }
3367
+ }
3368
+ }
3369
+
3370
+ {
3371
+ json::const_iterator it = v.find("extensionsRequired");
3372
+ if ((it != v.end()) && it.value().is_array()) {
3373
+ const json &root = it.value();
3374
+ for (unsigned int i = 0; i < root.size(); ++i) {
3375
+ model->extensionsRequired.push_back(root[i].get<std::string>());
3376
+ }
3377
+ }
3378
+ }
3379
+
3380
+ // 3. Parse Buffer
3381
+ {
3382
+ json::const_iterator rootIt = v.find("buffers");
3383
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3384
+ const json &root = rootIt.value();
3385
+
3386
+ json::const_iterator it(root.begin());
3387
+ json::const_iterator itEnd(root.end());
3388
+ for (; it != itEnd; it++) {
3389
+ if (!it.value().is_object()) {
3390
+ if (err) {
3391
+ (*err) += "`buffers' does not contain an JSON object.";
3392
+ }
3393
+ return false;
3394
+ }
3395
+ Buffer buffer;
3396
+ if (!ParseBuffer(&buffer, err, it->get<json>(), &fs, base_dir,
3397
+ is_binary_, bin_data_, bin_size_)) {
3398
+ return false;
3399
+ }
3400
+
3401
+ model->buffers.push_back(buffer);
3402
+ }
3403
+ }
3404
+ }
3405
+
3406
+ // 4. Parse BufferView
3407
+ {
3408
+ json::const_iterator rootIt = v.find("bufferViews");
3409
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3410
+ const json &root = rootIt.value();
3411
+
3412
+ json::const_iterator it(root.begin());
3413
+ json::const_iterator itEnd(root.end());
3414
+ for (; it != itEnd; it++) {
3415
+ if (!it.value().is_object()) {
3416
+ if (err) {
3417
+ (*err) += "`bufferViews' does not contain an JSON object.";
3418
+ }
3419
+ return false;
3420
+ }
3421
+ BufferView bufferView;
3422
+ if (!ParseBufferView(&bufferView, err, it->get<json>())) {
3423
+ return false;
3424
+ }
3425
+
3426
+ model->bufferViews.push_back(bufferView);
3427
+ }
3428
+ }
3429
+ }
3430
+
3431
+ // 5. Parse Accessor
3432
+ {
3433
+ json::const_iterator rootIt = v.find("accessors");
3434
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3435
+ const json &root = rootIt.value();
3436
+
3437
+ json::const_iterator it(root.begin());
3438
+ json::const_iterator itEnd(root.end());
3439
+ for (; it != itEnd; it++) {
3440
+ if (!it.value().is_object()) {
3441
+ if (err) {
3442
+ (*err) += "`accessors' does not contain an JSON object.";
3443
+ }
3444
+ return false;
3445
+ }
3446
+ Accessor accessor;
3447
+ if (!ParseAccessor(&accessor, err, it->get<json>())) {
3448
+ return false;
3449
+ }
3450
+
3451
+ model->accessors.push_back(accessor);
3452
+ }
3453
+ }
3454
+ }
3455
+
3456
+ // 6. Parse Mesh
3457
+ {
3458
+ json::const_iterator rootIt = v.find("meshes");
3459
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3460
+ const json &root = rootIt.value();
3461
+
3462
+ json::const_iterator it(root.begin());
3463
+ json::const_iterator itEnd(root.end());
3464
+ for (; it != itEnd; it++) {
3465
+ if (!it.value().is_object()) {
3466
+ if (err) {
3467
+ (*err) += "`meshes' does not contain an JSON object.";
3468
+ }
3469
+ return false;
3470
+ }
3471
+ Mesh mesh;
3472
+ if (!ParseMesh(&mesh, err, it->get<json>())) {
3473
+ return false;
3474
+ }
3475
+
3476
+ model->meshes.push_back(mesh);
3477
+ }
3478
+ }
3479
+ }
3480
+
3481
+ // 7. Parse Node
3482
+ {
3483
+ json::const_iterator rootIt = v.find("nodes");
3484
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3485
+ const json &root = rootIt.value();
3486
+
3487
+ json::const_iterator it(root.begin());
3488
+ json::const_iterator itEnd(root.end());
3489
+ for (; it != itEnd; it++) {
3490
+ if (!it.value().is_object()) {
3491
+ if (err) {
3492
+ (*err) += "`nodes' does not contain an JSON object.";
3493
+ }
3494
+ return false;
3495
+ }
3496
+ Node node;
3497
+ if (!ParseNode(&node, err, it->get<json>())) {
3498
+ return false;
3499
+ }
3500
+
3501
+ model->nodes.push_back(node);
3502
+ }
3503
+ }
3504
+ }
3505
+
3506
+ // 8. Parse scenes.
3507
+ {
3508
+ json::const_iterator rootIt = v.find("scenes");
3509
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3510
+ const json &root = rootIt.value();
3511
+
3512
+ json::const_iterator it(root.begin());
3513
+ json::const_iterator itEnd(root.end());
3514
+ for (; it != itEnd; it++) {
3515
+ if (!(it.value().is_object())) {
3516
+ if (err) {
3517
+ (*err) += "`scenes' does not contain an JSON object.";
3518
+ }
3519
+ return false;
3520
+ }
3521
+ const json &o = it->get<json>();
3522
+ std::vector<double> nodes;
3523
+ if (!ParseNumberArrayProperty(&nodes, err, o, "nodes", false)) {
3524
+ return false;
3525
+ }
3526
+
3527
+ Scene scene;
3528
+ ParseStringProperty(&scene.name, err, o, "name", false);
3529
+ std::vector<int> nodesIds;
3530
+ for (size_t i = 0; i < nodes.size(); i++) {
3531
+ nodesIds.push_back(static_cast<int>(nodes[i]));
3532
+ }
3533
+ scene.nodes = nodesIds;
3534
+
3535
+ ParseExtensionsProperty(&scene.extensions, err, o);
3536
+ ParseExtrasProperty(&scene.extras, o);
3537
+
3538
+ model->scenes.push_back(scene);
3539
+ }
3540
+ }
3541
+ }
3542
+
3543
+ // 9. Parse default scenes.
3544
+ {
3545
+ json::const_iterator rootIt = v.find("scene");
3546
+ if ((rootIt != v.end()) && rootIt.value().is_number()) {
3547
+ const int defaultScene = rootIt.value();
3548
+
3549
+ model->defaultScene = static_cast<int>(defaultScene);
3550
+ }
3551
+ }
3552
+
3553
+ // 10. Parse Material
3554
+ {
3555
+ json::const_iterator rootIt = v.find("materials");
3556
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3557
+ const json &root = rootIt.value();
3558
+
3559
+ json::const_iterator it(root.begin());
3560
+ json::const_iterator itEnd(root.end());
3561
+ for (; it != itEnd; it++) {
3562
+ if (!it.value().is_object()) {
3563
+ if (err) {
3564
+ (*err) += "`materials' does not contain an JSON object.";
3565
+ }
3566
+ return false;
3567
+ }
3568
+ json jsonMaterial = it->get<json>();
3569
+
3570
+ Material material;
3571
+ ParseStringProperty(&material.name, err, jsonMaterial, "name", false);
3572
+
3573
+ if (!ParseMaterial(&material, err, jsonMaterial)) {
3574
+ return false;
3575
+ }
3576
+
3577
+ model->materials.push_back(material);
3578
+ }
3579
+ }
3580
+ }
3581
+
3582
+ // 11. Parse Image
3583
+ {
3584
+ json::const_iterator rootIt = v.find("images");
3585
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3586
+ const json &root = rootIt.value();
3587
+
3588
+ json::const_iterator it(root.begin());
3589
+ json::const_iterator itEnd(root.end());
3590
+ for (; it != itEnd; it++) {
3591
+ if (!it.value().is_object()) {
3592
+ if (err) {
3593
+ (*err) += "`images' does not contain an JSON object.";
3594
+ }
3595
+ return false;
3596
+ }
3597
+ Image image;
3598
+ if (!ParseImage(&image, err, warn, it.value(), base_dir, &fs,
3599
+ &this->LoadImageData, load_image_user_data_)) {
3600
+ return false;
3601
+ }
3602
+
3603
+ if (image.bufferView != -1) {
3604
+ // Load image from the buffer view.
3605
+ if (size_t(image.bufferView) >= model->bufferViews.size()) {
3606
+ if (err) {
3607
+ std::stringstream ss;
3608
+ ss << "bufferView \"" << image.bufferView
3609
+ << "\" not found in the scene." << std::endl;
3610
+ (*err) += ss.str();
3611
+ }
3612
+ return false;
3613
+ }
3614
+
3615
+ const BufferView &bufferView =
3616
+ model->bufferViews[size_t(image.bufferView)];
3617
+ const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
3618
+
3619
+ if (*LoadImageData == nullptr) {
3620
+ if (err) {
3621
+ (*err) += "No LoadImageData callback specified.\n";
3622
+ }
3623
+ return false;
3624
+ }
3625
+ bool ret = LoadImageData(&image, err, warn, image.width, image.height,
3626
+ &buffer.data[bufferView.byteOffset],
3627
+ static_cast<int>(bufferView.byteLength),
3628
+ load_image_user_data_);
3629
+ if (!ret) {
3630
+ return false;
3631
+ }
3632
+ }
3633
+
3634
+ model->images.push_back(image);
3635
+ }
3636
+ }
3637
+ }
3638
+
3639
+ // 12. Parse Texture
3640
+ {
3641
+ json::const_iterator rootIt = v.find("textures");
3642
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3643
+ const json &root = rootIt.value();
3644
+
3645
+ json::const_iterator it(root.begin());
3646
+ json::const_iterator itEnd(root.end());
3647
+ for (; it != itEnd; it++) {
3648
+ if (!it.value().is_object()) {
3649
+ if (err) {
3650
+ (*err) += "`textures' does not contain an JSON object.";
3651
+ }
3652
+ return false;
3653
+ }
3654
+ Texture texture;
3655
+ if (!ParseTexture(&texture, err, it->get<json>(), base_dir)) {
3656
+ return false;
3657
+ }
3658
+
3659
+ model->textures.push_back(texture);
3660
+ }
3661
+ }
3662
+ }
3663
+
3664
+ // 13. Parse Animation
3665
+ {
3666
+ json::const_iterator rootIt = v.find("animations");
3667
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3668
+ const json &root = rootIt.value();
3669
+
3670
+ json::const_iterator it(root.begin());
3671
+ json::const_iterator itEnd(root.end());
3672
+ for (; it != itEnd; ++it) {
3673
+ if (!it.value().is_object()) {
3674
+ if (err) {
3675
+ (*err) += "`animations' does not contain an JSON object.";
3676
+ }
3677
+ return false;
3678
+ }
3679
+ Animation animation;
3680
+ if (!ParseAnimation(&animation, err, it->get<json>())) {
3681
+ return false;
3682
+ }
3683
+
3684
+ model->animations.push_back(animation);
3685
+ }
3686
+ }
3687
+ }
3688
+
3689
+ // 14. Parse Skin
3690
+ {
3691
+ json::const_iterator rootIt = v.find("skins");
3692
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3693
+ const json &root = rootIt.value();
3694
+
3695
+ json::const_iterator it(root.begin());
3696
+ json::const_iterator itEnd(root.end());
3697
+ for (; it != itEnd; ++it) {
3698
+ if (!it.value().is_object()) {
3699
+ if (err) {
3700
+ (*err) += "`skins' does not contain an JSON object.";
3701
+ }
3702
+ return false;
3703
+ }
3704
+ Skin skin;
3705
+ if (!ParseSkin(&skin, err, it->get<json>())) {
3706
+ return false;
3707
+ }
3708
+
3709
+ model->skins.push_back(skin);
3710
+ }
3711
+ }
3712
+ }
3713
+
3714
+ // 15. Parse Sampler
3715
+ {
3716
+ json::const_iterator rootIt = v.find("samplers");
3717
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3718
+ const json &root = rootIt.value();
3719
+
3720
+ json::const_iterator it(root.begin());
3721
+ json::const_iterator itEnd(root.end());
3722
+ for (; it != itEnd; ++it) {
3723
+ if (!it.value().is_object()) {
3724
+ if (err) {
3725
+ (*err) += "`samplers' does not contain an JSON object.";
3726
+ }
3727
+ return false;
3728
+ }
3729
+ Sampler sampler;
3730
+ if (!ParseSampler(&sampler, err, it->get<json>())) {
3731
+ return false;
3732
+ }
3733
+
3734
+ model->samplers.push_back(sampler);
3735
+ }
3736
+ }
3737
+ }
3738
+
3739
+ // 16. Parse Camera
3740
+ {
3741
+ json::const_iterator rootIt = v.find("cameras");
3742
+ if ((rootIt != v.end()) && rootIt.value().is_array()) {
3743
+ const json &root = rootIt.value();
3744
+
3745
+ json::const_iterator it(root.begin());
3746
+ json::const_iterator itEnd(root.end());
3747
+ for (; it != itEnd; ++it) {
3748
+ if (!it.value().is_object()) {
3749
+ if (err) {
3750
+ (*err) += "`cameras' does not contain an JSON object.";
3751
+ }
3752
+ return false;
3753
+ }
3754
+ Camera camera;
3755
+ if (!ParseCamera(&camera, err, it->get<json>())) {
3756
+ return false;
3757
+ }
3758
+
3759
+ model->cameras.push_back(camera);
3760
+ }
3761
+ }
3762
+ }
3763
+
3764
+ // 17. Parse Extensions
3765
+ ParseExtensionsProperty(&model->extensions, err, v);
3766
+
3767
+ // 18. Specific extension implementations
3768
+ {
3769
+ json::const_iterator rootIt = v.find("extensions");
3770
+ if ((rootIt != v.end()) && rootIt.value().is_object()) {
3771
+ const json &root = rootIt.value();
3772
+
3773
+ json::const_iterator it(root.begin());
3774
+ json::const_iterator itEnd(root.end());
3775
+ for (; it != itEnd; ++it) {
3776
+ // parse KHR_lights_cmn extension
3777
+ if ((it.key().compare("KHR_lights_cmn") == 0) &&
3778
+ it.value().is_object()) {
3779
+ const json &object = it.value();
3780
+ json::const_iterator itLight(object.find("lights"));
3781
+ json::const_iterator itLightEnd(object.end());
3782
+ if (itLight == itLightEnd) {
3783
+ continue;
3784
+ }
3785
+
3786
+ if (!itLight.value().is_array()) {
3787
+ continue;
3788
+ }
3789
+
3790
+ const json &lights = itLight.value();
3791
+ json::const_iterator arrayIt(lights.begin());
3792
+ json::const_iterator arrayItEnd(lights.end());
3793
+ for (; arrayIt != arrayItEnd; ++arrayIt) {
3794
+ Light light;
3795
+ if (!ParseLight(&light, err, arrayIt.value())) {
3796
+ return false;
3797
+ }
3798
+ model->lights.push_back(light);
3799
+ }
3800
+ }
3801
+ }
3802
+ }
3803
+ }
3804
+
3805
+ // 19. Parse Extras
3806
+ ParseExtrasProperty(&model->extras, v);
3807
+
3808
+ return true;
3809
+ }
3810
+
3811
+ bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
3812
+ std::string *warn, const char *str,
3813
+ unsigned int length,
3814
+ const std::string &base_dir,
3815
+ unsigned int check_sections) {
3816
+ is_binary_ = false;
3817
+ bin_data_ = nullptr;
3818
+ bin_size_ = 0;
3819
+
3820
+ return LoadFromString(model, err, warn, str, length, base_dir,
3821
+ check_sections);
3822
+ }
3823
+
3824
+ bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
3825
+ std::string *warn, const std::string &filename,
3826
+ unsigned int check_sections) {
3827
+ std::stringstream ss;
3828
+
3829
+ if (fs.ReadWholeFile == nullptr) {
3830
+ // Programmer error, assert() ?
3831
+ ss << "Failed to read file: " << filename
3832
+ << ": one or more FS callback not set" << std::endl;
3833
+ if (err) {
3834
+ (*err) = ss.str();
3835
+ }
3836
+ return false;
3837
+ }
3838
+
3839
+ std::vector<unsigned char> data;
3840
+ std::string fileerr;
3841
+ bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
3842
+ if (!fileread) {
3843
+ ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
3844
+ if (err) {
3845
+ (*err) = ss.str();
3846
+ }
3847
+ return false;
3848
+ }
3849
+
3850
+ size_t sz = data.size();
3851
+ if (sz == 0) {
3852
+ if (err) {
3853
+ (*err) = "Empty file.";
3854
+ }
3855
+ return false;
3856
+ }
3857
+
3858
+ std::string basedir = GetBaseDir(filename);
3859
+
3860
+ bool ret = LoadASCIIFromString(
3861
+ model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
3862
+ static_cast<unsigned int>(data.size()), basedir, check_sections);
3863
+
3864
+ return ret;
3865
+ }
3866
+
3867
+ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
3868
+ std::string *warn,
3869
+ const unsigned char *bytes,
3870
+ unsigned int size,
3871
+ const std::string &base_dir,
3872
+ unsigned int check_sections) {
3873
+ if (size < 20) {
3874
+ if (err) {
3875
+ (*err) = "Too short data size for glTF Binary.";
3876
+ }
3877
+ return false;
3878
+ }
3879
+
3880
+ if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
3881
+ bytes[3] == 'F') {
3882
+ // ok
3883
+ } else {
3884
+ if (err) {
3885
+ (*err) = "Invalid magic.";
3886
+ }
3887
+ return false;
3888
+ }
3889
+
3890
+ unsigned int version; // 4 bytes
3891
+ unsigned int length; // 4 bytes
3892
+ unsigned int model_length; // 4 bytes
3893
+ unsigned int model_format; // 4 bytes;
3894
+
3895
+ // @todo { Endian swap for big endian machine. }
3896
+ memcpy(&version, bytes + 4, 4);
3897
+ swap4(&version);
3898
+ memcpy(&length, bytes + 8, 4);
3899
+ swap4(&length);
3900
+ memcpy(&model_length, bytes + 12, 4);
3901
+ swap4(&model_length);
3902
+ memcpy(&model_format, bytes + 16, 4);
3903
+ swap4(&model_format);
3904
+
3905
+ // In case the Bin buffer is not present, the size is exactly 20 + size of
3906
+ // JSON contents,
3907
+ // so use "greater than" operator.
3908
+ if ((20 + model_length > size) || (model_length < 1) ||
3909
+ (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
3910
+ if (err) {
3911
+ (*err) = "Invalid glTF binary.";
3912
+ }
3913
+ return false;
3914
+ }
3915
+
3916
+ // Extract JSON string.
3917
+ std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
3918
+ model_length);
3919
+
3920
+ is_binary_ = true;
3921
+ bin_data_ = bytes + 20 + model_length +
3922
+ 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
3923
+ bin_size_ =
3924
+ length - (20 + model_length); // extract header + JSON scene data.
3925
+
3926
+ bool ret = LoadFromString(model, err, warn,
3927
+ reinterpret_cast<const char *>(&bytes[20]),
3928
+ model_length, base_dir, check_sections);
3929
+ if (!ret) {
3930
+ return ret;
3931
+ }
3932
+
3933
+ return true;
3934
+ }
3935
+
3936
+ bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
3937
+ std::string *warn,
3938
+ const std::string &filename,
3939
+ unsigned int check_sections) {
3940
+ std::stringstream ss;
3941
+
3942
+ if (fs.ReadWholeFile == nullptr) {
3943
+ // Programmer error, assert() ?
3944
+ ss << "Failed to read file: " << filename
3945
+ << ": one or more FS callback not set" << std::endl;
3946
+ if (err) {
3947
+ (*err) = ss.str();
3948
+ }
3949
+ return false;
3950
+ }
3951
+
3952
+ std::vector<unsigned char> data;
3953
+ std::string fileerr;
3954
+ bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
3955
+ if (!fileread) {
3956
+ ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
3957
+ if (err) {
3958
+ (*err) = ss.str();
3959
+ }
3960
+ return false;
3961
+ }
3962
+
3963
+ std::string basedir = GetBaseDir(filename);
3964
+
3965
+ bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
3966
+ static_cast<unsigned int>(data.size()),
3967
+ basedir, check_sections);
3968
+
3969
+ return ret;
3970
+ }
3971
+
3972
+ ///////////////////////
3973
+ // GLTF Serialization
3974
+ ///////////////////////
3975
+
3976
+ // typedef std::pair<std::string, json> json_object_pair;
3977
+
3978
+ template <typename T>
3979
+ static void SerializeNumberProperty(const std::string &key, T number,
3980
+ json &obj) {
3981
+ // obj.insert(
3982
+ // json_object_pair(key, json(static_cast<double>(number))));
3983
+ // obj[key] = static_cast<double>(number);
3984
+ obj[key] = number;
3985
+ }
3986
+
3987
+ template <typename T>
3988
+ static void SerializeNumberArrayProperty(const std::string &key,
3989
+ const std::vector<T> &value,
3990
+ json &obj) {
3991
+ json o;
3992
+ json vals;
3993
+
3994
+ for (unsigned int i = 0; i < value.size(); ++i) {
3995
+ vals.push_back(static_cast<T>(value[i]));
3996
+ }
3997
+ if (!vals.is_null()) {
3998
+ obj[key] = vals;
3999
+ }
4000
+ }
4001
+
4002
+ static void SerializeStringProperty(const std::string &key,
4003
+ const std::string &value, json &obj) {
4004
+ obj[key] = value;
4005
+ }
4006
+
4007
+ static void SerializeStringArrayProperty(const std::string &key,
4008
+ const std::vector<std::string> &value,
4009
+ json &obj) {
4010
+ json o;
4011
+ json vals;
4012
+
4013
+ for (unsigned int i = 0; i < value.size(); ++i) {
4014
+ vals.push_back(value[i]);
4015
+ }
4016
+
4017
+ obj[key] = vals;
4018
+ }
4019
+
4020
+ static bool ValueToJson(const Value &value, json *ret) {
4021
+ json obj;
4022
+ switch (value.Type()) {
4023
+ case NUMBER_TYPE:
4024
+ obj = json(value.Get<double>());
4025
+ break;
4026
+ case INT_TYPE:
4027
+ obj = json(value.Get<int>());
4028
+ break;
4029
+ case BOOL_TYPE:
4030
+ obj = json(value.Get<bool>());
4031
+ break;
4032
+ case STRING_TYPE:
4033
+ obj = json(value.Get<std::string>());
4034
+ break;
4035
+ case ARRAY_TYPE: {
4036
+ for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
4037
+ Value elementValue = value.Get(int(i));
4038
+ json elementJson;
4039
+ if (ValueToJson(value.Get(int(i)), &elementJson))
4040
+ obj.push_back(elementJson);
4041
+ }
4042
+ break;
4043
+ }
4044
+ case BINARY_TYPE:
4045
+ // TODO
4046
+ // obj = json(value.Get<std::vector<unsigned char>>());
4047
+ return false;
4048
+ break;
4049
+ case OBJECT_TYPE: {
4050
+ Value::Object objMap = value.Get<Value::Object>();
4051
+ for (auto &it : objMap) {
4052
+ json elementJson;
4053
+ if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
4054
+ }
4055
+ break;
4056
+ }
4057
+ case NULL_TYPE:
4058
+ default:
4059
+ return false;
4060
+ }
4061
+ if (ret) *ret = obj;
4062
+ return true;
4063
+ }
4064
+
4065
+ static void SerializeValue(const std::string &key, const Value &value,
4066
+ json &obj) {
4067
+ json ret;
4068
+ if (ValueToJson(value, &ret)) obj[key] = ret;
4069
+ }
4070
+
4071
+ static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
4072
+ json &o) {
4073
+ std::string header = "data:application/octet-stream;base64,";
4074
+ std::string encodedData =
4075
+ base64_encode(&data[0], static_cast<unsigned int>(data.size()));
4076
+ SerializeStringProperty("uri", header + encodedData, o);
4077
+ }
4078
+
4079
+ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
4080
+ const std::string &binFilename) {
4081
+ std::ofstream output(binFilename.c_str(), std::ofstream::binary);
4082
+ if(!output.is_open()) return false;
4083
+ output.write(reinterpret_cast<const char *>(&data[0]),
4084
+ std::streamsize(data.size()));
4085
+ output.close();
4086
+ return true;
4087
+ }
4088
+
4089
+ static void SerializeParameterMap(ParameterMap &param, json &o) {
4090
+ for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
4091
+ ++paramIt) {
4092
+ if (paramIt->second.number_array.size()) {
4093
+ SerializeNumberArrayProperty<double>(paramIt->first,
4094
+ paramIt->second.number_array, o);
4095
+ } else if (paramIt->second.json_double_value.size()) {
4096
+ json json_double_value;
4097
+ for (std::map<std::string, double>::iterator it =
4098
+ paramIt->second.json_double_value.begin();
4099
+ it != paramIt->second.json_double_value.end(); ++it) {
4100
+ if (it->first == "index") {
4101
+ json_double_value[it->first] = paramIt->second.TextureIndex();
4102
+ } else {
4103
+ json_double_value[it->first] = it->second;
4104
+ }
4105
+ }
4106
+
4107
+ o[paramIt->first] = json_double_value;
4108
+ } else if (!paramIt->second.string_value.empty()) {
4109
+ SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
4110
+ } else if (paramIt->second.has_number_value) {
4111
+ o[paramIt->first] = paramIt->second.number_value;
4112
+ } else {
4113
+ o[paramIt->first] = paramIt->second.bool_value;
4114
+ }
4115
+ }
4116
+ }
4117
+
4118
+ static void SerializeExtensionMap(ExtensionMap &extensions, json &o) {
4119
+ if (!extensions.size()) return;
4120
+
4121
+ json extMap;
4122
+ for (ExtensionMap::iterator extIt = extensions.begin();
4123
+ extIt != extensions.end(); ++extIt) {
4124
+ json extension_values;
4125
+
4126
+ // Allow an empty object for extension(#97)
4127
+ json ret;
4128
+ if (ValueToJson(extIt->second, &ret)) {
4129
+ extMap[extIt->first] = ret;
4130
+ }
4131
+ if(ret.is_null()) {
4132
+ if (!(extIt->first.empty())) { // name should not be empty, but for sure
4133
+ // create empty object so that an extension name is still included in json.
4134
+ extMap[extIt->first] = json({});
4135
+ }
4136
+ }
4137
+ }
4138
+ o["extensions"] = extMap;
4139
+ }
4140
+
4141
+ static void SerializeGltfAccessor(Accessor &accessor, json &o) {
4142
+ SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
4143
+
4144
+ if (accessor.byteOffset != 0.0)
4145
+ SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
4146
+
4147
+ SerializeNumberProperty<int>("componentType", accessor.componentType, o);
4148
+ SerializeNumberProperty<size_t>("count", accessor.count, o);
4149
+ SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
4150
+ SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
4151
+ std::string type;
4152
+ switch (accessor.type) {
4153
+ case TINYGLTF_TYPE_SCALAR:
4154
+ type = "SCALAR";
4155
+ break;
4156
+ case TINYGLTF_TYPE_VEC2:
4157
+ type = "VEC2";
4158
+ break;
4159
+ case TINYGLTF_TYPE_VEC3:
4160
+ type = "VEC3";
4161
+ break;
4162
+ case TINYGLTF_TYPE_VEC4:
4163
+ type = "VEC4";
4164
+ break;
4165
+ case TINYGLTF_TYPE_MAT2:
4166
+ type = "MAT2";
4167
+ break;
4168
+ case TINYGLTF_TYPE_MAT3:
4169
+ type = "MAT3";
4170
+ break;
4171
+ case TINYGLTF_TYPE_MAT4:
4172
+ type = "MAT4";
4173
+ break;
4174
+ }
4175
+
4176
+ SerializeStringProperty("type", type, o);
4177
+ if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
4178
+
4179
+ if (accessor.extras.Type() != NULL_TYPE) {
4180
+ SerializeValue("extras", accessor.extras, o);
4181
+ }
4182
+ }
4183
+
4184
+ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
4185
+ SerializeNumberProperty("sampler", channel.sampler, o);
4186
+ json target;
4187
+ SerializeNumberProperty("node", channel.target_node, target);
4188
+ SerializeStringProperty("path", channel.target_path, target);
4189
+
4190
+ o["target"] = target;
4191
+
4192
+ if (channel.extras.Type() != NULL_TYPE) {
4193
+ SerializeValue("extras", channel.extras, o);
4194
+ }
4195
+ }
4196
+
4197
+ static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
4198
+ SerializeNumberProperty("input", sampler.input, o);
4199
+ SerializeNumberProperty("output", sampler.output, o);
4200
+ SerializeStringProperty("interpolation", sampler.interpolation, o);
4201
+
4202
+ if (sampler.extras.Type() != NULL_TYPE) {
4203
+ SerializeValue("extras", sampler.extras, o);
4204
+ }
4205
+ }
4206
+
4207
+ static void SerializeGltfAnimation(Animation &animation, json &o) {
4208
+ if (!animation.name.empty())
4209
+ SerializeStringProperty("name", animation.name, o);
4210
+ json channels;
4211
+ for (unsigned int i = 0; i < animation.channels.size(); ++i) {
4212
+ json channel;
4213
+ AnimationChannel gltfChannel = animation.channels[i];
4214
+ SerializeGltfAnimationChannel(gltfChannel, channel);
4215
+ channels.push_back(channel);
4216
+ }
4217
+ o["channels"] = channels;
4218
+
4219
+ json samplers;
4220
+ for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
4221
+ json sampler;
4222
+ AnimationSampler gltfSampler = animation.samplers[i];
4223
+ SerializeGltfAnimationSampler(gltfSampler, sampler);
4224
+ samplers.push_back(sampler);
4225
+ }
4226
+
4227
+ o["samplers"] = samplers;
4228
+
4229
+ if (animation.extras.Type() != NULL_TYPE) {
4230
+ SerializeValue("extras", animation.extras, o);
4231
+ }
4232
+ }
4233
+
4234
+ static void SerializeGltfAsset(Asset &asset, json &o) {
4235
+ if (!asset.generator.empty()) {
4236
+ SerializeStringProperty("generator", asset.generator, o);
4237
+ }
4238
+
4239
+ if (!asset.version.empty()) {
4240
+ SerializeStringProperty("version", asset.version, o);
4241
+ }
4242
+
4243
+ if (asset.extras.Keys().size()) {
4244
+ SerializeValue("extras", asset.extras, o);
4245
+ }
4246
+
4247
+ SerializeExtensionMap(asset.extensions, o);
4248
+ }
4249
+
4250
+ static void SerializeGltfBuffer(Buffer &buffer, json &o) {
4251
+ SerializeNumberProperty("byteLength", buffer.data.size(), o);
4252
+ SerializeGltfBufferData(buffer.data, o);
4253
+
4254
+ if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
4255
+
4256
+ if (buffer.extras.Type() != NULL_TYPE) {
4257
+ SerializeValue("extras", buffer.extras, o);
4258
+ }
4259
+ }
4260
+
4261
+ static bool SerializeGltfBuffer(Buffer &buffer, json &o,
4262
+ const std::string &binFilename,
4263
+ const std::string &binBaseFilename) {
4264
+ if(!SerializeGltfBufferData(buffer.data, binFilename)) return false;
4265
+ SerializeNumberProperty("byteLength", buffer.data.size(), o);
4266
+ SerializeStringProperty("uri", binBaseFilename, o);
4267
+
4268
+ if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
4269
+
4270
+ if (buffer.extras.Type() != NULL_TYPE) {
4271
+ SerializeValue("extras", buffer.extras, o);
4272
+ }
4273
+ return true;
4274
+ }
4275
+
4276
+ static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
4277
+ SerializeNumberProperty("buffer", bufferView.buffer, o);
4278
+ SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
4279
+
4280
+ // byteStride is optional, minimum allowed is 4
4281
+ if (bufferView.byteStride >= 4) {
4282
+ SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
4283
+ }
4284
+ // byteOffset is optional, default is 0
4285
+ if (bufferView.byteOffset > 0) {
4286
+ SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
4287
+ }
4288
+ // Target is optional, check if it contains a valid value
4289
+ if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
4290
+ bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
4291
+ SerializeNumberProperty("target", bufferView.target, o);
4292
+ }
4293
+ if (bufferView.name.size()) {
4294
+ SerializeStringProperty("name", bufferView.name, o);
4295
+ }
4296
+
4297
+ if (bufferView.extras.Type() != NULL_TYPE) {
4298
+ SerializeValue("extras", bufferView.extras, o);
4299
+ }
4300
+ }
4301
+
4302
+ static void SerializeGltfImage(Image &image, json &o) {
4303
+ SerializeStringProperty("uri", image.uri, o);
4304
+
4305
+ if (image.name.size()) {
4306
+ SerializeStringProperty("name", image.name, o);
4307
+ }
4308
+
4309
+ if (image.extras.Type() != NULL_TYPE) {
4310
+ SerializeValue("extras", image.extras, o);
4311
+ }
4312
+
4313
+ SerializeExtensionMap(image.extensions, o);
4314
+ }
4315
+
4316
+ static void SerializeGltfMaterial(Material &material, json &o) {
4317
+ if (material.extras.Size()) SerializeValue("extras", material.extras, o);
4318
+ SerializeExtensionMap(material.extensions, o);
4319
+
4320
+ if (material.values.size()) {
4321
+ json pbrMetallicRoughness;
4322
+ SerializeParameterMap(material.values, pbrMetallicRoughness);
4323
+ o["pbrMetallicRoughness"] = pbrMetallicRoughness;
4324
+ }
4325
+
4326
+ SerializeParameterMap(material.additionalValues, o);
4327
+
4328
+ if (material.name.size()) {
4329
+ SerializeStringProperty("name", material.name, o);
4330
+ }
4331
+
4332
+ if (material.extras.Type() != NULL_TYPE) {
4333
+ SerializeValue("extras", material.extras, o);
4334
+ }
4335
+ }
4336
+
4337
+ static void SerializeGltfMesh(Mesh &mesh, json &o) {
4338
+ json primitives;
4339
+ for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
4340
+ json primitive;
4341
+ json attributes;
4342
+ Primitive gltfPrimitive = mesh.primitives[i];
4343
+ for (std::map<std::string, int>::iterator attrIt =
4344
+ gltfPrimitive.attributes.begin();
4345
+ attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
4346
+ SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
4347
+ }
4348
+
4349
+ primitive["attributes"] = attributes;
4350
+
4351
+ // Indicies is optional
4352
+ if (gltfPrimitive.indices > -1) {
4353
+ SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
4354
+ }
4355
+ // Material is optional
4356
+ if (gltfPrimitive.material > -1) {
4357
+ SerializeNumberProperty<int>("material", gltfPrimitive.material,
4358
+ primitive);
4359
+ }
4360
+ SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
4361
+
4362
+ // Morph targets
4363
+ if (gltfPrimitive.targets.size()) {
4364
+ json targets;
4365
+ for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
4366
+ json targetAttributes;
4367
+ std::map<std::string, int> targetData = gltfPrimitive.targets[k];
4368
+ for (std::map<std::string, int>::iterator attrIt = targetData.begin();
4369
+ attrIt != targetData.end(); ++attrIt) {
4370
+ SerializeNumberProperty<int>(attrIt->first, attrIt->second,
4371
+ targetAttributes);
4372
+ }
4373
+
4374
+ targets.push_back(targetAttributes);
4375
+ }
4376
+ primitive["targets"] = targets;
4377
+ }
4378
+
4379
+ if (gltfPrimitive.extras.Type() != NULL_TYPE) {
4380
+ SerializeValue("extras", gltfPrimitive.extras, primitive);
4381
+ }
4382
+
4383
+ primitives.push_back(primitive);
4384
+ }
4385
+
4386
+ o["primitives"] = primitives;
4387
+ if (mesh.weights.size()) {
4388
+ SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
4389
+ }
4390
+
4391
+ if (mesh.name.size()) {
4392
+ SerializeStringProperty("name", mesh.name, o);
4393
+ }
4394
+
4395
+ if (mesh.extras.Type() != NULL_TYPE) {
4396
+ SerializeValue("extras", mesh.extras, o);
4397
+ }
4398
+ }
4399
+
4400
+ static void SerializeGltfLight(Light &light, json &o) {
4401
+ if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
4402
+ SerializeNumberArrayProperty("color", light.color, o);
4403
+ SerializeStringProperty("type", light.type, o);
4404
+ }
4405
+
4406
+ static void SerializeGltfNode(Node &node, json &o) {
4407
+ if (node.translation.size() > 0) {
4408
+ SerializeNumberArrayProperty<double>("translation", node.translation, o);
4409
+ }
4410
+ if (node.rotation.size() > 0) {
4411
+ SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
4412
+ }
4413
+ if (node.scale.size() > 0) {
4414
+ SerializeNumberArrayProperty<double>("scale", node.scale, o);
4415
+ }
4416
+ if (node.matrix.size() > 0) {
4417
+ SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
4418
+ }
4419
+ if (node.mesh != -1) {
4420
+ SerializeNumberProperty<int>("mesh", node.mesh, o);
4421
+ }
4422
+
4423
+ if (node.skin != -1) {
4424
+ SerializeNumberProperty<int>("skin", node.skin, o);
4425
+ }
4426
+
4427
+ if (node.camera != -1) {
4428
+ SerializeNumberProperty<int>("camera", node.camera, o);
4429
+ }
4430
+
4431
+ if (node.extras.Type() != NULL_TYPE) {
4432
+ SerializeValue("extras", node.extras, o);
4433
+ }
4434
+
4435
+ SerializeExtensionMap(node.extensions, o);
4436
+ if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
4437
+ SerializeNumberArrayProperty<int>("children", node.children, o);
4438
+ }
4439
+
4440
+ static void SerializeGltfSampler(Sampler &sampler, json &o) {
4441
+ SerializeNumberProperty("magFilter", sampler.magFilter, o);
4442
+ SerializeNumberProperty("minFilter", sampler.minFilter, o);
4443
+ SerializeNumberProperty("wrapR", sampler.wrapR, o);
4444
+ SerializeNumberProperty("wrapS", sampler.wrapS, o);
4445
+ SerializeNumberProperty("wrapT", sampler.wrapT, o);
4446
+
4447
+ if (sampler.extras.Type() != NULL_TYPE) {
4448
+ SerializeValue("extras", sampler.extras, o);
4449
+ }
4450
+ }
4451
+
4452
+ static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
4453
+ json &o) {
4454
+ SerializeNumberProperty("zfar", camera.zfar, o);
4455
+ SerializeNumberProperty("znear", camera.znear, o);
4456
+ SerializeNumberProperty("xmag", camera.xmag, o);
4457
+ SerializeNumberProperty("ymag", camera.ymag, o);
4458
+
4459
+ if (camera.extras.Type() != NULL_TYPE) {
4460
+ SerializeValue("extras", camera.extras, o);
4461
+ }
4462
+ }
4463
+
4464
+ static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
4465
+ json &o) {
4466
+ SerializeNumberProperty("zfar", camera.zfar, o);
4467
+ SerializeNumberProperty("znear", camera.znear, o);
4468
+ if (camera.aspectRatio > 0) {
4469
+ SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
4470
+ }
4471
+
4472
+ if (camera.yfov > 0) {
4473
+ SerializeNumberProperty("yfov", camera.yfov, o);
4474
+ }
4475
+
4476
+ if (camera.extras.Type() != NULL_TYPE) {
4477
+ SerializeValue("extras", camera.extras, o);
4478
+ }
4479
+ }
4480
+
4481
+ static void SerializeGltfCamera(const Camera &camera, json &o) {
4482
+ SerializeStringProperty("type", camera.type, o);
4483
+ if (!camera.name.empty()) {
4484
+ SerializeStringProperty("name", camera.name, o);
4485
+ }
4486
+
4487
+ if (camera.type.compare("orthographic") == 0) {
4488
+ json orthographic;
4489
+ SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
4490
+ o["orthographic"] = orthographic;
4491
+ } else if (camera.type.compare("perspective") == 0) {
4492
+ json perspective;
4493
+ SerializeGltfPerspectiveCamera(camera.perspective, perspective);
4494
+ o["perspective"] = perspective;
4495
+ } else {
4496
+ // ???
4497
+ }
4498
+ }
4499
+
4500
+ static void SerializeGltfScene(Scene &scene, json &o) {
4501
+ SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
4502
+
4503
+ if (scene.name.size()) {
4504
+ SerializeStringProperty("name", scene.name, o);
4505
+ }
4506
+ if (scene.extras.Type() != NULL_TYPE) {
4507
+ SerializeValue("extras", scene.extras, o);
4508
+ }
4509
+ SerializeExtensionMap(scene.extensions, o);
4510
+ }
4511
+
4512
+ static void SerializeGltfSkin(Skin &skin, json &o) {
4513
+ if (skin.inverseBindMatrices != -1)
4514
+ SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
4515
+
4516
+ SerializeNumberArrayProperty<int>("joints", skin.joints, o);
4517
+ SerializeNumberProperty("skeleton", skin.skeleton, o);
4518
+ if (skin.name.size()) {
4519
+ SerializeStringProperty("name", skin.name, o);
4520
+ }
4521
+ }
4522
+
4523
+ static void SerializeGltfTexture(Texture &texture, json &o) {
4524
+ if (texture.sampler > -1) {
4525
+ SerializeNumberProperty("sampler", texture.sampler, o);
4526
+ }
4527
+ if (texture.source > -1) {
4528
+ SerializeNumberProperty("source", texture.source, o);
4529
+ }
4530
+ if (texture.extras.Type() != NULL_TYPE) {
4531
+ SerializeValue("extras", texture.extras, o);
4532
+ }
4533
+ SerializeExtensionMap(texture.extensions, o);
4534
+ }
4535
+
4536
+ static bool WriteGltfFile(const std::string &output,
4537
+ const std::string &content) {
4538
+ std::ofstream gltfFile(output.c_str());
4539
+ if (!gltfFile.is_open()) return false;
4540
+ gltfFile << content << std::endl;
4541
+ return true;
4542
+ }
4543
+
4544
+ static void WriteBinaryGltfFile(const std::string &output,
4545
+ const std::string &content) {
4546
+ std::ofstream gltfFile(output.c_str(), std::ios::binary);
4547
+
4548
+ const std::string header = "glTF";
4549
+ const int version = 2;
4550
+ const int padding_size = content.size() % 4;
4551
+
4552
+ // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, padding
4553
+ const int length = 12 + 8 + content.size() + padding_size;
4554
+
4555
+ gltfFile.write(header.c_str(), header.size());
4556
+ gltfFile.write(reinterpret_cast<const char *>(&version), sizeof(version));
4557
+ gltfFile.write(reinterpret_cast<const char *>(&length), sizeof(length));
4558
+
4559
+ // JSON chunk info, then JSON data
4560
+ const int model_length = content.size() + padding_size;
4561
+ const int model_format = 0x4E4F534A;
4562
+ gltfFile.write(reinterpret_cast<const char *>(&model_length), sizeof(model_length));
4563
+ gltfFile.write(reinterpret_cast<const char *>(&model_format), sizeof(model_format));
4564
+ gltfFile.write(content.c_str(), content.size());
4565
+
4566
+ // Chunk must be multiplies of 4, so pad with spaces
4567
+ if (padding_size > 0) {
4568
+ const std::string padding = std::string(padding_size, ' ');
4569
+ gltfFile.write(padding.c_str(), padding.size());
4570
+ }
4571
+ }
4572
+
4573
+ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
4574
+ bool embedImages = false,
4575
+ bool embedBuffers = false,
4576
+ bool prettyPrint = true,
4577
+ bool writeBinary = false) {
4578
+ json output;
4579
+
4580
+ // ACCESSORS
4581
+ json accessors;
4582
+ for (unsigned int i = 0; i < model->accessors.size(); ++i) {
4583
+ json accessor;
4584
+ SerializeGltfAccessor(model->accessors[i], accessor);
4585
+ accessors.push_back(accessor);
4586
+ }
4587
+ output["accessors"] = accessors;
4588
+
4589
+ // ANIMATIONS
4590
+ if (model->animations.size()) {
4591
+ json animations;
4592
+ for (unsigned int i = 0; i < model->animations.size(); ++i) {
4593
+ if (model->animations[i].channels.size()) {
4594
+ json animation;
4595
+ SerializeGltfAnimation(model->animations[i], animation);
4596
+ animations.push_back(animation);
4597
+ }
4598
+ }
4599
+ output["animations"] = animations;
4600
+ }
4601
+
4602
+ // ASSET
4603
+ json asset;
4604
+ SerializeGltfAsset(model->asset, asset);
4605
+ output["asset"] = asset;
4606
+
4607
+ std::string defaultBinFilename = GetBaseFilename(filename);
4608
+ std::string defaultBinFileExt = ".bin";
4609
+ std::string::size_type pos = defaultBinFilename.rfind('.', defaultBinFilename.length());
4610
+
4611
+ if (pos != std::string::npos) {
4612
+ defaultBinFilename = defaultBinFilename.substr(0, pos);
4613
+ }
4614
+ std::string baseDir = GetBaseDir(filename);
4615
+ if (baseDir.empty()) {
4616
+ baseDir = "./";
4617
+ }
4618
+
4619
+ // BUFFERS
4620
+ std::vector<std::string> usedUris;
4621
+ json buffers;
4622
+ for (unsigned int i = 0; i < model->buffers.size(); ++i) {
4623
+ json buffer;
4624
+ if (embedBuffers) {
4625
+ SerializeGltfBuffer(model->buffers[i], buffer);
4626
+ } else {
4627
+ std::string binSavePath;
4628
+ std::string binUri;
4629
+ if (!model->buffers[i].uri.empty()
4630
+ && !IsDataURI(model->buffers[i].uri)) {
4631
+ binUri = model->buffers[i].uri;
4632
+ }
4633
+ else {
4634
+ binUri = defaultBinFilename + defaultBinFileExt;
4635
+ bool inUse = true;
4636
+ int numUsed = 0;
4637
+ while(inUse) {
4638
+ inUse = false;
4639
+ for (const std::string& usedName : usedUris) {
4640
+ if (binUri.compare(usedName) != 0) continue;
4641
+ inUse = true;
4642
+ binUri = defaultBinFilename + std::to_string(numUsed++) + defaultBinFileExt;
4643
+ break;
4644
+ }
4645
+ }
4646
+ }
4647
+ usedUris.push_back(binUri);
4648
+ binSavePath = JoinPath(baseDir, binUri);
4649
+ if(!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
4650
+ binUri)) {
4651
+ return false;
4652
+ }
4653
+ }
4654
+ buffers.push_back(buffer);
4655
+ }
4656
+ output["buffers"] = buffers;
4657
+
4658
+ // BUFFERVIEWS
4659
+ json bufferViews;
4660
+ for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
4661
+ json bufferView;
4662
+ SerializeGltfBufferView(model->bufferViews[i], bufferView);
4663
+ bufferViews.push_back(bufferView);
4664
+ }
4665
+ output["bufferViews"] = bufferViews;
4666
+
4667
+ // Extensions used
4668
+ if (model->extensionsUsed.size()) {
4669
+ SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed,
4670
+ output);
4671
+ }
4672
+
4673
+ // Extensions required
4674
+ if (model->extensionsRequired.size()) {
4675
+ SerializeStringArrayProperty("extensionsRequired",
4676
+ model->extensionsRequired, output);
4677
+ }
4678
+
4679
+ // IMAGES
4680
+ if (model->images.size()) {
4681
+ json images;
4682
+ for (unsigned int i = 0; i < model->images.size(); ++i) {
4683
+ json image;
4684
+
4685
+ UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
4686
+ &this->WriteImageData, this->write_image_user_data_);
4687
+ SerializeGltfImage(model->images[i], image);
4688
+ images.push_back(image);
4689
+ }
4690
+ output["images"] = images;
4691
+ }
4692
+
4693
+ // MATERIALS
4694
+ if (model->materials.size()) {
4695
+ json materials;
4696
+ for (unsigned int i = 0; i < model->materials.size(); ++i) {
4697
+ json material;
4698
+ SerializeGltfMaterial(model->materials[i], material);
4699
+ materials.push_back(material);
4700
+ }
4701
+ output["materials"] = materials;
4702
+ }
4703
+
4704
+ // MESHES
4705
+ if (model->meshes.size()) {
4706
+ json meshes;
4707
+ for (unsigned int i = 0; i < model->meshes.size(); ++i) {
4708
+ json mesh;
4709
+ SerializeGltfMesh(model->meshes[i], mesh);
4710
+ meshes.push_back(mesh);
4711
+ }
4712
+ output["meshes"] = meshes;
4713
+ }
4714
+
4715
+ // NODES
4716
+ if (model->nodes.size()) {
4717
+ json nodes;
4718
+ for (unsigned int i = 0; i < model->nodes.size(); ++i) {
4719
+ json node;
4720
+ SerializeGltfNode(model->nodes[i], node);
4721
+ nodes.push_back(node);
4722
+ }
4723
+ output["nodes"] = nodes;
4724
+ }
4725
+
4726
+ // SCENE
4727
+ if (model->defaultScene > -1) {
4728
+ SerializeNumberProperty<int>("scene", model->defaultScene, output);
4729
+ }
4730
+
4731
+ // SCENES
4732
+ if (model->scenes.size()) {
4733
+ json scenes;
4734
+ for (unsigned int i = 0; i < model->scenes.size(); ++i) {
4735
+ json currentScene;
4736
+ SerializeGltfScene(model->scenes[i], currentScene);
4737
+ scenes.push_back(currentScene);
4738
+ }
4739
+ output["scenes"] = scenes;
4740
+ }
4741
+
4742
+ // SKINS
4743
+ if (model->skins.size()) {
4744
+ json skins;
4745
+ for (unsigned int i = 0; i < model->skins.size(); ++i) {
4746
+ json skin;
4747
+ SerializeGltfSkin(model->skins[i], skin);
4748
+ skins.push_back(skin);
4749
+ }
4750
+ output["skins"] = skins;
4751
+ }
4752
+
4753
+ // TEXTURES
4754
+ if (model->textures.size()) {
4755
+ json textures;
4756
+ for (unsigned int i = 0; i < model->textures.size(); ++i) {
4757
+ json texture;
4758
+ SerializeGltfTexture(model->textures[i], texture);
4759
+ textures.push_back(texture);
4760
+ }
4761
+ output["textures"] = textures;
4762
+ }
4763
+
4764
+ // SAMPLERS
4765
+ if (model->samplers.size()) {
4766
+ json samplers;
4767
+ for (unsigned int i = 0; i < model->samplers.size(); ++i) {
4768
+ json sampler;
4769
+ SerializeGltfSampler(model->samplers[i], sampler);
4770
+ samplers.push_back(sampler);
4771
+ }
4772
+ output["samplers"] = samplers;
4773
+ }
4774
+
4775
+ // CAMERAS
4776
+ if (model->cameras.size()) {
4777
+ json cameras;
4778
+ for (unsigned int i = 0; i < model->cameras.size(); ++i) {
4779
+ json camera;
4780
+ SerializeGltfCamera(model->cameras[i], camera);
4781
+ cameras.push_back(camera);
4782
+ }
4783
+ output["cameras"] = cameras;
4784
+ }
4785
+
4786
+ // EXTENSIONS
4787
+ SerializeExtensionMap(model->extensions, output);
4788
+
4789
+ // LIGHTS as KHR_lights_cmn
4790
+ if (model->lights.size()) {
4791
+ json lights;
4792
+ for (unsigned int i = 0; i < model->lights.size(); ++i) {
4793
+ json light;
4794
+ SerializeGltfLight(model->lights[i], light);
4795
+ lights.push_back(light);
4796
+ }
4797
+ json khr_lights_cmn;
4798
+ khr_lights_cmn["lights"] = lights;
4799
+ json ext_j;
4800
+
4801
+ if (output.find("extensions") != output.end()) {
4802
+ ext_j = output["extensions"];
4803
+ }
4804
+
4805
+ ext_j["KHR_lights_cmn"] = khr_lights_cmn;
4806
+
4807
+ output["extensions"] = ext_j;
4808
+ }
4809
+
4810
+ // EXTRAS
4811
+ if (model->extras.Type() != NULL_TYPE) {
4812
+ SerializeValue("extras", model->extras, output);
4813
+ }
4814
+
4815
+ if (writeBinary) {
4816
+ WriteBinaryGltfFile(filename, output.dump());
4817
+ } else {
4818
+ WriteGltfFile(filename, output.dump(prettyPrint ? 2 : 0));
4819
+ }
4820
+
4821
+ return true;
4822
+ }
4823
+
4824
+ } // namespace tinygltf
4825
+
4826
+ #ifdef __clang__
4827
+ #pragma clang diagnostic pop
4828
+ #endif
4829
+
4830
+ #endif // TINYGLTF_IMPLEMENTATION