Conscience Core
tiny_gltf.h
Go to the documentation of this file.
1 //
2 // Header-only tiny glTF 2.0 loader and serializer.
3 //
4 //
5 // The MIT License (MIT)
6 //
7 // Copyright (c) 2015 - Present 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: - v2.8.10
29 // See https://github.com/syoyo/tinygltf/releases for release history.
30 //
31 // Tiny glTF loader is using following third party libraries:
32 //
33 // - jsonhpp: C++ JSON library.
34 // - base64: base64 decode/encode library.
35 // - stb_image: Image loading library.
36 //
37 #ifndef TINY_GLTF_H_
38 #define TINY_GLTF_H_
39 
40 #include <array>
41 #include <cassert>
42 #include <cmath> // std::fabs
43 #include <cstdint>
44 #include <cstdlib>
45 #include <cstring>
46 #include <limits>
47 #include <map>
48 #include <string>
49 #include <vector>
50 using std::string;
51 // Auto-detect C++14 standard version
52 #if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && \
53  (__cplusplus >= 201402L)
54 #define TINYGLTF_USE_CPP14
55 #endif
56 
57 #ifdef __ANDROID__
58 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
59 #include <android/asset_manager.h>
60 #endif
61 #endif
62 
63 #ifdef __GNUC__
64 #if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
65 #define TINYGLTF_NOEXCEPT
66 #else
67 #define TINYGLTF_NOEXCEPT noexcept
68 #endif
69 #else
70 #define TINYGLTF_NOEXCEPT noexcept
71 #endif
72 
73 #define DEFAULT_METHODS(x) \
74  ~x() = default; \
75  x(const x &) = default; \
76  x(x &&) TINYGLTF_NOEXCEPT = default; \
77  x &operator=(const x &) = default; \
78  x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
79 
80 namespace tinygltf {
81 
82 #define TINYGLTF_MODE_POINTS (0)
83 #define TINYGLTF_MODE_LINE (1)
84 #define TINYGLTF_MODE_LINE_LOOP (2)
85 #define TINYGLTF_MODE_LINE_STRIP (3)
86 #define TINYGLTF_MODE_TRIANGLES (4)
87 #define TINYGLTF_MODE_TRIANGLE_STRIP (5)
88 #define TINYGLTF_MODE_TRIANGLE_FAN (6)
89 
90 #define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
91 #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
92 #define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
93 #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
94 #define TINYGLTF_COMPONENT_TYPE_INT (5124)
95 #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
96 #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
97 #define TINYGLTF_COMPONENT_TYPE_DOUBLE \
98  (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
99  // support double type even the schema seems allow any value of
100  // integer:
101  // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
102 
103 #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
104 #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
105 #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
106 #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
107 #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
108 #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
109 
110 #define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
111 #define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
112 #define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
113 
114 // Redeclarations of the above for technique.parameters.
115 #define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
116 #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
117 #define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
118 #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
119 #define TINYGLTF_PARAMETER_TYPE_INT (5124)
120 #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
121 #define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
122 
123 #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
124 #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
125 #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
126 
127 #define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
128 #define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
129 #define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
130 
131 #define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
132 #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
133 #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
134 #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
135 
136 #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
137 #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
138 #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
139 
140 #define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
141 
142 // End parameter types
143 
144 #define TINYGLTF_TYPE_VEC2 (2)
145 #define TINYGLTF_TYPE_VEC3 (3)
146 #define TINYGLTF_TYPE_VEC4 (4)
147 #define TINYGLTF_TYPE_MAT2 (32 + 2)
148 #define TINYGLTF_TYPE_MAT3 (32 + 3)
149 #define TINYGLTF_TYPE_MAT4 (32 + 4)
150 #define TINYGLTF_TYPE_SCALAR (64 + 1)
151 #define TINYGLTF_TYPE_VECTOR (64 + 4)
152 #define TINYGLTF_TYPE_MATRIX (64 + 16)
153 
154 #define TINYGLTF_IMAGE_FORMAT_JPEG (0)
155 #define TINYGLTF_IMAGE_FORMAT_PNG (1)
156 #define TINYGLTF_IMAGE_FORMAT_BMP (2)
157 #define TINYGLTF_IMAGE_FORMAT_GIF (3)
158 
159 #define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
160 #define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
161 #define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
162 #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
163 #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
164 
165 #define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
166 #define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
167 
168 #define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
169 #define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
170 
171 #define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
172 #define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
173 
174 #define TINYGLTF_DOUBLE_EPS (1.e-12)
175 #define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
176 
177 #ifdef __ANDROID__
178 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
179 #ifdef TINYGLTF_IMPLEMENTATION
180 AAssetManager *asset_manager = nullptr;
181 #else
182 extern AAssetManager *asset_manager;
183 #endif
184 #endif
185 #endif
186 
187 typedef enum {
196 } Type;
197 
198 typedef enum {
202 
203 static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
204  if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
205  return 1;
206  } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
207  return 1;
208  } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
209  return 2;
210  } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
211  return 2;
212  } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
213  return 4;
214  } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
215  return 4;
216  } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
217  return 4;
218  } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
219  return 8;
220  } else {
221  // Unknown component type
222  return -1;
223  }
224 }
225 
226 static inline int32_t GetNumComponentsInType(uint32_t ty) {
227  if (ty == TINYGLTF_TYPE_SCALAR) {
228  return 1;
229  } else if (ty == TINYGLTF_TYPE_VEC2) {
230  return 2;
231  } else if (ty == TINYGLTF_TYPE_VEC3) {
232  return 3;
233  } else if (ty == TINYGLTF_TYPE_VEC4) {
234  return 4;
235  } else if (ty == TINYGLTF_TYPE_MAT2) {
236  return 4;
237  } else if (ty == TINYGLTF_TYPE_MAT3) {
238  return 9;
239  } else if (ty == TINYGLTF_TYPE_MAT4) {
240  return 16;
241  } else {
242  // Unknown component type
243  return -1;
244  }
245 }
246 
247 // TODO(syoyo): Move these functions to TinyGLTF class
248 bool IsDataURI(const std::string &in);
249 bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
250  const std::string &in, size_t reqBytes, bool checkSize);
251 
252 #ifdef __clang__
253 #pragma clang diagnostic push
254 // Suppress warning for : static Value null_value
255 #pragma clang diagnostic ignored "-Wexit-time-destructors"
256 #pragma clang diagnostic ignored "-Wpadded"
257 #endif
258 
259 // Simple class to represent JSON object
260 class Value {
261  public:
262  typedef std::vector<Value> Array;
263  typedef std::map<std::string, Value> Object;
264 
265  Value() = default;
266 
267  explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
268  explicit Value(int i) : type_(INT_TYPE) {
269  int_value_ = i;
270  real_value_ = i;
271  }
272  explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
273  explicit Value(const std::string &s) : type_(STRING_TYPE) {
274  string_value_ = s;
275  }
276  explicit Value(std::string &&s)
277  : type_(STRING_TYPE), string_value_(std::move(s)) {}
278  explicit Value(const char *s) : type_(STRING_TYPE) { string_value_ = s; }
279  explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
280  binary_value_.resize(n);
281  memcpy(binary_value_.data(), p, n);
282  }
283  explicit Value(std::vector<unsigned char> &&v) noexcept
284  : type_(BINARY_TYPE),
285  binary_value_(std::move(v)) {}
286  explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
287  explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
288  array_value_(std::move(a)) {}
289 
290  explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
291  explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
292  object_value_(std::move(o)) {}
293 
295 
296  char Type() const { return static_cast<char>(type_); }
297 
298  bool IsBool() const { return (type_ == BOOL_TYPE); }
299 
300  bool IsInt() const { return (type_ == INT_TYPE); }
301 
302  bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
303 
304  bool IsReal() const { return (type_ == REAL_TYPE); }
305 
306  bool IsString() const { return (type_ == STRING_TYPE); }
307 
308  bool IsBinary() const { return (type_ == BINARY_TYPE); }
309 
310  bool IsArray() const { return (type_ == ARRAY_TYPE); }
311 
312  bool IsObject() const { return (type_ == OBJECT_TYPE); }
313 
314  // Use this function if you want to have number value as double.
315  double GetNumberAsDouble() const {
316  if (type_ == INT_TYPE) {
317  return double(int_value_);
318  } else {
319  return real_value_;
320  }
321  }
322 
323  // Use this function if you want to have number value as int.
324  // TODO(syoyo): Support int value larger than 32 bits
325  int GetNumberAsInt() const {
326  if (type_ == REAL_TYPE) {
327  return int(real_value_);
328  } else {
329  return int_value_;
330  }
331  }
332 
333  // Accessor
334  template <typename T>
335  const T &Get() const;
336  template <typename T>
337  T &Get();
338 
339  // Lookup value from an array
340  const Value &Get(size_t idx) const {
341  static Value null_value;
342  assert(IsArray());
343  return (idx < array_value_.size())
344  ? array_value_[idx]
345  : null_value;
346  }
347 
348  // Lookup value from a key-value pair
349  const Value &Get(const std::string &key) const {
350  static Value null_value;
351  assert(IsObject());
352  Object::const_iterator it = object_value_.find(key);
353  return (it != object_value_.end()) ? it->second : null_value;
354  }
355 
356  size_t ArrayLen() const {
357  if (!IsArray()) return 0;
358  return array_value_.size();
359  }
360 
361  // Valid only for object type.
362  bool Has(const std::string &key) const {
363  if (!IsObject()) return false;
364  Object::const_iterator it = object_value_.find(key);
365  return (it != object_value_.end()) ? true : false;
366  }
367 
368  // List keys
369  std::vector<std::string> Keys() const {
370  std::vector<std::string> keys;
371  if (!IsObject()) return keys; // empty
372 
373  for (Object::const_iterator it = object_value_.begin();
374  it != object_value_.end(); ++it) {
375  keys.push_back(it->first);
376  }
377 
378  return keys;
379  }
380 
381  size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
382 
383  bool operator==(const tinygltf::Value &other) const;
384 
385  protected:
387 
388  int int_value_ = 0;
389  double real_value_ = 0.0;
390  std::string string_value_;
391  std::vector<unsigned char> binary_value_;
394  bool boolean_value_ = false;
395 };
396 
397 #ifdef __clang__
398 #pragma clang diagnostic pop
399 #endif
400 
401 #define TINYGLTF_VALUE_GET(ctype, var) \
402  template <> \
403  inline const ctype &Value::Get<ctype>() const { \
404  return var; \
405  } \
406  template <> \
407  inline ctype &Value::Get<ctype>() { \
408  return var; \
409  }
410 TINYGLTF_VALUE_GET(bool, boolean_value_)
411 TINYGLTF_VALUE_GET(double, real_value_)
412 TINYGLTF_VALUE_GET(int, int_value_)
413 TINYGLTF_VALUE_GET(std::string, string_value_)
414 TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
415 TINYGLTF_VALUE_GET(Value::Array, array_value_)
416 TINYGLTF_VALUE_GET(Value::Object, object_value_)
417 #undef TINYGLTF_VALUE_GET
418 
419 #ifdef __clang__
420 #pragma clang diagnostic push
421 #pragma clang diagnostic ignored "-Wc++98-compat"
422 #pragma clang diagnostic ignored "-Wpadded"
423 #endif
424 
426 using ColorValue = std::array<double, 4>;
427 
428 // === legacy interface ====
429 // TODO(syoyo): Deprecate `Parameter` class.
430 struct Parameter {
431  bool bool_value = false;
432  bool has_number_value = false;
433  std::string string_value;
434  std::vector<double> number_array;
435  std::map<std::string, double> json_double_value;
436  double number_value = 0.0;
437 
438  // context sensitive methods. depending the type of the Parameter you are
439  // accessing, these are either valid or not
440  // If this parameter represent a texture map in a material, will return the
441  // texture index
442 
446  int TextureIndex() const {
447  const auto it = json_double_value.find("index");
448  if (it != std::end(json_double_value)) {
449  return int(it->second);
450  }
451  return -1;
452  }
453 
457  int TextureTexCoord() const {
458  const auto it = json_double_value.find("texCoord");
459  if (it != std::end(json_double_value)) {
460  return int(it->second);
461  }
462  // As per the spec, if texCoord is omitted, this parameter is 0
463  return 0;
464  }
465 
469  double TextureScale() const {
470  const auto it = json_double_value.find("scale");
471  if (it != std::end(json_double_value)) {
472  return it->second;
473  }
474  // As per the spec, if scale is omitted, this parameter is 1
475  return 1;
476  }
477 
481  double TextureStrength() const {
482  const auto it = json_double_value.find("strength");
483  if (it != std::end(json_double_value)) {
484  return it->second;
485  }
486  // As per the spec, if strength is omitted, this parameter is 1
487  return 1;
488  }
489 
493  double Factor() const { return number_value; }
494 
499  return {
500  {// this aggregate initialize the std::array object, and uses C++11 RVO.
502  (number_array.size() > 3 ? number_array[3] : 1.0)}};
503  }
504 
505  Parameter() = default;
507  bool operator==(const Parameter &) const;
508 };
509 
510 #ifdef __clang__
511 #pragma clang diagnostic pop
512 #endif
513 
514 #ifdef __clang__
515 #pragma clang diagnostic push
516 #pragma clang diagnostic ignored "-Wpadded"
517 #endif
518 
519 typedef std::map<std::string, Parameter> ParameterMap;
520 typedef std::map<std::string, Value> ExtensionMap;
521 
523  int sampler{-1}; // required
524  int target_node{-1}; // optional index of the node to target (alternative
525  // target should be provided by extension)
526  std::string target_path; // required with standard values of ["translation",
527  // "rotation", "scale", "weights"]
532 
533  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
534  std::string extras_json_string;
538 
539  AnimationChannel() = default;
541  bool operator==(const AnimationChannel &) const;
542 };
543 
545  int input{-1}; // required
546  int output{-1}; // required
547  std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
548  // string. default "LINEAR"
551 
552  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
553  std::string extras_json_string;
555 
558  bool operator==(const AnimationSampler &) const;
559 };
560 
561 struct Animation {
562  std::string name;
563  std::vector<AnimationChannel> channels;
564  std::vector<AnimationSampler> samplers;
567 
568  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
569  std::string extras_json_string;
571 
572  Animation() = default;
574  bool operator==(const Animation &) const;
575 };
576 
577 struct Skin {
578  std::string name;
579  int inverseBindMatrices{-1}; // required here but not in the spec
580  int skeleton{-1}; // The index of the node used as a skeleton root
581  std::vector<int> joints; // Indices of skeleton nodes
582 
585 
586  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
587  std::string extras_json_string;
589 
590  Skin() = default;
592  bool operator==(const Skin &) const;
593 };
594 
595 struct Sampler {
596  std::string name;
597  // glTF 2.0 spec does not define default value for `minFilter` and
598  // `magFilter`. Set -1 in TinyGLTF(issue #186)
599  int minFilter =
600  -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
601  // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
602  // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
603  int magFilter =
604  -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
605  int wrapS =
606  TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
607  // "REPEAT"], default "REPEAT"
608  int wrapT =
609  TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
610  // "REPEAT"], default "REPEAT"
611  // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
612  // not used.
613 
616 
617  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
618  std::string extras_json_string;
620 
621  Sampler() = default;
623  bool operator==(const Sampler &) const;
624 };
625 
626 struct Image {
627  std::string name;
628  int width{-1};
629  int height{-1};
630  int component{-1};
631  int bits{-1}; // bit depth per channel. 8(byte), 16 or 32.
632  int pixel_type{-1}; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
633  // UBYTE(bits = 8) or USHORT(bits = 16)
634  std::vector<unsigned char> image;
635  int bufferView{-1}; // (required if no uri)
636  std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
637  // "image/bmp", "image/gif"]
638  std::string uri; // (required if no mimeType) uri is not decoded(e.g.
639  // whitespace may be represented as %20)
642 
643  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
644  std::string extras_json_string;
646 
647  // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
648  // compressed for "image/jpeg" mime) This feature is good if you use custom
649  // image loader function. (e.g. delayed decoding of images for faster glTF
650  // parsing) Default parser for Image does not provide as-is loading feature at
651  // the moment. (You can manipulate this by providing your own LoadImageData
652  // function)
653  bool as_is{false};
654 
655  Image() = default;
657 
658  bool operator==(const Image &) const;
659 };
660 
661 struct Texture {
662  std::string name;
663 
664  int sampler{-1};
665  int source{-1};
668 
669  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
670  std::string extras_json_string;
672 
673  Texture() = default;
675 
676  bool operator==(const Texture &) const;
677 };
678 
679 struct TextureInfo {
680  int index{-1}; // required.
681  int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
682  // texture coordinate mapping.
683 
686 
687  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
688  std::string extras_json_string;
690 
691  TextureInfo() = default;
693  bool operator==(const TextureInfo &) const;
694 };
695 
697  int index{-1}; // required
698  int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
699  // texture coordinate mapping.
700  double scale{
701  1.0}; // scaledNormal = normalize((<sampled normal texture value>
702  // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
703 
706 
707  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
708  std::string extras_json_string;
710 
711  NormalTextureInfo() = default;
713  bool operator==(const NormalTextureInfo &) const;
714 };
715 
717  int index{-1}; // required
718  int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
719  // texture coordinate mapping.
720  double strength{1.0}; // occludedColor = lerp(color, color * <sampled
721  // occlusion texture value>, <occlusion strength>)
722 
725 
726  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
727  std::string extras_json_string;
729 
730  OcclusionTextureInfo() = default;
732  bool operator==(const OcclusionTextureInfo &) const;
733 };
734 
735 // pbrMetallicRoughness class defined in glTF 2.0 spec.
737  std::vector<double> baseColorFactor{1.0, 1.0, 1.0, 1.0}; // len = 4. default [1,1,1,1]
739  double metallicFactor{1.0}; // default 1
740  double roughnessFactor{1.0}; // default 1
742 
745 
746  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
747  std::string extras_json_string;
749 
750  PbrMetallicRoughness() = default;
752 
753  bool operator==(const PbrMetallicRoughness &) const;
754 };
755 
756 // Each extension should be stored in a ParameterMap.
757 // members not in the values could be included in the ParameterMap
758 // to keep a single material model
759 struct Material {
760  std::string name;
761 
762  std::vector<double> emissiveFactor{0.0, 0.0, 0.0}; // length 3. default [0, 0, 0]
763  std::string alphaMode{"OPAQUE"}; // default "OPAQUE"
764  double alphaCutoff{0.5}; // default 0.5
765  bool doubleSided{false}; // default false
766  std::vector<int> lods; // level of detail materials (MSFT_lod)
767 
769 
773 
774  // For backward compatibility
775  // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
778 
781 
782  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
783  std::string extras_json_string;
785 
786  Material() = default;
788 
789  bool operator==(const Material &) const;
790 };
791 
792 struct BufferView {
793  std::string name;
794  int buffer{-1}; // Required
795  size_t byteOffset{0}; // minimum 0, default 0
796  size_t byteLength{0}; // required, minimum 1. 0 = invalid
797  size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
798  // understood to be tightly packed
799  int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
800  // or attribs. Could be 0 for other data
803 
804  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
805  std::string extras_json_string;
807 
808  bool dracoDecoded{false}; // Flag indicating this has been draco decoded
809 
810  BufferView() = default;
812  bool operator==(const BufferView &) const;
813 };
814 
815 struct Accessor {
816  int bufferView{-1}; // optional in spec but required here since sparse
817  // accessor are not supported
818  std::string name;
819  size_t byteOffset{0};
820  bool normalized{false}; // optional.
821  int componentType{-1}; // (required) One of TINYGLTF_COMPONENT_TYPE_***
822  size_t count{0}; // required
823  int type{-1}; // (required) One of TINYGLTF_TYPE_*** ..
826 
827  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
828  std::string extras_json_string;
830 
831  std::vector<double>
832  minValues; // optional. integer value is promoted to double
833  std::vector<double>
834  maxValues; // optional. integer value is promoted to double
835 
836  struct Sparse {
837  int count;
838  bool isSparse;
839  struct {
840  size_t byteOffset;
842  int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
845  std::string extras_json_string;
847  } indices;
848  struct {
849  int bufferView;
850  size_t byteOffset;
851  Value extras;
853  std::string extras_json_string;
854  std::string extensions_json_string;
855  } values;
856  Value extras;
858  std::string extras_json_string;
859  std::string extensions_json_string;
860  };
861 
863 
868  int ByteStride(const BufferView &bufferViewObject) const {
869  if (bufferViewObject.byteStride == 0) {
870  // Assume data is tightly packed.
871  int componentSizeInBytes =
872  GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
873  if (componentSizeInBytes <= 0) {
874  return -1;
875  }
876 
877  int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
878  if (numComponents <= 0) {
879  return -1;
880  }
881 
882  return componentSizeInBytes * numComponents;
883  } else {
884  // Check if byteStride is a multiple of the size of the accessor's
885  // component type.
886  int componentSizeInBytes =
887  GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
888  if (componentSizeInBytes <= 0) {
889  return -1;
890  }
891 
892  if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
893  return -1;
894  }
895  return static_cast<int>(bufferViewObject.byteStride);
896  }
897 
898  // unreachable return 0;
899  }
900 
902 
903  {
904  sparse.isSparse = false;
905  }
907  bool operator==(const tinygltf::Accessor &) const;
908 };
909 
911  double aspectRatio{0.0}; // min > 0
912  double yfov{0.0}; // required. min > 0
913  double zfar{0.0}; // min > 0
914  double znear{0.0}; // required. min > 0
915 
916  PerspectiveCamera() = default;
918  bool operator==(const PerspectiveCamera &) const;
919 
922 
923  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
924  std::string extras_json_string;
926 };
927 
929  double xmag{0.0}; // required. must not be zero.
930  double ymag{0.0}; // required. must not be zero.
931  double zfar{0.0}; // required. `zfar` must be greater than `znear`.
932  double znear{0.0}; // required
933 
934  OrthographicCamera() = default;
936  bool operator==(const OrthographicCamera &) const;
937 
940 
941  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
942  std::string extras_json_string;
944 };
945 
946 struct Camera {
947  std::string type; // required. "perspective" or "orthographic"
948  std::string name;
949 
952 
953  Camera() = default;
955  bool operator==(const Camera &) const;
956 
959 
960  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
961  std::string extras_json_string;
963 };
964 
965 struct Primitive {
966  std::map<std::string, int> attributes; // (required) A dictionary object of
967  // integer, where each integer
968  // is the index of the accessor
969  // containing an attribute.
970  int material{-1}; // The index of the material to apply to this primitive
971  // when rendering.
972  int indices{-1}; // The index of the accessor that contains the indices.
973  int mode{-1}; // one of TINYGLTF_MODE_***
974  std::vector<std::map<std::string, int> > targets; // array of morph targets,
975  // where each target is a dict with attributes in ["POSITION, "NORMAL",
976  // "TANGENT"] pointing
977  // to their corresponding accessors
980 
981  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
982  std::string extras_json_string;
984 
985  Primitive() = default;
987  bool operator==(const Primitive &) const;
988 };
989 
990 struct Mesh {
991  std::string name;
992  std::vector<Primitive> primitives;
993  std::vector<double> weights; // weights to be applied to the Morph Targets
996 
997  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
998  std::string extras_json_string;
1000 
1001  Mesh() = default;
1003  bool operator==(const Mesh &) const;
1004 };
1005 
1006 class Node {
1007  public:
1008  Node() = default;
1009 
1011 
1012  bool operator==(const Node &) const;
1013 
1014  int camera{-1}; // the index of the camera referenced by this node
1015 
1016  std::string name;
1017  int skin{-1};
1018  int mesh{-1};
1019  int light{-1}; // light source index (KHR_lights_punctual)
1020  int emitter{-1}; // audio emitter index (KHR_audio)
1021  std::vector<int> lods; // level of detail nodes (MSFT_lod)
1022  std::vector<int> children;
1023  std::vector<double> rotation; // length must be 0 or 4
1024  std::vector<double> scale; // length must be 0 or 3
1025  std::vector<double> translation; // length must be 0 or 3
1026  std::vector<double> matrix; // length must be 0 or 16
1027  std::vector<double> weights; // The weights of the instantiated Morph Target
1028 
1031 
1032  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1033  std::string extras_json_string;
1035 };
1036 
1037 struct Buffer {
1038  std::string name;
1039  std::vector<unsigned char> data;
1040  std::string
1041  uri; // considered as required here but not in the spec (need to clarify)
1042  // uri is not decoded(e.g. whitespace may be represented as %20)
1045 
1046  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1047  std::string extras_json_string;
1049 
1050  Buffer() = default;
1052  bool operator==(const Buffer &) const;
1053 };
1054 
1055 struct Asset {
1056  std::string version = "2.0"; // required
1057  std::string generator;
1058  std::string minVersion;
1059  std::string copyright;
1062 
1063  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1064  std::string extras_json_string;
1066 
1067  Asset() = default;
1069  bool operator==(const Asset &) const;
1070 };
1071 
1072 struct Scene {
1073  std::string name;
1074  std::vector<int> nodes;
1075  std::vector<int> audioEmitters; // KHR_audio global emitters
1076 
1079 
1080  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1081  std::string extras_json_string;
1083 
1084  Scene() = default;
1086  bool operator==(const Scene &) const;
1087 };
1088 
1089 struct SpotLight {
1090  double innerConeAngle{0.0};
1091  double outerConeAngle{0.7853981634};
1092 
1093  SpotLight() = default;
1095  bool operator==(const SpotLight &) const;
1096 
1099 
1100  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1101  std::string extras_json_string;
1103 };
1104 
1105 struct Light {
1106  std::string name;
1107  std::vector<double> color;
1108  double intensity{1.0};
1109  std::string type;
1110  double range{0.0}; // 0.0 = infinite
1112 
1113  Light() = default;
1115 
1116  bool operator==(const Light &) const;
1117 
1120 
1121  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1122  std::string extras_json_string;
1124 };
1125 
1127  double coneInnerAngle{6.283185307179586};
1128  double coneOuterAngle{6.283185307179586};
1129  double coneOuterGain{0.0};
1130  double maxDistance{100.0};
1131  double refDistance{1.0};
1132  double rolloffFactor{1.0};
1133 
1134  PositionalEmitter() = default;
1136  bool operator==(const PositionalEmitter &) const;
1137 
1140 
1141  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1142  std::string extras_json_string;
1144 };
1145 
1147  std::string name;
1148  double gain{1.0};
1149  bool loop{false};
1150  bool playing{false};
1151  std::string
1152  type; // positional - Positional audio emitters. Using sound cones, the
1153  // orientation is +Z having the same front side for a glTF asset.
1154  // global - Global audio emitters are not affected by the position
1155  // of audio listeners. coneInnerAngle, coneOuterAngle,
1156  // coneOuterGain, distanceModel, maxDistance, refDistance, and
1157  // rolloffFactor should all be ignored when set.
1158  std::string
1159  distanceModel; // linear - A linear distance model calculating the
1160  // gain induced by the distance according to: 1.0
1161  // - rolloffFactor * (distance - refDistance) /
1162  // (maxDistance - refDistance)
1163  // inverse - (default) An inverse distance model
1164  // calculating the gain induced by the distance according
1165  // to: refDistance / (refDistance + rolloffFactor *
1166  // (Math.max(distance, refDistance) - refDistance))
1167  // exponential - An exponential distance model calculating
1168  // the gain induced by the distance according to:
1169  // pow((Math.max(distance, refDistance) / refDistance,
1170  // -rolloffFactor))
1172  int source{-1};
1173 
1174  AudioEmitter() : type("global"), distanceModel("inverse") {}
1176 
1177  bool operator==(const AudioEmitter &) const;
1178 
1181 
1182  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1183  std::string extras_json_string;
1185 };
1186 
1187 struct AudioSource {
1188  std::string name;
1189  std::string uri;
1190  int bufferView{-1}; // (required if no uri)
1191  std::string
1192  mimeType; // (required if no uri) The audio's MIME type. Required if
1193  // bufferView is defined. Unless specified by another
1194  // extension, the only supported mimeType is audio/mpeg.
1195 
1196  AudioSource() = default;
1198 
1199  bool operator==(const AudioSource &) const;
1200 
1203 
1204  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1205  std::string extras_json_string;
1207 };
1208 
1209 class Model {
1210  public:
1211  Model() = default;
1213 
1214  bool operator==(const Model &) const;
1215 
1216  std::vector<Accessor> accessors;
1217  std::vector<Animation> animations;
1218  std::vector<Buffer> buffers;
1219  std::vector<BufferView> bufferViews;
1220  std::vector<Material> materials;
1221  std::vector<Mesh> meshes;
1222  std::vector<Node> nodes;
1223  std::vector<Texture> textures;
1224  std::vector<Image> images;
1225  std::vector<Skin> skins;
1226  std::vector<Sampler> samplers;
1227  std::vector<Camera> cameras;
1228  std::vector<Scene> scenes;
1229  std::vector<Light> lights;
1230  std::vector<AudioEmitter> audioEmitters;
1231  std::vector<AudioSource> audioSources;
1232 
1233  int defaultScene{-1};
1234  std::vector<std::string> extensionsUsed;
1235  std::vector<std::string> extensionsRequired;
1236 
1238 
1241 
1242  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1243  std::string extras_json_string;
1245 };
1246 
1248  NO_REQUIRE = 0x00,
1257 };
1258 
1266 typedef bool (*URIEncodeFunction)(const std::string &in_uri,
1267  const std::string &object_type,
1268  std::string *out_uri, void *user_data);
1269 
1275 typedef bool (*URIDecodeFunction)(const std::string &in_uri,
1276  std::string *out_uri, void *user_data);
1277 
1278 // Declaration of default uri decode function
1279 bool URIDecode(const std::string &in_uri, std::string *out_uri,
1280  void *user_data);
1281 
1286  URIEncodeFunction encode; // Optional encode method
1287  URIDecodeFunction decode; // Required decode method
1288 
1289  void *user_data; // An argument that is passed to all uri callbacks
1290 };
1291 
1295 typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1296  std::string *, int, int,
1297  const unsigned char *, int,
1298  void *user_pointer);
1299 
1305 typedef bool (*WriteImageDataFunction)(const std::string *basepath,
1306  const std::string *filename,
1307  const Image *image, bool embedImages,
1308  const URICallbacks *uri_cb,
1309  std::string *out_uri,
1310  void *user_pointer);
1311 
1312 #ifndef TINYGLTF_NO_STB_IMAGE
1313 // Declaration of default image loader callback
1314 bool LoadImageData(Image *image, const int image_idx, std::string *err,
1315  std::string *warn, int req_width, int req_height,
1316  const unsigned char *bytes, int size, void *);
1317 #endif
1318 
1319 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1320 // Declaration of default image writer callback
1321 bool WriteImageData(const std::string *basepath, const std::string *filename,
1322  const Image *image, bool embedImages,
1323  const URICallbacks *uri_cb, std::string *out_uri, void *);
1324 #endif
1325 
1329 typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
1330 
1334 typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
1335 
1339 typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
1340  std::string *, const std::string &,
1341  void *);
1342 
1346 typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
1347  const std::vector<unsigned char> &,
1348  void *);
1349 
1353 typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err,
1354  const std::string &abs_filename,
1355  void *userdata);
1356 
1361 struct FsCallbacks {
1366  GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API,
1367  // add `InBytes` suffix.
1368 
1369  void *user_data; // An argument that is passed to all fs callbacks
1370 };
1371 
1372 #ifndef TINYGLTF_NO_FS
1373 // Declaration of default filesystem callbacks
1374 
1375 bool FileExists(const std::string &abs_filename, void *);
1376 
1384 std::string ExpandFilePath(const std::string &filepath, void *userdata);
1385 
1386 bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1387  const std::string &filepath, void *);
1388 
1389 bool WriteWholeFile(std::string *err, const std::string &filepath,
1390  const std::vector<unsigned char> &contents, void *);
1391 
1392 bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
1393  const std::string &filepath, void *);
1394 #endif
1395 
1399 class TinyGLTF {
1400  public:
1401 #ifdef __clang__
1402 #pragma clang diagnostic push
1403 #pragma clang diagnostic ignored "-Wc++98-compat"
1404 #endif
1405 
1406 
1407 #ifdef __clang__
1408 #pragma clang diagnostic pop
1409 #endif
1410 
1411  TinyGLTF();
1412  ~TinyGLTF() = default;
1413 
1419  bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
1420  const std::string &filename,
1421  unsigned int check_sections = REQUIRE_VERSION);
1422 
1431  bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1432  const char *str, const unsigned int length,
1433  const std::string &base_dir,
1434  unsigned int check_sections = REQUIRE_VERSION);
1435 
1441  bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
1442  const std::string &filename,
1443  unsigned int check_sections = REQUIRE_VERSION);
1444 
1453  bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
1454  const unsigned char *bytes,
1455  const unsigned int length,
1456  const std::string &base_dir = "",
1457  unsigned int check_sections = REQUIRE_VERSION);
1458 
1462  bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
1463  bool prettyPrint, bool writeBinary);
1464 
1468  bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
1469  bool embedImages, bool embedBuffers,
1470  bool prettyPrint, bool writeBinary);
1471 
1475  void SetParseStrictness(ParseStrictness strictness);
1476 
1480  void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1481 
1485  void RemoveImageLoader();
1486 
1490  void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1491 
1495  void SetURICallbacks(URICallbacks callbacks);
1496 
1500  void SetFsCallbacks(FsCallbacks callbacks);
1501 
1511  void SetSerializeDefaultValues(const bool enabled) {
1512  serialize_default_values_ = enabled;
1513  }
1514 
1515  bool GetSerializeDefaultValues() const { return serialize_default_values_; }
1516 
1523  store_original_json_for_extras_and_extensions_ = enabled;
1524  }
1525 
1527  return store_original_json_for_extras_and_extensions_;
1528  }
1529 
1534  void SetPreserveImageChannels(bool onoff) {
1535  preserve_image_channels_ = onoff;
1536  }
1537 
1543  void SetMaxExternalFileSize(size_t max_bytes) {
1544  max_external_file_size_ = max_bytes;
1545  }
1546 
1547  size_t GetMaxExternalFileSize() const { return max_external_file_size_; }
1548 
1549  bool GetPreserveImageChannels() const { return preserve_image_channels_; }
1550 
1551  private:
1558  bool LoadFromString(Model *model, std::string *err, std::string *warn,
1559  const char *str, const unsigned int length,
1560  const std::string &base_dir, unsigned int check_sections);
1561 
1562  const unsigned char *bin_data_ = nullptr;
1563  size_t bin_size_ = 0;
1564  bool is_binary_ = false;
1565 
1567 
1568  bool serialize_default_values_ = false;
1569 
1570  bool store_original_json_for_extras_and_extensions_ = false;
1571 
1572  bool preserve_image_channels_ = false;
1573 
1575  size_t max_external_file_size_{
1576  size_t((std::numeric_limits<int32_t>::max)())}; // Default 2GB
1577 
1578  // Warning & error messages
1579  std::string warn_;
1580  std::string err_;
1581 
1582  FsCallbacks fs;
1583 
1584  URICallbacks uri_cb = {
1585  // Use paths as-is by default. This will use JSON string escaping.
1586  nullptr,
1587  // Decode all URIs before using them as paths as the application may have
1588  // percent encoded them.
1590  // URI callback user data
1591  nullptr};
1592 
1594 #ifndef TINYGLTF_NO_STB_IMAGE
1596 #else
1597  nullptr;
1598 #endif
1599  void *load_image_user_data_{nullptr};
1600  bool user_image_loader_{false};
1601 
1603 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1605 #else
1606  nullptr;
1607 #endif
1608  void *write_image_user_data_{nullptr};
1609 };
1610 
1611 #ifdef __clang__
1612 #pragma clang diagnostic pop // -Wpadded
1613 #endif
1614 
1615 } // namespace tinygltf
1616 
1617 #endif // TINY_GLTF_H_
1618 
1619 #if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
1620 #include <algorithm>
1621 // #include <cassert>
1622 #ifndef TINYGLTF_NO_FS
1623 #include <sys/stat.h> // for is_directory check
1624 
1625 #include <cstdio>
1626 #include <fstream>
1627 #endif
1628 #include <sstream>
1629 
1630 #ifdef __clang__
1631 // Disable some warnings for external files.
1632 #pragma clang diagnostic push
1633 #pragma clang diagnostic ignored "-Wfloat-equal"
1634 #pragma clang diagnostic ignored "-Wexit-time-destructors"
1635 #pragma clang diagnostic ignored "-Wconversion"
1636 #pragma clang diagnostic ignored "-Wold-style-cast"
1637 #pragma clang diagnostic ignored "-Wglobal-constructors"
1638 #if __has_warning("-Wreserved-id-macro")
1639 #pragma clang diagnostic ignored "-Wreserved-id-macro"
1640 #endif
1641 #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1642 #pragma clang diagnostic ignored "-Wpadded"
1643 #pragma clang diagnostic ignored "-Wc++98-compat"
1644 #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1645 #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1646 #pragma clang diagnostic ignored "-Wswitch-enum"
1647 #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
1648 #pragma clang diagnostic ignored "-Wweak-vtables"
1649 #pragma clang diagnostic ignored "-Wcovered-switch-default"
1650 #if __has_warning("-Wdouble-promotion")
1651 #pragma clang diagnostic ignored "-Wdouble-promotion"
1652 #endif
1653 #if __has_warning("-Wcomma")
1654 #pragma clang diagnostic ignored "-Wcomma"
1655 #endif
1656 #if __has_warning("-Wzero-as-null-pointer-constant")
1657 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1658 #endif
1659 #if __has_warning("-Wcast-qual")
1660 #pragma clang diagnostic ignored "-Wcast-qual"
1661 #endif
1662 #if __has_warning("-Wmissing-variable-declarations")
1663 #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1664 #endif
1665 #if __has_warning("-Wmissing-prototypes")
1666 #pragma clang diagnostic ignored "-Wmissing-prototypes"
1667 #endif
1668 #if __has_warning("-Wcast-align")
1669 #pragma clang diagnostic ignored "-Wcast-align"
1670 #endif
1671 #if __has_warning("-Wnewline-eof")
1672 #pragma clang diagnostic ignored "-Wnewline-eof"
1673 #endif
1674 #if __has_warning("-Wunused-parameter")
1675 #pragma clang diagnostic ignored "-Wunused-parameter"
1676 #endif
1677 #if __has_warning("-Wmismatched-tags")
1678 #pragma clang diagnostic ignored "-Wmismatched-tags"
1679 #endif
1680 #if __has_warning("-Wextra-semi-stmt")
1681 #pragma clang diagnostic ignored "-Wextra-semi-stmt"
1682 #endif
1683 #endif
1684 
1685 // Disable GCC warnings
1686 #ifdef __GNUC__
1687 #pragma GCC diagnostic push
1688 #pragma GCC diagnostic ignored "-Wtype-limits"
1689 #endif // __GNUC__
1690 
1691 #ifndef TINYGLTF_NO_INCLUDE_JSON
1692 #ifndef TINYGLTF_USE_RAPIDJSON
1693 #include "json.hpp"
1694 #else
1695 #ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
1696 #include "document.h"
1697 #include "prettywriter.h"
1698 #include "rapidjson.h"
1699 #include "stringbuffer.h"
1700 #include "writer.h"
1701 #endif
1702 #endif
1703 #endif
1704 
1705 #ifdef TINYGLTF_ENABLE_DRACO
1706 #include "draco/compression/decode.h"
1707 #include "draco/core/decoder_buffer.h"
1708 #endif
1709 
1710 #ifndef TINYGLTF_NO_STB_IMAGE
1711 #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
1712 #include "stb_image.h"
1713 #endif
1714 #endif
1715 
1716 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1717 #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
1718 #include "stb_image_write.h"
1719 #endif
1720 #endif
1721 
1722 #ifdef __clang__
1723 #pragma clang diagnostic pop
1724 #endif
1725 
1726 #ifdef __GNUC__
1727 #pragma GCC diagnostic pop
1728 #endif
1729 
1730 #ifdef _WIN32
1731 
1732 // issue 143.
1733 // Define NOMINMAX to avoid min/max defines,
1734 // but undef it after included Windows.h
1735 #ifndef NOMINMAX
1736 #define TINYGLTF_INTERNAL_NOMINMAX
1737 #define NOMINMAX
1738 #endif
1739 
1740 #ifndef WIN32_LEAN_AND_MEAN
1741 #define WIN32_LEAN_AND_MEAN
1742 #define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1743 #endif
1744 #ifndef __MINGW32__
1745 #include <Windows.h> // include API for expanding a file path
1746 #else
1747 #include <windows.h>
1748 #endif
1749 
1750 #ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1751 #undef WIN32_LEAN_AND_MEAN
1752 #endif
1753 
1754 #if defined(TINYGLTF_INTERNAL_NOMINMAX)
1755 #undef NOMINMAX
1756 #endif
1757 
1758 #if defined(__GLIBCXX__) // mingw
1759 
1760 #include <fcntl.h> // _O_RDONLY
1761 
1762 #include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
1763 
1764 #endif
1765 
1766 #elif !defined(__ANDROID__) && !defined(__OpenBSD__)
1767 // #include <wordexp.h>
1768 #endif
1769 
1770 #if defined(__sparcv9) || defined(__powerpc__)
1771 // Big endian
1772 #else
1773 #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1774 #define TINYGLTF_LITTLE_ENDIAN 1
1775 #endif
1776 #endif
1777 
1778 namespace tinygltf {
1779 
1781  fs = {
1782 #ifndef TINYGLTF_NO_FS
1788 
1789  nullptr // Fs callback user data
1790 #else
1791  nullptr, nullptr, nullptr, nullptr, nullptr,
1792 
1793  nullptr // Fs callback user data
1794 #endif
1795  };
1796 }
1797 
1798 namespace detail {
1799 #ifdef TINYGLTF_USE_RAPIDJSON
1800 
1801 #ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1802 // This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1803 // documents may be active at once.
1804 using json =
1805  rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1806 using json_iterator = json::MemberIterator;
1807 using json_const_iterator = json::ConstMemberIterator;
1808 using json_const_array_iterator = json const *;
1809 using JsonDocument =
1810  rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1811 rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1812 rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
1813 #else
1814 // This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1815 // not thread safe. Only a single JsonDocument may be active at any one time,
1816 // meaning only a single gltf load/save can be active any one time.
1817 using json = rapidjson::Value;
1818 using json_iterator = json::MemberIterator;
1819 using json_const_iterator = json::ConstMemberIterator;
1820 using json_const_array_iterator = json const *;
1821 rapidjson::Document *s_pActiveDocument = nullptr;
1822 rapidjson::Document::AllocatorType &GetAllocator() {
1823  assert(s_pActiveDocument); // Root json node must be JsonDocument type
1824  return s_pActiveDocument->GetAllocator();
1825 }
1826 
1827 #ifdef __clang__
1828 #pragma clang diagnostic push
1829 // Suppress JsonDocument(JsonDocument &&rhs) noexcept
1830 #pragma clang diagnostic ignored "-Wunused-member-function"
1831 #endif
1832 
1833 struct JsonDocument : public rapidjson::Document {
1834  JsonDocument() {
1835  assert(s_pActiveDocument ==
1836  nullptr); // When using default allocator, only one document can be
1837  // active at a time, if you need multiple active at once,
1838  // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1839  s_pActiveDocument = this;
1840  }
1841  JsonDocument(const JsonDocument &) = delete;
1842  JsonDocument(JsonDocument &&rhs) noexcept
1843  : rapidjson::Document(std::move(rhs)) {
1844  s_pActiveDocument = this;
1845  rhs.isNil = true;
1846  }
1847  ~JsonDocument() {
1848  if (!isNil) {
1849  s_pActiveDocument = nullptr;
1850  }
1851  }
1852 
1853  private:
1854  bool isNil = false;
1855 };
1856 
1857 #ifdef __clang__
1858 #pragma clang diagnostic pop
1859 #endif
1860 
1861 #endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1862 
1863 #else
1864 using nlohmann::json;
1865 using json_iterator = json::iterator;
1866 using json_const_iterator = json::const_iterator;
1867 using json_const_array_iterator = json_const_iterator;
1868 using JsonDocument = json;
1869 #endif
1870 
1871 void JsonParse(JsonDocument &doc, const char *str, size_t length,
1872  bool throwExc = false) {
1873 #ifdef TINYGLTF_USE_RAPIDJSON
1874  (void)throwExc;
1875  doc.Parse(str, length);
1876 #else
1877  doc = detail::json::parse(str, str + length, nullptr, throwExc);
1878 #endif
1879 }
1880 } // namespace detail
1881 } // namespace tinygltf
1882 
1883 #ifdef __APPLE__
1884 #include "TargetConditionals.h"
1885 #endif
1886 
1887 #ifdef __clang__
1888 #pragma clang diagnostic push
1889 #pragma clang diagnostic ignored "-Wc++98-compat"
1890 #endif
1891 
1892 namespace tinygltf {
1893 
1900 struct LoadImageDataOption {
1901  // true: preserve image channels(e.g. load as RGB image if the image has RGB
1902  // channels) default `false`(channels are expanded to RGBA for backward
1903  // compatibility).
1904  bool preserve_channels{false};
1905 };
1906 
1907 // Equals function for Value, for recursivity
1908 static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1909  if (one.Type() != other.Type()) return false;
1910 
1911  switch (one.Type()) {
1912  case NULL_TYPE:
1913  return true;
1914  case BOOL_TYPE:
1915  return one.Get<bool>() == other.Get<bool>();
1916  case REAL_TYPE:
1917  return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1918  case INT_TYPE:
1919  return one.Get<int>() == other.Get<int>();
1920  case OBJECT_TYPE: {
1921  auto oneObj = one.Get<tinygltf::Value::Object>();
1922  auto otherObj = other.Get<tinygltf::Value::Object>();
1923  if (oneObj.size() != otherObj.size()) return false;
1924  for (auto &it : oneObj) {
1925  auto otherIt = otherObj.find(it.first);
1926  if (otherIt == otherObj.end()) return false;
1927 
1928  if (!Equals(it.second, otherIt->second)) return false;
1929  }
1930  return true;
1931  }
1932  case ARRAY_TYPE: {
1933  if (one.Size() != other.Size()) return false;
1934  for (size_t i = 0; i < one.Size(); ++i)
1935  if (!Equals(one.Get(i), other.Get(i))) return false;
1936  return true;
1937  }
1938  case STRING_TYPE:
1939  return one.Get<std::string>() == other.Get<std::string>();
1940  case BINARY_TYPE:
1941  return one.Get<std::vector<unsigned char> >() ==
1942  other.Get<std::vector<unsigned char> >();
1943  default: {
1944  // unhandled type
1945  return false;
1946  }
1947  }
1948 }
1949 
1950 // Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
1951 static bool Equals(const std::vector<double> &one,
1952  const std::vector<double> &other) {
1953  if (one.size() != other.size()) return false;
1954  for (int i = 0; i < int(one.size()); ++i) {
1955  if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1956  }
1957  return true;
1958 }
1959 
1960 bool Accessor::operator==(const Accessor &other) const {
1961  return this->bufferView == other.bufferView &&
1962  this->byteOffset == other.byteOffset &&
1963  this->componentType == other.componentType &&
1964  this->count == other.count && this->extensions == other.extensions &&
1965  this->extras == other.extras &&
1966  Equals(this->maxValues, other.maxValues) &&
1967  Equals(this->minValues, other.minValues) && this->name == other.name &&
1968  this->normalized == other.normalized && this->type == other.type;
1969 }
1970 bool Animation::operator==(const Animation &other) const {
1971  return this->channels == other.channels &&
1972  this->extensions == other.extensions && this->extras == other.extras &&
1973  this->name == other.name && this->samplers == other.samplers;
1974 }
1975 bool AnimationChannel::operator==(const AnimationChannel &other) const {
1976  return this->extensions == other.extensions && this->extras == other.extras &&
1977  this->target_node == other.target_node &&
1978  this->target_path == other.target_path &&
1979  this->sampler == other.sampler;
1980 }
1981 bool AnimationSampler::operator==(const AnimationSampler &other) const {
1982  return this->extras == other.extras && this->extensions == other.extensions &&
1983  this->input == other.input &&
1984  this->interpolation == other.interpolation &&
1985  this->output == other.output;
1986 }
1987 bool Asset::operator==(const Asset &other) const {
1988  return this->copyright == other.copyright &&
1989  this->extensions == other.extensions && this->extras == other.extras &&
1990  this->generator == other.generator &&
1991  this->minVersion == other.minVersion && this->version == other.version;
1992 }
1993 bool Buffer::operator==(const Buffer &other) const {
1994  return this->data == other.data && this->extensions == other.extensions &&
1995  this->extras == other.extras && this->name == other.name &&
1996  this->uri == other.uri;
1997 }
1998 bool BufferView::operator==(const BufferView &other) const {
1999  return this->buffer == other.buffer && this->byteLength == other.byteLength &&
2000  this->byteOffset == other.byteOffset &&
2001  this->byteStride == other.byteStride && this->name == other.name &&
2002  this->target == other.target && this->extensions == other.extensions &&
2003  this->extras == other.extras &&
2004  this->dracoDecoded == other.dracoDecoded;
2005 }
2006 bool Camera::operator==(const Camera &other) const {
2007  return this->name == other.name && this->extensions == other.extensions &&
2008  this->extras == other.extras &&
2009  this->orthographic == other.orthographic &&
2010  this->perspective == other.perspective && this->type == other.type;
2011 }
2012 bool Image::operator==(const Image &other) const {
2013  return this->bufferView == other.bufferView &&
2014  this->component == other.component &&
2015  this->extensions == other.extensions && this->extras == other.extras &&
2016  this->height == other.height && this->image == other.image &&
2017  this->mimeType == other.mimeType && this->name == other.name &&
2018  this->uri == other.uri && this->width == other.width;
2019 }
2020 bool Light::operator==(const Light &other) const {
2021  return Equals(this->color, other.color) && this->name == other.name &&
2022  this->type == other.type;
2023 }
2024 bool AudioEmitter::operator==(const AudioEmitter &other) const {
2025  return this->name == other.name &&
2026  TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) &&
2027  this->loop == other.loop && this->playing == other.playing &&
2028  this->type == other.type &&
2029  this->distanceModel == other.distanceModel &&
2030  this->source == other.source;
2031 }
2032 bool AudioSource::operator==(const AudioSource &other) const {
2033  return this->name == other.name && this->uri == other.uri;
2034 }
2035 bool Material::operator==(const Material &other) const {
2036  return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
2037  (this->normalTexture == other.normalTexture) &&
2038  (this->occlusionTexture == other.occlusionTexture) &&
2039  (this->emissiveTexture == other.emissiveTexture) &&
2040  Equals(this->emissiveFactor, other.emissiveFactor) &&
2041  (this->alphaMode == other.alphaMode) &&
2042  TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
2043  (this->doubleSided == other.doubleSided) &&
2044  (this->extensions == other.extensions) &&
2045  (this->extras == other.extras) && (this->values == other.values) &&
2046  (this->additionalValues == other.additionalValues) &&
2047  (this->name == other.name);
2048 }
2049 bool Mesh::operator==(const Mesh &other) const {
2050  return this->extensions == other.extensions && this->extras == other.extras &&
2051  this->name == other.name && Equals(this->weights, other.weights) &&
2052  this->primitives == other.primitives;
2053 }
2054 bool Model::operator==(const Model &other) const {
2055  return this->accessors == other.accessors &&
2056  this->animations == other.animations && this->asset == other.asset &&
2057  this->buffers == other.buffers &&
2058  this->bufferViews == other.bufferViews &&
2059  this->cameras == other.cameras &&
2060  this->defaultScene == other.defaultScene &&
2061  this->extensions == other.extensions &&
2062  this->extensionsRequired == other.extensionsRequired &&
2063  this->extensionsUsed == other.extensionsUsed &&
2064  this->extras == other.extras && this->images == other.images &&
2065  this->lights == other.lights && this->materials == other.materials &&
2066  this->meshes == other.meshes && this->nodes == other.nodes &&
2067  this->samplers == other.samplers && this->scenes == other.scenes &&
2068  this->skins == other.skins && this->textures == other.textures;
2069 }
2070 bool Node::operator==(const Node &other) const {
2071  return this->camera == other.camera && this->children == other.children &&
2072  this->extensions == other.extensions && this->extras == other.extras &&
2073  Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
2074  (this->light == other.light) && (this->emitter == other.emitter) &&
2075  this->name == other.name && Equals(this->rotation, other.rotation) &&
2076  Equals(this->scale, other.scale) && this->skin == other.skin &&
2077  Equals(this->translation, other.translation) &&
2078  Equals(this->weights, other.weights);
2079 }
2080 bool SpotLight::operator==(const SpotLight &other) const {
2081  return this->extensions == other.extensions && this->extras == other.extras &&
2082  TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
2083  TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
2084 }
2085 bool PositionalEmitter::operator==(const PositionalEmitter &other) const {
2086  return this->extensions == other.extensions && this->extras == other.extras &&
2087  TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) &&
2088  TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) &&
2089  TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) &&
2090  TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) &&
2091  TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) &&
2092  TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor);
2093 }
2094 bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
2095  return this->extensions == other.extensions && this->extras == other.extras &&
2096  TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
2097  TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
2098  TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2099  TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
2100 }
2101 bool Parameter::operator==(const Parameter &other) const {
2102  if (this->bool_value != other.bool_value ||
2103  this->has_number_value != other.has_number_value)
2104  return false;
2105 
2106  if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
2107  return false;
2108 
2109  if (this->json_double_value.size() != other.json_double_value.size())
2110  return false;
2111  for (auto &it : this->json_double_value) {
2112  auto otherIt = other.json_double_value.find(it.first);
2113  if (otherIt == other.json_double_value.end()) return false;
2114 
2115  if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
2116  }
2117 
2118  if (!Equals(this->number_array, other.number_array)) return false;
2119 
2120  if (this->string_value != other.string_value) return false;
2121 
2122  return true;
2123 }
2124 bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
2125  return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
2126  this->extensions == other.extensions && this->extras == other.extras &&
2127  TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
2128  TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2129  TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
2130 }
2131 bool Primitive::operator==(const Primitive &other) const {
2132  return this->attributes == other.attributes && this->extras == other.extras &&
2133  this->indices == other.indices && this->material == other.material &&
2134  this->mode == other.mode && this->targets == other.targets;
2135 }
2136 bool Sampler::operator==(const Sampler &other) const {
2137  return this->extensions == other.extensions && this->extras == other.extras &&
2138  this->magFilter == other.magFilter &&
2139  this->minFilter == other.minFilter && this->name == other.name &&
2140  this->wrapS == other.wrapS && this->wrapT == other.wrapT;
2141 
2142  // this->wrapR == other.wrapR
2143 }
2144 bool Scene::operator==(const Scene &other) const {
2145  return this->extensions == other.extensions && this->extras == other.extras &&
2146  this->name == other.name && this->nodes == other.nodes;
2147 }
2148 bool Skin::operator==(const Skin &other) const {
2149  return this->extensions == other.extensions && this->extras == other.extras &&
2150  this->inverseBindMatrices == other.inverseBindMatrices &&
2151  this->joints == other.joints && this->name == other.name &&
2152  this->skeleton == other.skeleton;
2153 }
2154 bool Texture::operator==(const Texture &other) const {
2155  return this->extensions == other.extensions && this->extras == other.extras &&
2156  this->name == other.name && this->sampler == other.sampler &&
2157  this->source == other.source;
2158 }
2159 bool TextureInfo::operator==(const TextureInfo &other) const {
2160  return this->extensions == other.extensions && this->extras == other.extras &&
2161  this->index == other.index && this->texCoord == other.texCoord;
2162 }
2163 bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
2164  return this->extensions == other.extensions && this->extras == other.extras &&
2165  this->index == other.index && this->texCoord == other.texCoord &&
2166  TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
2167 }
2168 bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
2169  return this->extensions == other.extensions && this->extras == other.extras &&
2170  this->index == other.index && this->texCoord == other.texCoord &&
2171  TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
2172 }
2173 bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2174  return this->extensions == other.extensions && this->extras == other.extras &&
2175  (this->baseColorTexture == other.baseColorTexture) &&
2176  (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
2177  Equals(this->baseColorFactor, other.baseColorFactor) &&
2178  TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2179  TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2180 }
2181 bool Value::operator==(const Value &other) const {
2182  return Equals(*this, other);
2183 }
2184 
2185 static void swap4(unsigned int *val) {
2186 #ifdef TINYGLTF_LITTLE_ENDIAN
2187  (void)val;
2188 #else
2189  unsigned int tmp = *val;
2190  unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2191  unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2192 
2193  dst[0] = src[3];
2194  dst[1] = src[2];
2195  dst[2] = src[1];
2196  dst[3] = src[0];
2197 #endif
2198 }
2199 
2200 static std::string JoinPath(const std::string &path0,
2201  const std::string &path1) {
2202  if (path0.empty()) {
2203  return path1;
2204  } else {
2205  // check '/'
2206  char lastChar = *path0.rbegin();
2207  if (lastChar != '/') {
2208  return path0 + std::string("/") + path1;
2209  } else {
2210  return path0 + path1;
2211  }
2212  }
2213 }
2214 
2215 static std::string FindFile(const std::vector<std::string> &paths,
2216  const std::string &filepath, FsCallbacks *fs) {
2217  if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2218  fs->FileExists == nullptr) {
2219  // Error, fs callback[s] missing
2220  return std::string();
2221  }
2222 
2223  // https://github.com/syoyo/tinygltf/issues/416
2224  // Use strlen() since std::string's size/length reports the number of elements
2225  // in the buffer, not the length of string(null-terminated) strip
2226  // null-character in the middle of string.
2227  size_t slength = strlen(filepath.c_str());
2228  if (slength == 0) {
2229  return std::string();
2230  }
2231 
2232  std::string cleaned_filepath = std::string(filepath.c_str());
2233 
2234  for (size_t i = 0; i < paths.size(); i++) {
2235  std::string absPath =
2236  fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data);
2237  if (fs->FileExists(absPath, fs->user_data)) {
2238  return absPath;
2239  }
2240  }
2241 
2242  return std::string();
2243 }
2244 
2245 static std::string GetFilePathExtension(const std::string &FileName) {
2246  if (FileName.find_last_of(".") != std::string::npos)
2247  return FileName.substr(FileName.find_last_of(".") + 1);
2248  return "";
2249 }
2250 
2251 static std::string GetBaseDir(const std::string &filepath) {
2252  if (filepath.find_last_of("/\\") != std::string::npos)
2253  return filepath.substr(0, filepath.find_last_of("/\\"));
2254  return "";
2255 }
2256 
2257 static std::string GetBaseFilename(const std::string &filepath) {
2258  auto idx = filepath.find_last_of("/\\");
2259  if (idx != std::string::npos) return filepath.substr(idx + 1);
2260  return filepath;
2261 }
2262 
2263 std::string base64_encode(unsigned char const *, unsigned int len);
2264 std::string base64_decode(std::string const &s);
2265 
2266 /*
2267  base64.cpp and base64.h
2268 
2269  Copyright (C) 2004-2008 RenĂ© Nyffenegger
2270 
2271  This source code is provided 'as-is', without any express or implied
2272  warranty. In no event will the author be held liable for any damages
2273  arising from the use of this software.
2274 
2275  Permission is granted to anyone to use this software for any purpose,
2276  including commercial applications, and to alter it and redistribute it
2277  freely, subject to the following restrictions:
2278 
2279  1. The origin of this source code must not be misrepresented; you must not
2280  claim that you wrote the original source code. If you use this source code
2281  in a product, an acknowledgment in the product documentation would be
2282  appreciated but is not required.
2283 
2284  2. Altered source versions must be plainly marked as such, and must not be
2285  misrepresented as being the original source code.
2286 
2287  3. This notice may not be removed or altered from any source distribution.
2288 
2289  RenĂ© Nyffenegger rene.nyffenegger@adp-gmbh.ch
2290 
2291 */
2292 
2293 #ifdef __clang__
2294 #pragma clang diagnostic push
2295 #pragma clang diagnostic ignored "-Wsign-conversion"
2296 #pragma clang diagnostic ignored "-Wconversion"
2297 #endif
2298 
2299 static inline bool is_base64(unsigned char c) {
2300  return (isalnum(c) || (c == '+') || (c == '/'));
2301 }
2302 
2303 std::string base64_encode(unsigned char const *bytes_to_encode,
2304  unsigned int in_len) {
2305  std::string ret;
2306  int i = 0;
2307  int j = 0;
2308  unsigned char char_array_3[3];
2309  unsigned char char_array_4[4];
2310 
2311  const char *base64_chars =
2312  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2313  "abcdefghijklmnopqrstuvwxyz"
2314  "0123456789+/";
2315 
2316  while (in_len--) {
2317  char_array_3[i++] = *(bytes_to_encode++);
2318  if (i == 3) {
2319  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2320  char_array_4[1] =
2321  ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2322  char_array_4[2] =
2323  ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2324  char_array_4[3] = char_array_3[2] & 0x3f;
2325 
2326  for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
2327  i = 0;
2328  }
2329  }
2330 
2331  if (i) {
2332  for (j = i; j < 3; j++) char_array_3[j] = '\0';
2333 
2334  char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2335  char_array_4[1] =
2336  ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2337  char_array_4[2] =
2338  ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2339 
2340  for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
2341 
2342  while ((i++ < 3)) ret += '=';
2343  }
2344 
2345  return ret;
2346 }
2347 
2348 std::string base64_decode(std::string const &encodedString) {
2349  int in_len = static_cast<int>(encodedString.size());
2350  int i = 0;
2351  int j = 0;
2352  int in_ = 0;
2353  unsigned char char_array_4[4], char_array_3[3];
2354  std::string ret;
2355 
2356  const std::string base64_chars =
2357  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2358  "abcdefghijklmnopqrstuvwxyz"
2359  "0123456789+/";
2360 
2361  while (in_len-- && (encodedString[in_] != '=') &&
2362  is_base64(encodedString[in_])) {
2363  char_array_4[i++] = encodedString[in_];
2364  in_++;
2365  if (i == 4) {
2366  for (i = 0; i < 4; i++)
2367  char_array_4[i] =
2368  static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
2369 
2370  char_array_3[0] =
2371  (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2372  char_array_3[1] =
2373  ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2374  char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2375 
2376  for (i = 0; (i < 3); i++) ret += char_array_3[i];
2377  i = 0;
2378  }
2379  }
2380 
2381  if (i) {
2382  for (j = i; j < 4; j++) char_array_4[j] = 0;
2383 
2384  for (j = 0; j < 4; j++)
2385  char_array_4[j] =
2386  static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
2387 
2388  char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2389  char_array_3[1] =
2390  ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2391  char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2392 
2393  for (j = 0; (j < i - 1); j++) {
2394  ret += char_array_3[j];
2395  }
2396  }
2397 
2398  return ret;
2399 }
2400 #ifdef __clang__
2401 #pragma clang diagnostic pop
2402 #endif
2403 
2404 // https://github.com/syoyo/tinygltf/issues/228
2405 // TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2406 // decoding?
2407 //
2408 // Uri Decoding from DLIB
2409 // http://dlib.net/dlib/server/server_http.cpp.html
2410 // --- dlib begin ------------------------------------------------------------
2411 // Copyright (C) 2003 Davis E. King (davis@dlib.net)
2412 // License: Boost Software License
2413 // Boost Software License - Version 1.0 - August 17th, 2003
2414 
2415 // Permission is hereby granted, free of charge, to any person or organization
2416 // obtaining a copy of the software and accompanying documentation covered by
2417 // this license (the "Software") to use, reproduce, display, distribute,
2418 // execute, and transmit the Software, and to prepare derivative works of the
2419 // Software, and to permit third-parties to whom the Software is furnished to
2420 // do so, all subject to the following:
2421 // The copyright notices in the Software and this entire statement, including
2422 // the above license grant, this restriction and the following disclaimer,
2423 // must be included in all copies of the Software, in whole or in part, and
2424 // all derivative works of the Software, unless such copies or derivative
2425 // works are solely in the form of machine-executable object code generated by
2426 // a source language processor.
2427 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2428 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2429 // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2430 // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2431 // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2432 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2433 // DEALINGS IN THE SOFTWARE.
2434 //
2435 namespace dlib {
2436 
2437 inline unsigned char from_hex(unsigned char ch) {
2438  if (ch <= '9' && ch >= '0')
2439  ch -= '0';
2440  else if (ch <= 'f' && ch >= 'a')
2441  ch -= 'a' - 10;
2442  else if (ch <= 'F' && ch >= 'A')
2443  ch -= 'A' - 10;
2444  else
2445  ch = 0;
2446  return ch;
2447 }
2448 
2449 static const std::string urldecode(const std::string &str) {
2450 
2451  string result;
2452  string::size_type i;
2453  for (i = 0; i < str.size(); ++i) {
2454  if (str[i] == '+') {
2455  result += ' ';
2456  } else if (str[i] == '%' && str.size() > i + 2) {
2457  const unsigned char ch1 =
2458  from_hex(static_cast<unsigned char>(str[i + 1]));
2459  const unsigned char ch2 =
2460  from_hex(static_cast<unsigned char>(str[i + 2]));
2461  const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2462  result += static_cast<char>(ch);
2463  i += 2;
2464  } else {
2465  result += str[i];
2466  }
2467  }
2468  return result;
2469 }
2470 
2471 } // namespace dlib
2472 // --- dlib end --------------------------------------------------------------
2473 
2474 bool URIDecode(const std::string &in_uri, std::string *out_uri,
2475  void *user_data) {
2476  (void)user_data;
2477  *out_uri = dlib::urldecode(in_uri);
2478  return true;
2479 }
2480 
2481 static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
2482  std::string *warn, const std::string &filename,
2483  const std::string &basedir, bool required,
2484  size_t reqBytes, bool checkSize,
2485  size_t maxFileSize, FsCallbacks *fs) {
2486  if (fs == nullptr || fs->FileExists == nullptr ||
2487  fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
2488  // This is a developer error, assert() ?
2489  if (err) {
2490  (*err) += "FS callback[s] not set\n";
2491  }
2492  return false;
2493  }
2494 
2495  std::string *failMsgOut = required ? err : warn;
2496 
2497  out->clear();
2498 
2499  std::vector<std::string> paths;
2500  paths.push_back(basedir);
2501  paths.push_back(".");
2502 
2503  std::string filepath = FindFile(paths, filename, fs);
2504  if (filepath.empty() || filename.empty()) {
2505  if (failMsgOut) {
2506  (*failMsgOut) += "File not found : " + filename + "\n";
2507  }
2508  return false;
2509  }
2510 
2511  // Check file size
2512  if (fs->GetFileSizeInBytes) {
2513  size_t file_size{0};
2514  std::string _err;
2515  bool ok =
2516  fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data);
2517  if (!ok) {
2518  if (_err.size()) {
2519  if (failMsgOut) {
2520  (*failMsgOut) += "Getting file size failed : " + filename +
2521  ", err = " + _err + "\n";
2522  }
2523  }
2524  return false;
2525  }
2526 
2527  if (file_size > maxFileSize) {
2528  if (failMsgOut) {
2529  (*failMsgOut) += "File size " + std::to_string(file_size) +
2530  " exceeds maximum allowed file size " +
2531  std::to_string(maxFileSize) + " : " + filepath + "\n";
2532  }
2533  return false;
2534  }
2535  }
2536 
2537  std::vector<unsigned char> buf;
2538  std::string fileReadErr;
2539  bool fileRead =
2540  fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
2541  if (!fileRead) {
2542  if (failMsgOut) {
2543  (*failMsgOut) +=
2544  "File read error : " + filepath + " : " + fileReadErr + "\n";
2545  }
2546  return false;
2547  }
2548 
2549  size_t sz = buf.size();
2550  if (sz == 0) {
2551  if (failMsgOut) {
2552  (*failMsgOut) += "File is empty : " + filepath + "\n";
2553  }
2554  return false;
2555  }
2556 
2557  if (checkSize) {
2558  if (reqBytes == sz) {
2559  out->swap(buf);
2560  return true;
2561  } else {
2562  std::stringstream ss;
2563  ss << "File size mismatch : " << filepath << ", requestedBytes "
2564  << reqBytes << ", but got " << sz << std::endl;
2565  if (failMsgOut) {
2566  (*failMsgOut) += ss.str();
2567  }
2568  return false;
2569  }
2570  }
2571 
2572  out->swap(buf);
2573  return true;
2574 }
2575 
2577  strictness_ = strictness;
2578 }
2579 
2580 void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
2581  LoadImageData = func;
2582  load_image_user_data_ = user_data;
2583  user_image_loader_ = true;
2584 }
2585 
2587  LoadImageData =
2588 #ifndef TINYGLTF_NO_STB_IMAGE
2590 #else
2591  nullptr;
2592 #endif
2593 
2594  load_image_user_data_ = nullptr;
2595  user_image_loader_ = false;
2596 }
2597 
2598 #ifndef TINYGLTF_NO_STB_IMAGE
2599 bool LoadImageData(Image *image, const int image_idx, std::string *err,
2600  std::string *warn, int req_width, int req_height,
2601  const unsigned char *bytes, int size, void *user_data) {
2602  (void)warn;
2603 
2604  LoadImageDataOption option;
2605  if (user_data) {
2606  option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2607  }
2608 
2609  int w = 0, h = 0, comp = 0, req_comp = 0;
2610 
2611  unsigned char *data = nullptr;
2612 
2613  // preserve_channels true: Use channels stored in the image file.
2614  // false: force 32-bit textures for common Vulkan compatibility. It appears
2615  // that some GPU drivers do not support 24-bit images for Vulkan
2616  req_comp = option.preserve_channels ? 0 : 4;
2617  int bits = 8;
2618  int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
2619 
2620  // It is possible that the image we want to load is a 16bit per channel image
2621  // We are going to attempt to load it as 16bit per channel, and if it worked,
2622  // set the image data accordingly. We are casting the returned pointer into
2623  // unsigned char, because we are representing "bytes". But we are updating
2624  // the Image metadata to signal that this image uses 2 bytes (16bits) per
2625  // channel:
2626  if (stbi_is_16_bit_from_memory(bytes, size)) {
2627  data = reinterpret_cast<unsigned char *>(
2628  stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
2629  if (data) {
2630  bits = 16;
2632  }
2633  }
2634 
2635  // at this point, if data is still NULL, it means that the image wasn't
2636  // 16bit per channel, we are going to load it as a normal 8bit per channel
2637  // image as we used to do:
2638  // if image cannot be decoded, ignore parsing and keep it by its path
2639  // don't break in this case
2640  // FIXME we should only enter this function if the image is embedded. If
2641  // image->uri references
2642  // an image file, it should be left as it is. Image loading should not be
2643  // mandatory (to support other formats)
2644  if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
2645  if (!data) {
2646  // NOTE: you can use `warn` instead of `err`
2647  if (err) {
2648  (*err) +=
2649  "Unknown image format. STB cannot decode image data for image[" +
2650  std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
2651  }
2652  return false;
2653  }
2654 
2655  if ((w < 1) || (h < 1)) {
2656  stbi_image_free(data);
2657  if (err) {
2658  (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2659  "] name = \"" + image->name + "\"\n";
2660  }
2661  return false;
2662  }
2663 
2664  if (req_width > 0) {
2665  if (req_width != w) {
2666  stbi_image_free(data);
2667  if (err) {
2668  (*err) += "Image width mismatch for image[" +
2669  std::to_string(image_idx) + "] name = \"" + image->name +
2670  "\"\n";
2671  }
2672  return false;
2673  }
2674  }
2675 
2676  if (req_height > 0) {
2677  if (req_height != h) {
2678  stbi_image_free(data);
2679  if (err) {
2680  (*err) += "Image height mismatch. for image[" +
2681  std::to_string(image_idx) + "] name = \"" + image->name +
2682  "\"\n";
2683  }
2684  return false;
2685  }
2686  }
2687 
2688  if (req_comp != 0) {
2689  // loaded data has `req_comp` channels(components)
2690  comp = req_comp;
2691  }
2692 
2693  image->width = w;
2694  image->height = h;
2695  image->component = comp;
2696  image->bits = bits;
2697  image->pixel_type = pixel_type;
2698  image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2699  std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
2700  stbi_image_free(data);
2701 
2702  return true;
2703 }
2704 #endif
2705 
2706 void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2707  WriteImageData = func;
2708  write_image_user_data_ = user_data;
2709 }
2710 
2711 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2712 static void WriteToMemory_stbi(void *context, void *data, int size) {
2713  std::vector<unsigned char> *buffer =
2714  reinterpret_cast<std::vector<unsigned char> *>(context);
2715 
2716  unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2717 
2718  buffer->insert(buffer->end(), pData, pData + size);
2719 }
2720 
2721 bool WriteImageData(const std::string *basepath, const std::string *filename,
2722  const Image *image, bool embedImages,
2723  const URICallbacks *uri_cb, std::string *out_uri,
2724  void *fsPtr) {
2725  const std::string ext = GetFilePathExtension(*filename);
2726 
2727  // Write image to temporary buffer
2728  std::string header;
2729  std::vector<unsigned char> data;
2730 
2731  if (ext == "png") {
2732  if ((image->bits != 8) ||
2733  (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
2734  // Unsupported pixel format
2735  return false;
2736  }
2737 
2738  if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
2739  image->height, image->component,
2740  &image->image[0], 0)) {
2741  return false;
2742  }
2743  header = "data:image/png;base64,";
2744  } else if (ext == "jpg") {
2745  if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
2746  image->height, image->component,
2747  &image->image[0], 100)) {
2748  return false;
2749  }
2750  header = "data:image/jpeg;base64,";
2751  } else if (ext == "bmp") {
2752  if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
2753  image->height, image->component,
2754  &image->image[0])) {
2755  return false;
2756  }
2757  header = "data:image/bmp;base64,";
2758  } else if (!embedImages) {
2759  // Error: can't output requested format to file
2760  return false;
2761  }
2762 
2763  if (embedImages) {
2764  // Embed base64-encoded image into URI
2765  if (data.size()) {
2766  *out_uri = header + base64_encode(&data[0],
2767  static_cast<unsigned int>(data.size()));
2768  } else {
2769  // Throw error?
2770  }
2771  } else {
2772  // Write image to disc
2773  FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
2774  if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
2775  const std::string imagefilepath = JoinPath(*basepath, *filename);
2776  std::string writeError;
2777  if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2778  fs->user_data)) {
2779  // Could not write image file to disc; Throw error ?
2780  return false;
2781  }
2782  } else {
2783  // Throw error?
2784  }
2785  if (uri_cb->encode) {
2786  if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
2787  return false;
2788  }
2789  } else {
2790  *out_uri = *filename;
2791  }
2792  }
2793 
2794  return true;
2795 }
2796 #endif
2797 
2798 void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
2799  assert(callbacks.decode);
2800  if (callbacks.decode) {
2801  uri_cb = callbacks;
2802  }
2803 }
2804 
2805 void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
2806 
2807 #ifdef _WIN32
2808 static inline std::wstring UTF8ToWchar(const std::string &str) {
2809  int wstr_size =
2810  MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
2811  std::wstring wstr((size_t)wstr_size, 0);
2812  MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2813  (int)wstr.size());
2814  return wstr;
2815 }
2816 
2817 static inline std::string WcharToUTF8(const std::wstring &wstr) {
2818  int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
2819  nullptr, 0, nullptr, nullptr);
2820  std::string str((size_t)str_size, 0);
2821  WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
2822  (int)str.size(), nullptr, nullptr);
2823  return str;
2824 }
2825 #endif
2826 
2827 #ifndef TINYGLTF_NO_FS
2828 // Default implementations of filesystem functions
2829 
2830 bool FileExists(const std::string &abs_filename, void *) {
2831  bool ret;
2832 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2833  if (asset_manager) {
2834  AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2835  AASSET_MODE_STREAMING);
2836  if (!asset) {
2837  return false;
2838  }
2839  AAsset_close(asset);
2840  ret = true;
2841  } else {
2842  return false;
2843  }
2844 #else
2845 #ifdef _WIN32
2846 #if defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2847 
2848  // First check if a file is a directory.
2849  DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str());
2850  if (result == INVALID_FILE_ATTRIBUTES) {
2851  return false;
2852  }
2853  if (result & FILE_ATTRIBUTE_DIRECTORY) {
2854  return false;
2855  }
2856 
2857  FILE *fp = nullptr;
2858  errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
2859  if (err != 0) {
2860  return false;
2861  }
2862 #elif defined(__GLIBCXX__)
2863  FILE *fp = fopen(abs_filename.c_str(), "rb");
2864  if (!fp) {
2865  return false;
2866  }
2867 #else
2868  // TODO: is_directory check
2869  FILE *fp = nullptr;
2870  errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2871  if (err != 0) {
2872  return false;
2873  }
2874 #endif
2875 
2876 #else
2877  struct stat sb;
2878  if (stat(abs_filename.c_str(), &sb)) {
2879  return false;
2880  }
2881  if (S_ISDIR(sb.st_mode)) {
2882  return false;
2883  }
2884 
2885  FILE *fp = fopen(abs_filename.c_str(), "rb");
2886 #endif
2887  if (fp) {
2888  ret = true;
2889  fclose(fp);
2890  } else {
2891  ret = false;
2892  }
2893 #endif
2894 
2895  return ret;
2896 }
2897 
2898 std::string ExpandFilePath(const std::string &filepath, void *) {
2899  // https://github.com/syoyo/tinygltf/issues/368
2900  //
2901  // No file path expansion in built-in FS function anymore, since glTF URI
2902  // should not contain tilde('~') and environment variables, and for security
2903  // reason(`wordexp`).
2904  //
2905  // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2906  // `LoadBinaryFromMemory`) in expanded absolute path.
2907 
2908  return filepath;
2909 
2910 #if 0
2911 #ifdef _WIN32
2912  // Assume input `filepath` is encoded in UTF-8
2913  std::wstring wfilepath = UTF8ToWchar(filepath);
2914  DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
2915  wchar_t *wstr = new wchar_t[wlen];
2916  ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
2917 
2918  std::wstring ws(wstr);
2919  delete[] wstr;
2920  return WcharToUTF8(ws);
2921 
2922 #else
2923 
2924 #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
2925  defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
2926  // no expansion
2927  std::string s = filepath;
2928 #else
2929  std::string s;
2930  wordexp_t p;
2931 
2932  if (filepath.empty()) {
2933  return "";
2934  }
2935 
2936  // Quote the string to keep any spaces in filepath intact.
2937  std::string quoted_path = "\"" + filepath + "\"";
2938  // char** w;
2939  int ret = wordexp(quoted_path.c_str(), &p, 0);
2940  if (ret) {
2941  // err
2942  s = filepath;
2943  return s;
2944  }
2945 
2946  // Use first element only.
2947  if (p.we_wordv) {
2948  s = std::string(p.we_wordv[0]);
2949  wordfree(&p);
2950  } else {
2951  s = filepath;
2952  }
2953 
2954 #endif
2955 
2956  return s;
2957 #endif
2958 #endif
2959 }
2960 
2961 bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
2962  const std::string &filepath, void *userdata) {
2963  (void)userdata;
2964 
2965 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2966  if (asset_manager) {
2967  AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2968  AASSET_MODE_STREAMING);
2969  if (!asset) {
2970  if (err) {
2971  (*err) += "File open error : " + filepath + "\n";
2972  }
2973  return false;
2974  }
2975  size_t size = AAsset_getLength(asset);
2976 
2977  if (size == 0) {
2978  if (err) {
2979  (*err) += "Invalid file size : " + filepath +
2980  " (does the path point to a directory?)";
2981  }
2982  return false;
2983  }
2984 
2985  return true;
2986  } else {
2987  if (err) {
2988  (*err) += "No asset manager specified : " + filepath + "\n";
2989  }
2990  return false;
2991  }
2992 #else
2993 #ifdef _WIN32
2994 #if defined(__GLIBCXX__) // mingw
2995  int file_descriptor =
2996  _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2997  __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2998  std::istream f(&wfile_buf);
2999 #elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
3000  // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
3001  // `wchar_t *`
3002  std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
3003 #else
3004  // Unknown compiler/runtime
3005  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3006 #endif
3007 #else
3008  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3009 #endif
3010  if (!f) {
3011  if (err) {
3012  (*err) += "File open error : " + filepath + "\n";
3013  }
3014  return false;
3015  }
3016 
3017  // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
3018  f.peek();
3019  if (!f) {
3020  if (err) {
3021  (*err) +=
3022  "File read error. Maybe empty file or invalid file : " + filepath +
3023  "\n";
3024  }
3025  return false;
3026  }
3027 
3028  f.seekg(0, f.end);
3029  const auto sz = f.tellg();
3030 
3031  // std::cout << "sz = " << sz << "\n";
3032  f.seekg(0, f.beg);
3033 
3034  if (sz < 0) {
3035  if (err) {
3036  (*err) += "Invalid file size : " + filepath +
3037  " (does the path point to a directory?)";
3038  }
3039  return false;
3040  } else if (sz == std::streamoff(0)) {
3041  if (err) {
3042  (*err) += "File is empty : " + filepath + "\n";
3043  }
3044  return false;
3045  } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3046  if (err) {
3047  (*err) += "Invalid file size : " + filepath + "\n";
3048  }
3049  return false;
3050  }
3051 
3052  (*filesize_out) = sz;
3053  return true;
3054 #endif
3055 }
3056 
3057 bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
3058  const std::string &filepath, void *) {
3059 
3060 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
3061  if (asset_manager) {
3062  AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
3063  AASSET_MODE_STREAMING);
3064  if (!asset) {
3065  if (err) {
3066  (*err) += "File open error : " + filepath + "\n";
3067  }
3068  return false;
3069  }
3070  size_t size = AAsset_getLength(asset);
3071  if (size == 0) {
3072  if (err) {
3073  (*err) += "Invalid file size : " + filepath +
3074  " (does the path point to a directory?)";
3075  }
3076  return false;
3077  }
3078  out->resize(size);
3079  AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
3080  AAsset_close(asset);
3081  return true;
3082  } else {
3083  if (err) {
3084  (*err) += "No asset manager specified : " + filepath + "\n";
3085  }
3086  return false;
3087  }
3088 #else
3089 #ifdef _WIN32
3090 #if defined(__GLIBCXX__) // mingw
3091  int file_descriptor =
3092  _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
3093  __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
3094  std::istream f(&wfile_buf);
3095 #elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
3096  // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
3097  // `wchar_t *`
3098  std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
3099 #else
3100  // Unknown compiler/runtime
3101  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3102 #endif
3103 #else
3104  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3105 #endif
3106  if (!f) {
3107  if (err) {
3108  (*err) += "File open error : " + filepath + "\n";
3109  }
3110  return false;
3111  }
3112 
3113  // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
3114  f.peek();
3115  if (!f) {
3116  if (err) {
3117  (*err) +=
3118  "File read error. Maybe empty file or invalid file : " + filepath +
3119  "\n";
3120  }
3121  return false;
3122  }
3123 
3124  f.seekg(0, f.end);
3125  const auto sz = f.tellg();
3126 
3127  // std::cout << "sz = " << sz << "\n";
3128  f.seekg(0, f.beg);
3129 
3130  if (sz < 0) {
3131  if (err) {
3132  (*err) += "Invalid file size : " + filepath +
3133  " (does the path point to a directory?)";
3134  }
3135  return false;
3136  } else if (sz == std::streamoff(0)) {
3137  if (err) {
3138  (*err) += "File is empty : " + filepath + "\n";
3139  }
3140  return false;
3141  } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3142  if (err) {
3143  (*err) += "Invalid file size : " + filepath + "\n";
3144  }
3145  return false;
3146  }
3147 
3148  out->resize(sz);
3149  f.read(reinterpret_cast<char *>(&out->at(0)),
3150  static_cast<std::streamsize>(sz));
3151 
3152  return true;
3153 #endif
3154 }
3155 
3156 bool WriteWholeFile(std::string *err, const std::string &filepath,
3157  const std::vector<unsigned char> &contents, void *) {
3158 #ifdef _WIN32
3159 #if defined(__GLIBCXX__) // mingw
3160  int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
3161  _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
3162  __gnu_cxx::stdio_filebuf<char> wfile_buf(
3163  file_descriptor, std::ios_base::out | std::ios_base::binary);
3164  std::ostream f(&wfile_buf);
3165 #elif defined(_MSC_VER)
3166  std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
3167 #else // clang?
3168  std::ofstream f(filepath.c_str(), std::ofstream::binary);
3169 #endif
3170 #else
3171  std::ofstream f(filepath.c_str(), std::ofstream::binary);
3172 #endif
3173  if (!f) {
3174  if (err) {
3175  (*err) += "File open error for writing : " + filepath + "\n";
3176  }
3177  return false;
3178  }
3179 
3180  f.write(reinterpret_cast<const char *>(&contents.at(0)),
3181  static_cast<std::streamsize>(contents.size()));
3182  if (!f) {
3183  if (err) {
3184  (*err) += "File write error: " + filepath + "\n";
3185  }
3186  return false;
3187  }
3188 
3189  return true;
3190 }
3191 
3192 #endif // TINYGLTF_NO_FS
3193 
3194 static std::string MimeToExt(const std::string &mimeType) {
3195  if (mimeType == "image/jpeg") {
3196  return "jpg";
3197  } else if (mimeType == "image/png") {
3198  return "png";
3199  } else if (mimeType == "image/bmp") {
3200  return "bmp";
3201  } else if (mimeType == "image/gif") {
3202  return "gif";
3203  }
3204 
3205  return "";
3206 }
3207 
3208 static bool UpdateImageObject(const Image &image, std::string &baseDir,
3209  int index, bool embedImages,
3210  const URICallbacks *uri_cb,
3212  void *user_data, std::string *out_uri) {
3213  std::string filename;
3214  std::string ext;
3215  // If image has uri, use it as a filename
3216  if (image.uri.size()) {
3217  std::string decoded_uri;
3218  if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
3219  // A decode failure results in a failure to write the gltf.
3220  return false;
3221  }
3222  filename = GetBaseFilename(decoded_uri);
3223  ext = GetFilePathExtension(filename);
3224  } else if (image.bufferView != -1) {
3225  // If there's no URI and the data exists in a buffer,
3226  // don't change properties or write images
3227  } else if (image.name.size()) {
3228  ext = MimeToExt(image.mimeType);
3229  // Otherwise use name as filename
3230  filename = image.name + "." + ext;
3231  } else {
3232  ext = MimeToExt(image.mimeType);
3233  // Fallback to index of image as filename
3234  filename = std::to_string(index) + "." + ext;
3235  }
3236 
3237  // If callback is set and image data exists, modify image data object. If
3238  // image data does not exist, this is not considered a failure and the
3239  // original uri should be maintained.
3240  bool imageWritten = false;
3241  if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
3242  imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
3243  uri_cb, out_uri, user_data);
3244  if (!imageWritten) {
3245  return false;
3246  }
3247  }
3248 
3249  // Use the original uri if the image was not written.
3250  if (!imageWritten) {
3251  *out_uri = image.uri;
3252  }
3253 
3254  return true;
3255 }
3256 
3257 bool IsDataURI(const std::string &in) {
3258  std::string header = "data:application/octet-stream;base64,";
3259  if (in.find(header) == 0) {
3260  return true;
3261  }
3262 
3263  header = "data:image/jpeg;base64,";
3264  if (in.find(header) == 0) {
3265  return true;
3266  }
3267 
3268  header = "data:image/png;base64,";
3269  if (in.find(header) == 0) {
3270  return true;
3271  }
3272 
3273  header = "data:image/bmp;base64,";
3274  if (in.find(header) == 0) {
3275  return true;
3276  }
3277 
3278  header = "data:image/gif;base64,";
3279  if (in.find(header) == 0) {
3280  return true;
3281  }
3282 
3283  header = "data:text/plain;base64,";
3284  if (in.find(header) == 0) {
3285  return true;
3286  }
3287 
3288  header = "data:application/gltf-buffer;base64,";
3289  if (in.find(header) == 0) {
3290  return true;
3291  }
3292 
3293  return false;
3294 }
3295 
3296 bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3297  const std::string &in, size_t reqBytes, bool checkSize) {
3298  std::string header = "data:application/octet-stream;base64,";
3299  std::string data;
3300  if (in.find(header) == 0) {
3301  data = base64_decode(in.substr(header.size())); // cut mime string.
3302  }
3303 
3304  if (data.empty()) {
3305  header = "data:image/jpeg;base64,";
3306  if (in.find(header) == 0) {
3307  mime_type = "image/jpeg";
3308  data = base64_decode(in.substr(header.size())); // cut mime string.
3309  }
3310  }
3311 
3312  if (data.empty()) {
3313  header = "data:image/png;base64,";
3314  if (in.find(header) == 0) {
3315  mime_type = "image/png";
3316  data = base64_decode(in.substr(header.size())); // cut mime string.
3317  }
3318  }
3319 
3320  if (data.empty()) {
3321  header = "data:image/bmp;base64,";
3322  if (in.find(header) == 0) {
3323  mime_type = "image/bmp";
3324  data = base64_decode(in.substr(header.size())); // cut mime string.
3325  }
3326  }
3327 
3328  if (data.empty()) {
3329  header = "data:image/gif;base64,";
3330  if (in.find(header) == 0) {
3331  mime_type = "image/gif";
3332  data = base64_decode(in.substr(header.size())); // cut mime string.
3333  }
3334  }
3335 
3336  if (data.empty()) {
3337  header = "data:text/plain;base64,";
3338  if (in.find(header) == 0) {
3339  mime_type = "text/plain";
3340  data = base64_decode(in.substr(header.size()));
3341  }
3342  }
3343 
3344  if (data.empty()) {
3345  header = "data:application/gltf-buffer;base64,";
3346  if (in.find(header) == 0) {
3347  data = base64_decode(in.substr(header.size()));
3348  }
3349  }
3350 
3351  // TODO(syoyo): Allow empty buffer? #229
3352  if (data.empty()) {
3353  return false;
3354  }
3355 
3356  if (checkSize) {
3357  if (data.size() != reqBytes) {
3358  return false;
3359  }
3360  out->resize(reqBytes);
3361  } else {
3362  out->resize(data.size());
3363  }
3364  std::copy(data.begin(), data.end(), out->begin());
3365  return true;
3366 }
3367 
3368 namespace detail {
3369 bool GetInt(const detail::json &o, int &val) {
3370 #ifdef TINYGLTF_USE_RAPIDJSON
3371  if (!o.IsDouble()) {
3372  if (o.IsInt()) {
3373  val = o.GetInt();
3374  return true;
3375  } else if (o.IsUint()) {
3376  val = static_cast<int>(o.GetUint());
3377  return true;
3378  } else if (o.IsInt64()) {
3379  val = static_cast<int>(o.GetInt64());
3380  return true;
3381  } else if (o.IsUint64()) {
3382  val = static_cast<int>(o.GetUint64());
3383  return true;
3384  }
3385  }
3386 
3387  return false;
3388 #else
3389  auto type = o.type();
3390 
3391  if ((type == detail::json::value_t::number_integer) ||
3392  (type == detail::json::value_t::number_unsigned)) {
3393  val = static_cast<int>(o.get<int64_t>());
3394  return true;
3395  }
3396 
3397  return false;
3398 #endif
3399 }
3400 
3401 #ifdef TINYGLTF_USE_RAPIDJSON
3402 bool GetDouble(const detail::json &o, double &val) {
3403  if (o.IsDouble()) {
3404  val = o.GetDouble();
3405  return true;
3406  }
3407 
3408  return false;
3409 }
3410 #endif
3411 
3412 bool GetNumber(const detail::json &o, double &val) {
3413 #ifdef TINYGLTF_USE_RAPIDJSON
3414  if (o.IsNumber()) {
3415  val = o.GetDouble();
3416  return true;
3417  }
3418 
3419  return false;
3420 #else
3421  if (o.is_number()) {
3422  val = o.get<double>();
3423  return true;
3424  }
3425 
3426  return false;
3427 #endif
3428 }
3429 
3430 bool GetString(const detail::json &o, std::string &val) {
3431 #ifdef TINYGLTF_USE_RAPIDJSON
3432  if (o.IsString()) {
3433  val = o.GetString();
3434  return true;
3435  }
3436 
3437  return false;
3438 #else
3439  if (o.type() == detail::json::value_t::string) {
3440  val = o.get<std::string>();
3441  return true;
3442  }
3443 
3444  return false;
3445 #endif
3446 }
3447 
3448 bool IsArray(const detail::json &o) {
3449 #ifdef TINYGLTF_USE_RAPIDJSON
3450  return o.IsArray();
3451 #else
3452  return o.is_array();
3453 #endif
3454 }
3455 
3456 detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
3457 #ifdef TINYGLTF_USE_RAPIDJSON
3458  return o.Begin();
3459 #else
3460  return o.begin();
3461 #endif
3462 }
3463 
3464 detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
3465 #ifdef TINYGLTF_USE_RAPIDJSON
3466  return o.End();
3467 #else
3468  return o.end();
3469 #endif
3470 }
3471 
3472 bool IsObject(const detail::json &o) {
3473 #ifdef TINYGLTF_USE_RAPIDJSON
3474  return o.IsObject();
3475 #else
3476  return o.is_object();
3477 #endif
3478 }
3479 
3480 detail::json_const_iterator ObjectBegin(const detail::json &o) {
3481 #ifdef TINYGLTF_USE_RAPIDJSON
3482  return o.MemberBegin();
3483 #else
3484  return o.begin();
3485 #endif
3486 }
3487 
3488 detail::json_const_iterator ObjectEnd(const detail::json &o) {
3489 #ifdef TINYGLTF_USE_RAPIDJSON
3490  return o.MemberEnd();
3491 #else
3492  return o.end();
3493 #endif
3494 }
3495 
3496 // Making this a const char* results in a pointer to a temporary when
3497 // TINYGLTF_USE_RAPIDJSON is off.
3498 std::string GetKey(detail::json_const_iterator &it) {
3499 #ifdef TINYGLTF_USE_RAPIDJSON
3500  return it->name.GetString();
3501 #else
3502  return it.key().c_str();
3503 #endif
3504 }
3505 
3506 bool FindMember(const detail::json &o, const char *member,
3507  detail::json_const_iterator &it) {
3508 #ifdef TINYGLTF_USE_RAPIDJSON
3509  if (!o.IsObject()) {
3510  return false;
3511  }
3512  it = o.FindMember(member);
3513  return it != o.MemberEnd();
3514 #else
3515  it = o.find(member);
3516  return it != o.end();
3517 #endif
3518 }
3519 
3520 bool FindMember(detail::json &o, const char *member,
3521  detail::json_iterator &it) {
3522 #ifdef TINYGLTF_USE_RAPIDJSON
3523  if (!o.IsObject()) {
3524  return false;
3525  }
3526  it = o.FindMember(member);
3527  return it != o.MemberEnd();
3528 #else
3529  it = o.find(member);
3530  return it != o.end();
3531 #endif
3532 }
3533 
3534 void Erase(detail::json &o, detail::json_iterator &it) {
3535 #ifdef TINYGLTF_USE_RAPIDJSON
3536  o.EraseMember(it);
3537 #else
3538  o.erase(it);
3539 #endif
3540 }
3541 
3542 bool IsEmpty(const detail::json &o) {
3543 #ifdef TINYGLTF_USE_RAPIDJSON
3544  return o.ObjectEmpty();
3545 #else
3546  return o.empty();
3547 #endif
3548 }
3549 
3550 const detail::json &GetValue(detail::json_const_iterator &it) {
3551 #ifdef TINYGLTF_USE_RAPIDJSON
3552  return it->value;
3553 #else
3554  return it.value();
3555 #endif
3556 }
3557 
3558 detail::json &GetValue(detail::json_iterator &it) {
3559 #ifdef TINYGLTF_USE_RAPIDJSON
3560  return it->value;
3561 #else
3562  return it.value();
3563 #endif
3564 }
3565 
3566 std::string JsonToString(const detail::json &o, int spacing = -1) {
3567 #ifdef TINYGLTF_USE_RAPIDJSON
3568  using namespace rapidjson;
3569  StringBuffer buffer;
3570  if (spacing == -1) {
3571  Writer<StringBuffer> writer(buffer);
3572  // TODO: Better error handling.
3573  // https://github.com/syoyo/tinygltf/issues/332
3574  if (!o.Accept(writer)) {
3575  return "tiny_gltf::JsonToString() failed rapidjson conversion";
3576  }
3577  } else {
3578  PrettyWriter<StringBuffer> writer(buffer);
3579  writer.SetIndent(' ', uint32_t(spacing));
3580  if (!o.Accept(writer)) {
3581  return "tiny_gltf::JsonToString() failed rapidjson conversion";
3582  }
3583  }
3584  return buffer.GetString();
3585 #else
3586  return o.dump(spacing);
3587 #endif
3588 }
3589 
3590 } // namespace detail
3591 
3592 static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
3593  Value val{};
3594 #ifdef TINYGLTF_USE_RAPIDJSON
3595  using rapidjson::Type;
3596  switch (o.GetType()) {
3597  case Type::kObjectType: {
3598  Value::Object value_object;
3599  for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
3600  Value entry;
3601  ParseJsonAsValue(&entry, it->value);
3602  if (entry.Type() != NULL_TYPE)
3603  value_object.emplace(detail::GetKey(it), std::move(entry));
3604  }
3605  if (value_object.size() > 0) val = Value(std::move(value_object));
3606  } break;
3607  case Type::kArrayType: {
3608  Value::Array value_array;
3609  value_array.reserve(o.Size());
3610  for (auto it = o.Begin(); it != o.End(); ++it) {
3611  Value entry;
3612  ParseJsonAsValue(&entry, *it);
3613  if (entry.Type() != NULL_TYPE)
3614  value_array.emplace_back(std::move(entry));
3615  }
3616  if (value_array.size() > 0) val = Value(std::move(value_array));
3617  } break;
3618  case Type::kStringType:
3619  val = Value(std::string(o.GetString()));
3620  break;
3621  case Type::kFalseType:
3622  case Type::kTrueType:
3623  val = Value(o.GetBool());
3624  break;
3625  case Type::kNumberType:
3626  if (!o.IsDouble()) {
3627  int i = 0;
3628  detail::GetInt(o, i);
3629  val = Value(i);
3630  } else {
3631  double d = 0.0;
3632  detail::GetDouble(o, d);
3633  val = Value(d);
3634  }
3635  break;
3636  case Type::kNullType:
3637  break;
3638  // all types are covered, so no `case default`
3639  }
3640 #else
3641  switch (o.type()) {
3643  Value::Object value_object;
3644  for (auto it = o.begin(); it != o.end(); it++) {
3645  Value entry;
3646  ParseJsonAsValue(&entry, it.value());
3647  if (entry.Type() != NULL_TYPE)
3648  value_object.emplace(it.key(), std::move(entry));
3649  }
3650  if (value_object.size() > 0) val = Value(std::move(value_object));
3651  } break;
3653  Value::Array value_array;
3654  value_array.reserve(o.size());
3655  for (auto it = o.begin(); it != o.end(); it++) {
3656  Value entry;
3657  ParseJsonAsValue(&entry, it.value());
3658  if (entry.Type() != NULL_TYPE)
3659  value_array.emplace_back(std::move(entry));
3660  }
3661  if (value_array.size() > 0) val = Value(std::move(value_array));
3662  } break;
3663  case detail::json::value_t::string:
3664  val = Value(o.get<std::string>());
3665  break;
3666  case detail::json::value_t::boolean:
3667  val = Value(o.get<bool>());
3668  break;
3669  case detail::json::value_t::number_integer:
3670  case detail::json::value_t::number_unsigned:
3671  val = Value(static_cast<int>(o.get<int64_t>()));
3672  break;
3673  case detail::json::value_t::number_float:
3674  val = Value(o.get<double>());
3675  break;
3676  case detail::json::value_t::null:
3677  case detail::json::value_t::discarded:
3678  case detail::json::value_t::binary:
3679  // default:
3680  break;
3681  }
3682 #endif
3683  const bool isNotNull = val.Type() != NULL_TYPE;
3684 
3685  if (ret) *ret = std::move(val);
3686 
3687  return isNotNull;
3688 }
3689 
3690 static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
3691  detail::json_const_iterator it;
3692  if (!detail::FindMember(o, "extras", it)) {
3693  return false;
3694  }
3695 
3696  return ParseJsonAsValue(ret, detail::GetValue(it));
3697 }
3698 
3699 static bool ParseBooleanProperty(bool *ret, std::string *err,
3700  const detail::json &o,
3701  const std::string &property,
3702  const bool required,
3703  const std::string &parent_node = "") {
3704  detail::json_const_iterator it;
3705  if (!detail::FindMember(o, property.c_str(), it)) {
3706  if (required) {
3707  if (err) {
3708  (*err) += "'" + property + "' property is missing";
3709  if (!parent_node.empty()) {
3710  (*err) += " in " + parent_node;
3711  }
3712  (*err) += ".\n";
3713  }
3714  }
3715  return false;
3716  }
3717 
3718  auto &value = detail::GetValue(it);
3719 
3720  bool isBoolean;
3721  bool boolValue = false;
3722 #ifdef TINYGLTF_USE_RAPIDJSON
3723  isBoolean = value.IsBool();
3724  if (isBoolean) {
3725  boolValue = value.GetBool();
3726  }
3727 #else
3728  isBoolean = value.is_boolean();
3729  if (isBoolean) {
3730  boolValue = value.get<bool>();
3731  }
3732 #endif
3733  if (!isBoolean) {
3734  if (required) {
3735  if (err) {
3736  (*err) += "'" + property + "' property is not a bool type.\n";
3737  }
3738  }
3739  return false;
3740  }
3741 
3742  if (ret) {
3743  (*ret) = boolValue;
3744  }
3745 
3746  return true;
3747 }
3748 
3749 static bool ParseIntegerProperty(int *ret, std::string *err,
3750  const detail::json &o,
3751  const std::string &property,
3752  const bool required,
3753  const std::string &parent_node = "") {
3754  detail::json_const_iterator it;
3755  if (!detail::FindMember(o, property.c_str(), it)) {
3756  if (required) {
3757  if (err) {
3758  (*err) += "'" + property + "' property is missing";
3759  if (!parent_node.empty()) {
3760  (*err) += " in " + parent_node;
3761  }
3762  (*err) += ".\n";
3763  }
3764  }
3765  return false;
3766  }
3767 
3768  int intValue;
3769  bool isInt = detail::GetInt(detail::GetValue(it), intValue);
3770  if (!isInt) {
3771  if (required) {
3772  if (err) {
3773  (*err) += "'" + property + "' property is not an integer type.\n";
3774  }
3775  }
3776  return false;
3777  }
3778 
3779  if (ret) {
3780  (*ret) = intValue;
3781  }
3782 
3783  return true;
3784 }
3785 
3786 static bool ParseUnsignedProperty(size_t *ret, std::string *err,
3787  const detail::json &o,
3788  const std::string &property,
3789  const bool required,
3790  const std::string &parent_node = "") {
3791  detail::json_const_iterator it;
3792  if (!detail::FindMember(o, property.c_str(), it)) {
3793  if (required) {
3794  if (err) {
3795  (*err) += "'" + property + "' property is missing";
3796  if (!parent_node.empty()) {
3797  (*err) += " in " + parent_node;
3798  }
3799  (*err) += ".\n";
3800  }
3801  }
3802  return false;
3803  }
3804 
3805  auto &value = detail::GetValue(it);
3806 
3807  size_t uValue = 0;
3808  bool isUValue;
3809 #ifdef TINYGLTF_USE_RAPIDJSON
3810  isUValue = false;
3811  if (value.IsUint()) {
3812  uValue = value.GetUint();
3813  isUValue = true;
3814  } else if (value.IsUint64()) {
3815  uValue = value.GetUint64();
3816  isUValue = true;
3817  }
3818 #else
3819  isUValue = value.is_number_unsigned();
3820  if (isUValue) {
3821  uValue = value.get<size_t>();
3822  }
3823 #endif
3824  if (!isUValue) {
3825  if (required) {
3826  if (err) {
3827  (*err) += "'" + property + "' property is not a positive integer.\n";
3828  }
3829  }
3830  return false;
3831  }
3832 
3833  if (ret) {
3834  (*ret) = uValue;
3835  }
3836 
3837  return true;
3838 }
3839 
3840 static bool ParseNumberProperty(double *ret, std::string *err,
3841  const detail::json &o,
3842  const std::string &property,
3843  const bool required,
3844  const std::string &parent_node = "") {
3845  detail::json_const_iterator it;
3846 
3847  if (!detail::FindMember(o, property.c_str(), it)) {
3848  if (required) {
3849  if (err) {
3850  (*err) += "'" + property + "' property is missing";
3851  if (!parent_node.empty()) {
3852  (*err) += " in " + parent_node;
3853  }
3854  (*err) += ".\n";
3855  }
3856  }
3857  return false;
3858  }
3859 
3860  double numberValue;
3861  bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
3862 
3863  if (!isNumber) {
3864  if (required) {
3865  if (err) {
3866  (*err) += "'" + property + "' property is not a number type.\n";
3867  }
3868  }
3869  return false;
3870  }
3871 
3872  if (ret) {
3873  (*ret) = numberValue;
3874  }
3875 
3876  return true;
3877 }
3878 
3879 static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
3880  const detail::json &o,
3881  const std::string &property, bool required,
3882  const std::string &parent_node = "") {
3883  detail::json_const_iterator it;
3884  if (!detail::FindMember(o, property.c_str(), it)) {
3885  if (required) {
3886  if (err) {
3887  (*err) += "'" + property + "' property is missing";
3888  if (!parent_node.empty()) {
3889  (*err) += " in " + parent_node;
3890  }
3891  (*err) += ".\n";
3892  }
3893  }
3894  return false;
3895  }
3896 
3897  if (!detail::IsArray(detail::GetValue(it))) {
3898  if (required) {
3899  if (err) {
3900  (*err) += "'" + property + "' property is not an array";
3901  if (!parent_node.empty()) {
3902  (*err) += " in " + parent_node;
3903  }
3904  (*err) += ".\n";
3905  }
3906  }
3907  return false;
3908  }
3909 
3910  ret->clear();
3911  auto end = detail::ArrayEnd(detail::GetValue(it));
3912  for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
3913  double numberValue;
3914  const bool isNumber = detail::GetNumber(*i, numberValue);
3915  if (!isNumber) {
3916  if (required) {
3917  if (err) {
3918  (*err) += "'" + property + "' property is not a number.\n";
3919  if (!parent_node.empty()) {
3920  (*err) += " in " + parent_node;
3921  }
3922  (*err) += ".\n";
3923  }
3924  }
3925  return false;
3926  }
3927  ret->push_back(numberValue);
3928  }
3929 
3930  return true;
3931 }
3932 
3933 static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3934  const detail::json &o,
3935  const std::string &property,
3936  bool required,
3937  const std::string &parent_node = "") {
3938  detail::json_const_iterator it;
3939  if (!detail::FindMember(o, property.c_str(), it)) {
3940  if (required) {
3941  if (err) {
3942  (*err) += "'" + property + "' property is missing";
3943  if (!parent_node.empty()) {
3944  (*err) += " in " + parent_node;
3945  }
3946  (*err) += ".\n";
3947  }
3948  }
3949  return false;
3950  }
3951 
3952  if (!detail::IsArray(detail::GetValue(it))) {
3953  if (required) {
3954  if (err) {
3955  (*err) += "'" + property + "' property is not an array";
3956  if (!parent_node.empty()) {
3957  (*err) += " in " + parent_node;
3958  }
3959  (*err) += ".\n";
3960  }
3961  }
3962  return false;
3963  }
3964 
3965  ret->clear();
3966  auto end = detail::ArrayEnd(detail::GetValue(it));
3967  for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
3968  int numberValue;
3969  bool isNumber = detail::GetInt(*i, numberValue);
3970  if (!isNumber) {
3971  if (required) {
3972  if (err) {
3973  (*err) += "'" + property + "' property is not an integer type.\n";
3974  if (!parent_node.empty()) {
3975  (*err) += " in " + parent_node;
3976  }
3977  (*err) += ".\n";
3978  }
3979  }
3980  return false;
3981  }
3982  ret->push_back(numberValue);
3983  }
3984 
3985  return true;
3986 }
3987 
3988 static bool ParseStringProperty(
3989  std::string *ret, std::string *err, const detail::json &o,
3990  const std::string &property, bool required,
3991  const std::string &parent_node = std::string()) {
3992  detail::json_const_iterator it;
3993  if (!detail::FindMember(o, property.c_str(), it)) {
3994  if (required) {
3995  if (err) {
3996  (*err) += "'" + property + "' property is missing";
3997  if (parent_node.empty()) {
3998  (*err) += ".\n";
3999  } else {
4000  (*err) += " in `" + parent_node + "'.\n";
4001  }
4002  }
4003  }
4004  return false;
4005  }
4006 
4007  std::string strValue;
4008  if (!detail::GetString(detail::GetValue(it), strValue)) {
4009  if (required) {
4010  if (err) {
4011  (*err) += "'" + property + "' property is not a string type.\n";
4012  }
4013  }
4014  return false;
4015  }
4016 
4017  if (ret) {
4018  (*ret) = std::move(strValue);
4019  }
4020 
4021  return true;
4022 }
4023 
4024 static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
4025  std::string *err, const detail::json &o,
4026  const std::string &property,
4027  bool required,
4028  const std::string &parent = "") {
4029  detail::json_const_iterator it;
4030  if (!detail::FindMember(o, property.c_str(), it)) {
4031  if (required) {
4032  if (err) {
4033  if (!parent.empty()) {
4034  (*err) +=
4035  "'" + property + "' property is missing in " + parent + ".\n";
4036  } else {
4037  (*err) += "'" + property + "' property is missing.\n";
4038  }
4039  }
4040  }
4041  return false;
4042  }
4043 
4044  const detail::json &dict = detail::GetValue(it);
4045 
4046  // Make sure we are dealing with an object / dictionary.
4047  if (!detail::IsObject(dict)) {
4048  if (required) {
4049  if (err) {
4050  (*err) += "'" + property + "' property is not an object.\n";
4051  }
4052  }
4053  return false;
4054  }
4055 
4056  ret->clear();
4057 
4058  detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4059  detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
4060 
4061  for (; dictIt != dictItEnd; ++dictIt) {
4062  int intVal;
4063  if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
4064  if (required) {
4065  if (err) {
4066  (*err) += "'" + property + "' value is not an integer type.\n";
4067  }
4068  }
4069  return false;
4070  }
4071 
4072  // Insert into the list.
4073  (*ret)[detail::GetKey(dictIt)] = intVal;
4074  }
4075  return true;
4076 }
4077 
4078 static bool ParseJSONProperty(std::map<std::string, double> *ret,
4079  std::string *err, const detail::json &o,
4080  const std::string &property, bool required) {
4081  detail::json_const_iterator it;
4082  if (!detail::FindMember(o, property.c_str(), it)) {
4083  if (required) {
4084  if (err) {
4085  (*err) += "'" + property + "' property is missing. \n'";
4086  }
4087  }
4088  return false;
4089  }
4090 
4091  const detail::json &obj = detail::GetValue(it);
4092 
4093  if (!detail::IsObject(obj)) {
4094  if (required) {
4095  if (err) {
4096  (*err) += "'" + property + "' property is not a JSON object.\n";
4097  }
4098  }
4099  return false;
4100  }
4101 
4102  ret->clear();
4103 
4104  detail::json_const_iterator it2(detail::ObjectBegin(obj));
4105  detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
4106  for (; it2 != itEnd; ++it2) {
4107  double numVal;
4108  if (detail::GetNumber(detail::GetValue(it2), numVal))
4109  ret->emplace(std::string(detail::GetKey(it2)), numVal);
4110  }
4111 
4112  return true;
4113 }
4114 
4115 static bool ParseParameterProperty(Parameter *param, std::string *err,
4116  const detail::json &o,
4117  const std::string &prop, bool required) {
4118  // A parameter value can either be a string or an array of either a boolean or
4119  // a number. Booleans of any kind aren't supported here. Granted, it
4120  // complicates the Parameter structure and breaks it semantically in the sense
4121  // that the client probably works off the assumption that if the string is
4122  // empty the vector is used, etc. Would a tagged union work?
4123  if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
4124  // Found string property.
4125  return true;
4126  } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
4127  false)) {
4128  // Found a number array.
4129  return true;
4130  } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
4131  param->has_number_value = true;
4132  return true;
4133  } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
4134  false)) {
4135  return true;
4136  } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
4137  return true;
4138  } else {
4139  if (required) {
4140  if (err) {
4141  (*err) += "parameter must be a string or number / number array.\n";
4142  }
4143  }
4144  return false;
4145  }
4146 }
4147 
4148 static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
4149  const detail::json &o) {
4150  (void)err;
4151 
4152  detail::json_const_iterator it;
4153  if (!detail::FindMember(o, "extensions", it)) {
4154  return false;
4155  }
4156 
4157  auto &obj = detail::GetValue(it);
4158  if (!detail::IsObject(obj)) {
4159  return false;
4160  }
4161  ExtensionMap extensions;
4162  detail::json_const_iterator extIt =
4163  detail::ObjectBegin(obj); // it.value().begin();
4164  detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
4165  for (; extIt != extEnd; ++extIt) {
4166  auto &itObj = detail::GetValue(extIt);
4167  if (!detail::IsObject(itObj)) continue;
4168  std::string key(detail::GetKey(extIt));
4169  if (!ParseJsonAsValue(&extensions[key], itObj)) {
4170  if (!key.empty()) {
4171  // create empty object so that an extension object is still of type
4172  // object
4173  extensions[key] = Value{Value::Object{}};
4174  }
4175  }
4176  }
4177  if (ret) {
4178  (*ret) = std::move(extensions);
4179  }
4180  return true;
4181 }
4182 
4183 template <typename GltfType>
4184 static bool ParseExtrasAndExtensions(GltfType *target, std::string *err,
4185  const detail::json &o,
4186  bool store_json_strings) {
4187  ParseExtensionsProperty(&target->extensions, err, o);
4188  ParseExtrasProperty(&target->extras, o);
4189 
4190  if (store_json_strings) {
4191  {
4192  detail::json_const_iterator it;
4193  if (detail::FindMember(o, "extensions", it)) {
4194  target->extensions_json_string =
4195  detail::JsonToString(detail::GetValue(it));
4196  }
4197  }
4198  {
4199  detail::json_const_iterator it;
4200  if (detail::FindMember(o, "extras", it)) {
4201  target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4202  }
4203  }
4204  }
4205  return true;
4206 }
4207 
4208 static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
4209  bool store_original_json_for_extras_and_extensions) {
4210  ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
4211  ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
4212  ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
4213  ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
4214 
4215  ParseExtrasAndExtensions(asset, err, o,
4216  store_original_json_for_extras_and_extensions);
4217  return true;
4218 }
4219 
4220 static bool ParseImage(Image *image, const int image_idx, std::string *err,
4221  std::string *warn, const detail::json &o,
4222  bool store_original_json_for_extras_and_extensions,
4223  const std::string &basedir, const size_t max_file_size,
4224  FsCallbacks *fs, const URICallbacks *uri_cb,
4226  void *load_image_user_data = nullptr) {
4227  // A glTF image must either reference a bufferView or an image uri
4228 
4229  // schema says oneOf [`bufferView`, `uri`]
4230  // TODO(syoyo): Check the type of each parameters.
4231  detail::json_const_iterator it;
4232  bool hasBufferView = detail::FindMember(o, "bufferView", it);
4233  bool hasURI = detail::FindMember(o, "uri", it);
4234 
4235  ParseStringProperty(&image->name, err, o, "name", false);
4236 
4237  if (hasBufferView && hasURI) {
4238  // Should not both defined.
4239  if (err) {
4240  (*err) +=
4241  "Only one of `bufferView` or `uri` should be defined, but both are "
4242  "defined for image[" +
4243  std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
4244  }
4245  return false;
4246  }
4247 
4248  if (!hasBufferView && !hasURI) {
4249  if (err) {
4250  (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
4251  std::to_string(image_idx) + "] name = \"" + image->name +
4252  "\"\n";
4253  }
4254  return false;
4255  }
4256 
4257  ParseExtrasAndExtensions(image, err, o,
4258  store_original_json_for_extras_and_extensions);
4259 
4260  if (hasBufferView) {
4261  int bufferView = -1;
4262  if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
4263  if (err) {
4264  (*err) += "Failed to parse `bufferView` for image[" +
4265  std::to_string(image_idx) + "] name = \"" + image->name +
4266  "\"\n";
4267  }
4268  return false;
4269  }
4270 
4271  std::string mime_type;
4272  ParseStringProperty(&mime_type, err, o, "mimeType", false);
4273 
4274  int width = 0;
4275  ParseIntegerProperty(&width, err, o, "width", false);
4276 
4277  int height = 0;
4278  ParseIntegerProperty(&height, err, o, "height", false);
4279 
4280  // Just only save some information here. Loading actual image data from
4281  // bufferView is done after this `ParseImage` function.
4282  image->bufferView = bufferView;
4283  image->mimeType = mime_type;
4284  image->width = width;
4285  image->height = height;
4286 
4287  return true;
4288  }
4289 
4290  // Parse URI & Load image data.
4291 
4292  std::string uri;
4293  std::string tmp_err;
4294  if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
4295  if (err) {
4296  (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
4297  "] name = \"" + image->name + "\".\n";
4298  }
4299  return false;
4300  }
4301 
4302  std::vector<unsigned char> img;
4303 
4304  if (IsDataURI(uri)) {
4305  if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
4306  if (err) {
4307  (*err) += "Failed to decode 'uri' for image[" +
4308  std::to_string(image_idx) + "] name = \"" + image->name +
4309  "\"\n";
4310  }
4311  return false;
4312  }
4313  } else {
4314  // Assume external file
4315  // Keep texture path (for textures that cannot be decoded)
4316  image->uri = uri;
4317 #ifdef TINYGLTF_NO_EXTERNAL_IMAGE
4318  return true;
4319 #else
4320  std::string decoded_uri;
4321  if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
4322  if (warn) {
4323  (*warn) += "Failed to decode 'uri' for image[" +
4324  std::to_string(image_idx) + "] name = \"" + image->name +
4325  "\"\n";
4326  }
4327 
4328  // Image loading failure is not critical to overall gltf loading.
4329  return true;
4330  }
4331 
4332  if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4333  /* required */ false, /* required bytes */ 0,
4334  /* checksize */ false,
4335  /* max file size */ max_file_size, fs)) {
4336  if (warn) {
4337  (*warn) += "Failed to load external 'uri' for image[" +
4338  std::to_string(image_idx) + "] name = \"" + decoded_uri +
4339  "\"\n";
4340  }
4341  // If the image cannot be loaded, keep uri as image->uri.
4342  return true;
4343  }
4344 
4345  if (img.empty()) {
4346  if (warn) {
4347  (*warn) += "Image data is empty for image[" +
4348  std::to_string(image_idx) + "] name = \"" + image->name +
4349  "\" \n";
4350  }
4351  return false;
4352  }
4353 #endif
4354  }
4355 
4356  if (*LoadImageData == nullptr) {
4357  if (err) {
4358  (*err) += "No LoadImageData callback specified.\n";
4359  }
4360  return false;
4361  }
4362  return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
4363  static_cast<int>(img.size()), load_image_user_data);
4364 }
4365 
4366 static bool ParseTexture(Texture *texture, std::string *err,
4367  const detail::json &o,
4368  bool store_original_json_for_extras_and_extensions,
4369  const std::string &basedir) {
4370  (void)basedir;
4371  int sampler = -1;
4372  int source = -1;
4373  ParseIntegerProperty(&sampler, err, o, "sampler", false);
4374 
4375  ParseIntegerProperty(&source, err, o, "source", false);
4376 
4377  texture->sampler = sampler;
4378  texture->source = source;
4379 
4380  ParseExtrasAndExtensions(texture, err, o,
4381  store_original_json_for_extras_and_extensions);
4382 
4383  ParseStringProperty(&texture->name, err, o, "name", false);
4384 
4385  return true;
4386 }
4387 
4388 static bool ParseTextureInfo(
4389  TextureInfo *texinfo, std::string *err, const detail::json &o,
4390  bool store_original_json_for_extras_and_extensions) {
4391  if (texinfo == nullptr) {
4392  return false;
4393  }
4394 
4395  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4396  /* required */ true, "TextureInfo")) {
4397  return false;
4398  }
4399 
4400  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4401 
4402  ParseExtrasAndExtensions(texinfo, err, o,
4403  store_original_json_for_extras_and_extensions);
4404 
4405  return true;
4406 }
4407 
4408 static bool ParseNormalTextureInfo(
4409  NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
4410  bool store_original_json_for_extras_and_extensions) {
4411  if (texinfo == nullptr) {
4412  return false;
4413  }
4414 
4415  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4416  /* required */ true, "NormalTextureInfo")) {
4417  return false;
4418  }
4419 
4420  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4421  ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4422 
4423  ParseExtrasAndExtensions(texinfo, err, o,
4424  store_original_json_for_extras_and_extensions);
4425 
4426  return true;
4427 }
4428 
4429 static bool ParseOcclusionTextureInfo(
4430  OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
4431  bool store_original_json_for_extras_and_extensions) {
4432  if (texinfo == nullptr) {
4433  return false;
4434  }
4435 
4436  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4437  /* required */ true, "NormalTextureInfo")) {
4438  return false;
4439  }
4440 
4441  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4442  ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4443 
4444  ParseExtrasAndExtensions(texinfo, err, o,
4445  store_original_json_for_extras_and_extensions);
4446 
4447  return true;
4448 }
4449 
4450 static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o,
4451  bool store_original_json_for_extras_and_extensions,
4452  FsCallbacks *fs, const URICallbacks *uri_cb,
4453  const std::string &basedir,
4454  const size_t max_buffer_size, bool is_binary = false,
4455  const unsigned char *bin_data = nullptr,
4456  size_t bin_size = 0) {
4457  size_t byteLength;
4458  if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4459  "Buffer")) {
4460  return false;
4461  }
4462 
4463  // In glTF 2.0, uri is not mandatory anymore
4464  buffer->uri.clear();
4465  ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
4466 
4467  // having an empty uri for a non embedded image should not be valid
4468  if (!is_binary && buffer->uri.empty()) {
4469  if (err) {
4470  (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4471  }
4472  }
4473 
4474  detail::json_const_iterator type;
4475  if (detail::FindMember(o, "type", type)) {
4476  std::string typeStr;
4477  if (detail::GetString(detail::GetValue(type), typeStr)) {
4478  if (typeStr.compare("arraybuffer") == 0) {
4479  // buffer.type = "arraybuffer";
4480  }
4481  }
4482  }
4483 
4484  if (is_binary) {
4485  // Still binary glTF accepts external dataURI.
4486  if (!buffer->uri.empty()) {
4487  // First try embedded data URI.
4488  if (IsDataURI(buffer->uri)) {
4489  std::string mime_type;
4490  if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4491  true)) {
4492  if (err) {
4493  (*err) +=
4494  "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4495  }
4496  return false;
4497  }
4498  } else {
4499  // External .bin file.
4500  std::string decoded_uri;
4501  if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4502  return false;
4503  }
4504  if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
4505  decoded_uri, basedir, /* required */ true,
4506  byteLength, /* checkSize */ true,
4507  /* max_file_size */ max_buffer_size, fs)) {
4508  return false;
4509  }
4510  }
4511  } else {
4512  // load data from (embedded) binary data
4513 
4514  if ((bin_size == 0) || (bin_data == nullptr)) {
4515  if (err) {
4516  (*err) +=
4517  "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
4518  }
4519  return false;
4520  }
4521 
4522  if (byteLength > bin_size) {
4523  if (err) {
4524  std::stringstream ss;
4525  ss << "Invalid `byteLength'. Must be equal or less than binary size: "
4526  "`byteLength' = "
4527  << byteLength << ", binary size = " << bin_size << std::endl;
4528  (*err) += ss.str();
4529  }
4530  return false;
4531  }
4532 
4533  // Read buffer data
4534  buffer->data.resize(static_cast<size_t>(byteLength));
4535  memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
4536  }
4537 
4538  } else {
4539  if (IsDataURI(buffer->uri)) {
4540  std::string mime_type;
4541  if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4542  true)) {
4543  if (err) {
4544  (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4545  }
4546  return false;
4547  }
4548  } else {
4549  // Assume external .bin file.
4550  std::string decoded_uri;
4551  if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4552  return false;
4553  }
4554  if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4555  basedir, /* required */ true, byteLength,
4556  /* checkSize */ true,
4557  /* max file size */ max_buffer_size, fs)) {
4558  return false;
4559  }
4560  }
4561  }
4562 
4563  ParseStringProperty(&buffer->name, err, o, "name", false);
4564 
4565  ParseExtrasAndExtensions(buffer, err, o,
4566  store_original_json_for_extras_and_extensions);
4567 
4568  return true;
4569 }
4570 
4571 static bool ParseBufferView(
4572  BufferView *bufferView, std::string *err, const detail::json &o,
4573  bool store_original_json_for_extras_and_extensions) {
4574  int buffer = -1;
4575  if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
4576  return false;
4577  }
4578 
4579  size_t byteOffset = 0;
4580  ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
4581 
4582  size_t byteLength = 1;
4583  if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4584  "BufferView")) {
4585  return false;
4586  }
4587 
4588  size_t byteStride = 0;
4589  if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
4590  // Spec says: When byteStride of referenced bufferView is not defined, it
4591  // means that accessor elements are tightly packed, i.e., effective stride
4592  // equals the size of the element.
4593  // We cannot determine the actual byteStride until Accessor are parsed, thus
4594  // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4595  byteStride = 0;
4596  }
4597 
4598  if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4599  if (err) {
4600  std::stringstream ss;
4601  ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4602  "4 : "
4603  << byteStride << std::endl;
4604 
4605  (*err) += ss.str();
4606  }
4607  return false;
4608  }
4609 
4610  int target = 0;
4611  ParseIntegerProperty(&target, err, o, "target", false);
4612  if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4614  // OK
4615  } else {
4616  target = 0;
4617  }
4618  bufferView->target = target;
4619 
4620  ParseStringProperty(&bufferView->name, err, o, "name", false);
4621 
4622  ParseExtrasAndExtensions(bufferView, err, o,
4623  store_original_json_for_extras_and_extensions);
4624 
4625  bufferView->buffer = buffer;
4626  bufferView->byteOffset = byteOffset;
4627  bufferView->byteLength = byteLength;
4628  bufferView->byteStride = byteStride;
4629  return true;
4630 }
4631 
4632 static bool ParseSparseAccessor(
4633  Accessor::Sparse *sparse, std::string *err, const detail::json &o,
4634  bool store_original_json_for_extras_and_extensions) {
4635  sparse->isSparse = true;
4636 
4637  int count = 0;
4638  if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4639  return false;
4640  }
4641 
4642  ParseExtrasAndExtensions(sparse, err, o,
4643  store_original_json_for_extras_and_extensions);
4644 
4645  detail::json_const_iterator indices_iterator;
4646  detail::json_const_iterator values_iterator;
4647  if (!detail::FindMember(o, "indices", indices_iterator)) {
4648  (*err) = "the sparse object of this accessor doesn't have indices";
4649  return false;
4650  }
4651 
4652  if (!detail::FindMember(o, "values", values_iterator)) {
4653  (*err) = "the sparse object of this accessor doesn't have values";
4654  return false;
4655  }
4656 
4657  const detail::json &indices_obj = detail::GetValue(indices_iterator);
4658  const detail::json &values_obj = detail::GetValue(values_iterator);
4659 
4660  int indices_buffer_view = 0, component_type = 0;
4661  size_t indices_byte_offset = 0;
4662  if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4663  "bufferView", true, "SparseAccessor")) {
4664  return false;
4665  }
4666  ParseUnsignedProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
4667  false);
4668  if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
4669  true, "SparseAccessor")) {
4670  return false;
4671  }
4672 
4673  int values_buffer_view = 0;
4674  size_t values_byte_offset = 0;
4675  if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
4676  true, "SparseAccessor")) {
4677  return false;
4678  }
4679  ParseUnsignedProperty(&values_byte_offset, err, values_obj, "byteOffset",
4680  false);
4681 
4682  sparse->count = count;
4683  sparse->indices.bufferView = indices_buffer_view;
4684  sparse->indices.byteOffset = indices_byte_offset;
4685  sparse->indices.componentType = component_type;
4686  ParseExtrasAndExtensions(&sparse->indices, err, indices_obj,
4687  store_original_json_for_extras_and_extensions);
4688 
4689  sparse->values.bufferView = values_buffer_view;
4690  sparse->values.byteOffset = values_byte_offset;
4691  ParseExtrasAndExtensions(&sparse->values, err, values_obj,
4692  store_original_json_for_extras_and_extensions);
4693 
4694  return true;
4695 }
4696 
4697 static bool ParseAccessor(Accessor *accessor, std::string *err,
4698  const detail::json &o,
4699  bool store_original_json_for_extras_and_extensions) {
4700  int bufferView = -1;
4701  ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
4702 
4703  size_t byteOffset = 0;
4704  ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
4705 
4706  bool normalized = false;
4707  ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4708 
4709  size_t componentType = 0;
4710  if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4711  "Accessor")) {
4712  return false;
4713  }
4714 
4715  size_t count = 0;
4716  if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
4717  return false;
4718  }
4719 
4720  std::string type;
4721  if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
4722  return false;
4723  }
4724 
4725  if (type.compare("SCALAR") == 0) {
4726  accessor->type = TINYGLTF_TYPE_SCALAR;
4727  } else if (type.compare("VEC2") == 0) {
4728  accessor->type = TINYGLTF_TYPE_VEC2;
4729  } else if (type.compare("VEC3") == 0) {
4730  accessor->type = TINYGLTF_TYPE_VEC3;
4731  } else if (type.compare("VEC4") == 0) {
4732  accessor->type = TINYGLTF_TYPE_VEC4;
4733  } else if (type.compare("MAT2") == 0) {
4734  accessor->type = TINYGLTF_TYPE_MAT2;
4735  } else if (type.compare("MAT3") == 0) {
4736  accessor->type = TINYGLTF_TYPE_MAT3;
4737  } else if (type.compare("MAT4") == 0) {
4738  accessor->type = TINYGLTF_TYPE_MAT4;
4739  } else {
4740  std::stringstream ss;
4741  ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
4742  if (err) {
4743  (*err) += ss.str();
4744  }
4745  return false;
4746  }
4747 
4748  ParseStringProperty(&accessor->name, err, o, "name", false);
4749 
4750  accessor->minValues.clear();
4751  accessor->maxValues.clear();
4752  ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4753  "Accessor");
4754 
4755  ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4756  "Accessor");
4757 
4758  accessor->count = count;
4759  accessor->bufferView = bufferView;
4760  accessor->byteOffset = byteOffset;
4761  accessor->normalized = normalized;
4762  {
4763  if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4764  componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
4765  // OK
4766  accessor->componentType = int(componentType);
4767  } else {
4768  std::stringstream ss;
4769  ss << "Invalid `componentType` in accessor. Got " << componentType
4770  << "\n";
4771  if (err) {
4772  (*err) += ss.str();
4773  }
4774  return false;
4775  }
4776  }
4777 
4778  ParseExtrasAndExtensions(accessor, err, o,
4779  store_original_json_for_extras_and_extensions);
4780 
4781  // check if accessor has a "sparse" object:
4782  detail::json_const_iterator iterator;
4783  if (detail::FindMember(o, "sparse", iterator)) {
4784  // here this accessor has a "sparse" subobject
4785  return ParseSparseAccessor(&accessor->sparse, err,
4786  detail::GetValue(iterator),
4787  store_original_json_for_extras_and_extensions);
4788  }
4789 
4790  return true;
4791 }
4792 
4793 #ifdef TINYGLTF_ENABLE_DRACO
4794 
4795 static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4796  std::vector<uint8_t> &outBuffer) {
4797  if (componentSize == 4) {
4798  assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
4799  memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4800  outBuffer.size());
4801  } else {
4802  size_t faceStride = componentSize * 3;
4803  for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4804  const draco::Mesh::Face &face = mesh->face(f);
4805  if (componentSize == 2) {
4806  uint16_t indices[3] = {(uint16_t)face[0].value(),
4807  (uint16_t)face[1].value(),
4808  (uint16_t)face[2].value()};
4809  memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4810  faceStride);
4811  } else {
4812  uint8_t indices[3] = {(uint8_t)face[0].value(),
4813  (uint8_t)face[1].value(),
4814  (uint8_t)face[2].value()};
4815  memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4816  faceStride);
4817  }
4818  }
4819  }
4820 }
4821 
4822 template <typename T>
4823 static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4824  const draco::PointAttribute *pAttribute,
4825  std::vector<uint8_t> &outBuffer) {
4826  size_t byteOffset = 0;
4827  T values[4] = {0, 0, 0, 0};
4828  for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
4829  const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
4830  if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4831  values))
4832  return false;
4833 
4834  memcpy(outBuffer.data() + byteOffset, &values[0],
4835  sizeof(T) * pAttribute->num_components());
4836  byteOffset += sizeof(T) * pAttribute->num_components();
4837  }
4838 
4839  return true;
4840 }
4841 
4842 static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4843  const draco::PointAttribute *pAttribute,
4844  std::vector<uint8_t> &outBuffer) {
4845  bool decodeResult = false;
4846  switch (componentType) {
4848  decodeResult =
4849  GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4850  break;
4852  decodeResult =
4853  GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4854  break;
4856  decodeResult =
4857  GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4858  break;
4860  decodeResult =
4861  GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4862  break;
4864  decodeResult =
4865  GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4866  break;
4868  decodeResult =
4869  GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4870  break;
4872  decodeResult =
4873  GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4874  break;
4876  decodeResult =
4877  GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4878  break;
4879  default:
4880  return false;
4881  }
4882 
4883  return decodeResult;
4884 }
4885 
4886 static bool ParseDracoExtension(Primitive *primitive, Model *model,
4887  std::string *err, std::string *warn,
4888  const Value &dracoExtensionValue,
4889  ParseStrictness strictness) {
4890  (void)err;
4891  auto bufferViewValue = dracoExtensionValue.Get("bufferView");
4892  if (!bufferViewValue.IsInt()) return false;
4893  auto attributesValue = dracoExtensionValue.Get("attributes");
4894  if (!attributesValue.IsObject()) return false;
4895 
4896  auto attributesObject = attributesValue.Get<Value::Object>();
4897  int bufferView = bufferViewValue.Get<int>();
4898 
4899  BufferView &view = model->bufferViews[bufferView];
4900  Buffer &buffer = model->buffers[view.buffer];
4901  // BufferView has already been decoded
4902  if (view.dracoDecoded) return true;
4903  view.dracoDecoded = true;
4904 
4905  const char *bufferViewData =
4906  reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
4907  size_t bufferViewSize = view.byteLength;
4908 
4909  // decode draco
4910  draco::DecoderBuffer decoderBuffer;
4911  decoderBuffer.Init(bufferViewData, bufferViewSize);
4912  draco::Decoder decoder;
4913  auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4914  if (!decodeResult.ok()) {
4915  return false;
4916  }
4917  const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
4918 
4919  // create new bufferView for indices
4920  if (primitive->indices >= 0) {
4921  if (strictness == ParseStrictness::Permissive) {
4922  const draco::PointIndex::ValueType numPoint = mesh->num_points();
4923  // handle the situation where the stored component type does not match the
4924  // required type for the actual number of stored points
4925  int supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4926  if (numPoint < static_cast<draco::PointIndex::ValueType>(
4927  std::numeric_limits<uint8_t>::max())) {
4928  supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4929  } else if (
4930  numPoint < static_cast<draco::PointIndex::ValueType>(
4931  std::numeric_limits<uint16_t>::max())) {
4932  supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
4933  } else {
4934  supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
4935  }
4936 
4937  if (supposedComponentType > model->accessors[primitive->indices].componentType) {
4938  if (warn) {
4939  (*warn) +=
4940  "GLTF component type " + std::to_string(model->accessors[primitive->indices].componentType) +
4941  " is not sufficient for number of stored points,"
4942  " treating as " + std::to_string(supposedComponentType) + "\n";
4943  }
4944  model->accessors[primitive->indices].componentType = supposedComponentType;
4945  }
4946  }
4947 
4948  int32_t componentSize = GetComponentSizeInBytes(
4949  model->accessors[primitive->indices].componentType);
4950  Buffer decodedIndexBuffer;
4951  decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4952 
4953  DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4954 
4955  model->buffers.emplace_back(std::move(decodedIndexBuffer));
4956 
4957  BufferView decodedIndexBufferView;
4958  decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
4959  decodedIndexBufferView.byteLength =
4960  int(mesh->num_faces() * 3 * componentSize);
4961  decodedIndexBufferView.byteOffset = 0;
4962  decodedIndexBufferView.byteStride = 0;
4963  decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4964  model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4965 
4966  model->accessors[primitive->indices].bufferView =
4967  int(model->bufferViews.size() - 1);
4968  model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
4969  }
4970 
4971  for (const auto &attribute : attributesObject) {
4972  if (!attribute.second.IsInt()) return false;
4973  auto primitiveAttribute = primitive->attributes.find(attribute.first);
4974  if (primitiveAttribute == primitive->attributes.end()) return false;
4975 
4976  int dracoAttributeIndex = attribute.second.Get<int>();
4977  const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
4978  const auto componentType =
4979  model->accessors[primitiveAttribute->second].componentType;
4980 
4981  // Create a new buffer for this decoded buffer
4982  Buffer decodedBuffer;
4983  size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4984  GetComponentSizeInBytes(componentType);
4985  decodedBuffer.data.resize(bufferSize);
4986 
4987  if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4988  decodedBuffer.data))
4989  return false;
4990 
4991  model->buffers.emplace_back(std::move(decodedBuffer));
4992 
4993  BufferView decodedBufferView;
4994  decodedBufferView.buffer = int(model->buffers.size() - 1);
4995  decodedBufferView.byteLength = bufferSize;
4996  decodedBufferView.byteOffset = pAttribute->byte_offset();
4997  decodedBufferView.byteStride = pAttribute->byte_stride();
4998  decodedBufferView.target = primitive->indices >= 0
5001  model->bufferViews.emplace_back(std::move(decodedBufferView));
5002 
5003  model->accessors[primitiveAttribute->second].bufferView =
5004  int(model->bufferViews.size() - 1);
5005  model->accessors[primitiveAttribute->second].count =
5006  int(mesh->num_points());
5007  }
5008 
5009  return true;
5010 }
5011 #endif
5012 
5013 static bool ParsePrimitive(Primitive *primitive, Model *model,
5014  std::string *err, std::string *warn,
5015  const detail::json &o,
5016  bool store_original_json_for_extras_and_extensions,
5017  ParseStrictness strictness) {
5018  int material = -1;
5019  ParseIntegerProperty(&material, err, o, "material", false);
5020  primitive->material = material;
5021 
5023  ParseIntegerProperty(&mode, err, o, "mode", false);
5024  primitive->mode = mode; // Why only triangles were supported ?
5025 
5026  int indices = -1;
5027  ParseIntegerProperty(&indices, err, o, "indices", false);
5028  primitive->indices = indices;
5029  if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
5030  true, "Primitive")) {
5031  return false;
5032  }
5033 
5034  // Look for morph targets
5035  detail::json_const_iterator targetsObject;
5036  if (detail::FindMember(o, "targets", targetsObject) &&
5037  detail::IsArray(detail::GetValue(targetsObject))) {
5038  auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
5039  for (detail::json_const_array_iterator i =
5040  detail::ArrayBegin(detail::GetValue(targetsObject));
5041  i != targetsObjectEnd; ++i) {
5042  std::map<std::string, int> targetAttribues;
5043 
5044  const detail::json &dict = *i;
5045  if (detail::IsObject(dict)) {
5046  detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
5047  detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
5048 
5049  for (; dictIt != dictItEnd; ++dictIt) {
5050  int iVal;
5051  if (detail::GetInt(detail::GetValue(dictIt), iVal))
5052  targetAttribues[detail::GetKey(dictIt)] = iVal;
5053  }
5054  primitive->targets.emplace_back(std::move(targetAttribues));
5055  }
5056  }
5057  }
5058 
5059  ParseExtrasAndExtensions(primitive, err, o,
5060  store_original_json_for_extras_and_extensions);
5061 
5062 #ifdef TINYGLTF_ENABLE_DRACO
5063  auto dracoExtension =
5064  primitive->extensions.find("KHR_draco_mesh_compression");
5065  if (dracoExtension != primitive->extensions.end()) {
5066  ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness);
5067  }
5068 #else
5069  (void)model;
5070  (void)warn;
5071  (void)strictness;
5072 #endif
5073 
5074  return true;
5075 }
5076 
5077 static bool ParseMesh(Mesh *mesh, Model *model,
5078  std::string *err, std::string *warn,
5079  const detail::json &o,
5080  bool store_original_json_for_extras_and_extensions,
5081  ParseStrictness strictness) {
5082  ParseStringProperty(&mesh->name, err, o, "name", false);
5083 
5084  mesh->primitives.clear();
5085  detail::json_const_iterator primObject;
5086  if (detail::FindMember(o, "primitives", primObject) &&
5087  detail::IsArray(detail::GetValue(primObject))) {
5088  detail::json_const_array_iterator primEnd =
5089  detail::ArrayEnd(detail::GetValue(primObject));
5090  for (detail::json_const_array_iterator i =
5091  detail::ArrayBegin(detail::GetValue(primObject));
5092  i != primEnd; ++i) {
5093  Primitive primitive;
5094  if (ParsePrimitive(&primitive, model, err, warn, *i,
5095  store_original_json_for_extras_and_extensions,
5096  strictness)) {
5097  // Only add the primitive if the parsing succeeds.
5098  mesh->primitives.emplace_back(std::move(primitive));
5099  }
5100  }
5101  }
5102 
5103  // Should probably check if has targets and if dimensions fit
5104  ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
5105 
5106  ParseExtrasAndExtensions(mesh, err, o,
5107  store_original_json_for_extras_and_extensions);
5108 
5109  return true;
5110 }
5111 
5112 static bool ParseNode(Node *node, std::string *err, const detail::json &o,
5113  bool store_original_json_for_extras_and_extensions) {
5114  ParseStringProperty(&node->name, err, o, "name", false);
5115 
5116  int skin = -1;
5117  ParseIntegerProperty(&skin, err, o, "skin", false);
5118  node->skin = skin;
5119 
5120  // Matrix and T/R/S are exclusive
5121  if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
5122  ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
5123  ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
5124  ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
5125  }
5126 
5127  int camera = -1;
5128  ParseIntegerProperty(&camera, err, o, "camera", false);
5129  node->camera = camera;
5130 
5131  int mesh = -1;
5132  ParseIntegerProperty(&mesh, err, o, "mesh", false);
5133  node->mesh = mesh;
5134 
5135  node->children.clear();
5136  ParseIntegerArrayProperty(&node->children, err, o, "children", false);
5137 
5138  ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
5139 
5140  ParseExtrasAndExtensions(node, err, o,
5141  store_original_json_for_extras_and_extensions);
5142 
5143  // KHR_lights_punctual: parse light source reference
5144  int light = -1;
5145  if (node->extensions.count("KHR_lights_punctual") != 0) {
5146  auto const &light_ext = node->extensions["KHR_lights_punctual"];
5147  if (light_ext.Has("light")) {
5148  light = light_ext.Get("light").GetNumberAsInt();
5149  } else {
5150  if (err) {
5151  *err +=
5152  "Node has extension KHR_lights_punctual, but does not reference "
5153  "a light source.\n";
5154  }
5155  return false;
5156  }
5157  }
5158  node->light = light;
5159 
5160  // KHR_audio: parse audio source reference
5161  int emitter = -1;
5162  if (node->extensions.count("KHR_audio") != 0) {
5163  auto const &audio_ext = node->extensions["KHR_audio"];
5164  if (audio_ext.Has("emitter")) {
5165  emitter = audio_ext.Get("emitter").GetNumberAsInt();
5166  } else {
5167  if (err) {
5168  *err +=
5169  "Node has extension KHR_audio, but does not reference "
5170  "a audio emitter.\n";
5171  }
5172  return false;
5173  }
5174  }
5175  node->emitter = emitter;
5176 
5177  node->lods.clear();
5178  if (node->extensions.count("MSFT_lod") != 0) {
5179  auto const &msft_lod_ext = node->extensions["MSFT_lod"];
5180  if (msft_lod_ext.Has("ids")) {
5181  auto idsArr = msft_lod_ext.Get("ids");
5182  for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5183  node->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5184  }
5185  } else {
5186  if (err) {
5187  *err +=
5188  "Node has extension MSFT_lod, but does not reference "
5189  "other nodes via their ids.\n";
5190  }
5191  return false;
5192  }
5193  }
5194 
5195  return true;
5196 }
5197 
5198 static bool ParseScene(Scene *scene, std::string *err, const detail::json &o,
5199  bool store_original_json_for_extras_and_extensions) {
5200  ParseStringProperty(&scene->name, err, o, "name", false);
5201  ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false);
5202 
5203  ParseExtrasAndExtensions(scene, err, o,
5204  store_original_json_for_extras_and_extensions);
5205 
5206  // Parse KHR_audio global emitters
5207  if (scene->extensions.count("KHR_audio") != 0) {
5208  auto const &audio_ext = scene->extensions["KHR_audio"];
5209  if (audio_ext.Has("emitters")) {
5210  auto emittersArr = audio_ext.Get("emitters");
5211  for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) {
5212  scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt());
5213  }
5214  } else {
5215  if (err) {
5216  *err +=
5217  "Node has extension KHR_audio, but does not reference "
5218  "a audio emitter.\n";
5219  }
5220  return false;
5221  }
5222  }
5223 
5224  return true;
5225 }
5226 
5227 static bool ParsePbrMetallicRoughness(
5228  PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
5229  bool store_original_json_for_extras_and_extensions) {
5230  if (pbr == nullptr) {
5231  return false;
5232  }
5233 
5234  std::vector<double> baseColorFactor;
5235  if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
5236  /* required */ false)) {
5237  if (baseColorFactor.size() != 4) {
5238  if (err) {
5239  (*err) +=
5240  "Array length of `baseColorFactor` parameter in "
5241  "pbrMetallicRoughness must be 4, but got " +
5242  std::to_string(baseColorFactor.size()) + "\n";
5243  }
5244  return false;
5245  }
5246  pbr->baseColorFactor = baseColorFactor;
5247  }
5248 
5249  {
5250  detail::json_const_iterator it;
5251  if (detail::FindMember(o, "baseColorTexture", it)) {
5252  ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
5253  store_original_json_for_extras_and_extensions);
5254  }
5255  }
5256 
5257  {
5258  detail::json_const_iterator it;
5259  if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
5260  ParseTextureInfo(&pbr->metallicRoughnessTexture, err,
5261  detail::GetValue(it),
5262  store_original_json_for_extras_and_extensions);
5263  }
5264  }
5265 
5266  ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
5267  ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
5268 
5269  ParseExtrasAndExtensions(pbr, err, o,
5270  store_original_json_for_extras_and_extensions);
5271 
5272  return true;
5273 }
5274 
5275 static bool ParseMaterial(Material *material, std::string *err, std::string *warn,
5276  const detail::json &o,
5277  bool store_original_json_for_extras_and_extensions,
5278  ParseStrictness strictness) {
5279  ParseStringProperty(&material->name, err, o, "name", /* required */ false);
5280 
5281  if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5282  "emissiveFactor",
5283  /* required */ false)) {
5284  if (strictness==ParseStrictness::Permissive && material->emissiveFactor.size() == 4) {
5285  if (warn) {
5286  (*warn) +=
5287  "Array length of `emissiveFactor` parameter in "
5288  "material must be 3, but got 4\n";
5289  }
5290  material->emissiveFactor.resize(3);
5291  }
5292  else if (material->emissiveFactor.size() != 3) {
5293  if (err) {
5294  (*err) +=
5295  "Array length of `emissiveFactor` parameter in "
5296  "material must be 3, but got " +
5297  std::to_string(material->emissiveFactor.size()) + "\n";
5298  }
5299  return false;
5300  }
5301  } else {
5302  // fill with default values
5303  material->emissiveFactor = {0.0, 0.0, 0.0};
5304  }
5305 
5306  ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
5307  /* required */ false);
5308  ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
5309  /* required */ false);
5310  ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
5311  /* required */ false);
5312 
5313  {
5314  detail::json_const_iterator it;
5315  if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
5316  ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
5317  detail::GetValue(it),
5318  store_original_json_for_extras_and_extensions);
5319  }
5320  }
5321 
5322  {
5323  detail::json_const_iterator it;
5324  if (detail::FindMember(o, "normalTexture", it)) {
5325  ParseNormalTextureInfo(&material->normalTexture, err,
5326  detail::GetValue(it),
5327  store_original_json_for_extras_and_extensions);
5328  }
5329  }
5330 
5331  {
5332  detail::json_const_iterator it;
5333  if (detail::FindMember(o, "occlusionTexture", it)) {
5334  ParseOcclusionTextureInfo(&material->occlusionTexture, err,
5335  detail::GetValue(it),
5336  store_original_json_for_extras_and_extensions);
5337  }
5338  }
5339 
5340  {
5341  detail::json_const_iterator it;
5342  if (detail::FindMember(o, "emissiveTexture", it)) {
5343  ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
5344  store_original_json_for_extras_and_extensions);
5345  }
5346  }
5347 
5348  // Old code path. For backward compatibility, we still store material values
5349  // as Parameter. This will create duplicated information for
5350  // example(pbrMetallicRoughness), but should be negligible in terms of memory
5351  // consumption.
5352  // TODO(syoyo): Remove in the next major release.
5353  material->values.clear();
5354  material->additionalValues.clear();
5355 
5356  detail::json_const_iterator it(detail::ObjectBegin(o));
5357  detail::json_const_iterator itEnd(detail::ObjectEnd(o));
5358 
5359  for (; it != itEnd; ++it) {
5360  std::string key(detail::GetKey(it));
5361  if (key == "pbrMetallicRoughness") {
5362  if (detail::IsObject(detail::GetValue(it))) {
5363  const detail::json &values_object = detail::GetValue(it);
5364 
5365  detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5366  detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
5367 
5368  for (; itVal != itValEnd; ++itVal) {
5369  Parameter param;
5370  if (ParseParameterProperty(&param, err, values_object,
5371  detail::GetKey(itVal), false)) {
5372  material->values.emplace(detail::GetKey(itVal), std::move(param));
5373  }
5374  }
5375  }
5376  } else if (key == "extensions" || key == "extras") {
5377  // done later, skip, otherwise poorly parsed contents will be saved in the
5378  // parametermap and serialized again later
5379  } else {
5380  Parameter param;
5381  if (ParseParameterProperty(&param, err, o, key, false)) {
5382  // names of materials have already been parsed. Putting it in this map
5383  // doesn't correctly reflect the glTF specification
5384  if (key != "name")
5385  material->additionalValues.emplace(std::move(key), std::move(param));
5386  }
5387  }
5388  }
5389 
5390  material->extensions.clear(); // Note(agnat): Why?
5391  ParseExtrasAndExtensions(material, err, o,
5392  store_original_json_for_extras_and_extensions);
5393 
5394  material->lods.clear();
5395  if (material->extensions.count("MSFT_lod") != 0) {
5396  auto const &msft_lod_ext = material->extensions["MSFT_lod"];
5397  if (msft_lod_ext.Has("ids")) {
5398  auto idsArr = msft_lod_ext.Get("ids");
5399  for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5400  material->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5401  }
5402  } else {
5403  if (err) {
5404  *err +=
5405  "Material has extension MSFT_lod, but does not reference "
5406  "other materials via their ids.\n";
5407  }
5408  return false;
5409  }
5410  }
5411 
5412  return true;
5413 }
5414 
5415 static bool ParseAnimationChannel(
5416  AnimationChannel *channel, std::string *err, const detail::json &o,
5417  bool store_original_json_for_extras_and_extensions) {
5418  int samplerIndex = -1;
5419  int targetIndex = -1;
5420  if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
5421  "AnimationChannel")) {
5422  if (err) {
5423  (*err) += "`sampler` field is missing in animation channels\n";
5424  }
5425  return false;
5426  }
5427 
5428  detail::json_const_iterator targetIt;
5429  if (detail::FindMember(o, "target", targetIt) &&
5430  detail::IsObject(detail::GetValue(targetIt))) {
5431  const detail::json &target_object = detail::GetValue(targetIt);
5432 
5433  ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
5434 
5435  if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5436  true)) {
5437  if (err) {
5438  (*err) += "`path` field is missing in animation.channels.target\n";
5439  }
5440  return false;
5441  }
5442  ParseExtensionsProperty(&channel->target_extensions, err, target_object);
5443  ParseExtrasProperty(&channel->target_extras, target_object);
5444  if (store_original_json_for_extras_and_extensions) {
5445  {
5446  detail::json_const_iterator it;
5447  if (detail::FindMember(target_object, "extensions", it)) {
5448  channel->target_extensions_json_string =
5449  detail::JsonToString(detail::GetValue(it));
5450  }
5451  }
5452  {
5453  detail::json_const_iterator it;
5454  if (detail::FindMember(target_object, "extras", it)) {
5455  channel->target_extras_json_string =
5456  detail::JsonToString(detail::GetValue(it));
5457  }
5458  }
5459  }
5460  }
5461 
5462  channel->sampler = samplerIndex;
5463  channel->target_node = targetIndex;
5464 
5465  ParseExtrasAndExtensions(channel, err, o,
5466  store_original_json_for_extras_and_extensions);
5467 
5468  return true;
5469 }
5470 
5471 static bool ParseAnimation(Animation *animation, std::string *err,
5472  const detail::json &o,
5473  bool store_original_json_for_extras_and_extensions) {
5474  {
5475  detail::json_const_iterator channelsIt;
5476  if (detail::FindMember(o, "channels", channelsIt) &&
5477  detail::IsArray(detail::GetValue(channelsIt))) {
5478  detail::json_const_array_iterator channelEnd =
5479  detail::ArrayEnd(detail::GetValue(channelsIt));
5480  for (detail::json_const_array_iterator i =
5481  detail::ArrayBegin(detail::GetValue(channelsIt));
5482  i != channelEnd; ++i) {
5483  AnimationChannel channel;
5484  if (ParseAnimationChannel(
5485  &channel, err, *i,
5486  store_original_json_for_extras_and_extensions)) {
5487  // Only add the channel if the parsing succeeds.
5488  animation->channels.emplace_back(std::move(channel));
5489  }
5490  }
5491  }
5492  }
5493 
5494  {
5495  detail::json_const_iterator samplerIt;
5496  if (detail::FindMember(o, "samplers", samplerIt) &&
5497  detail::IsArray(detail::GetValue(samplerIt))) {
5498  const detail::json &sampler_array = detail::GetValue(samplerIt);
5499 
5500  detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5501  detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
5502 
5503  for (; it != itEnd; ++it) {
5504  const detail::json &s = *it;
5505 
5506  AnimationSampler sampler;
5507  int inputIndex = -1;
5508  int outputIndex = -1;
5509  if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
5510  if (err) {
5511  (*err) += "`input` field is missing in animation.sampler\n";
5512  }
5513  return false;
5514  }
5515  ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5516  false);
5517  if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
5518  if (err) {
5519  (*err) += "`output` field is missing in animation.sampler\n";
5520  }
5521  return false;
5522  }
5523  sampler.input = inputIndex;
5524  sampler.output = outputIndex;
5525  ParseExtrasAndExtensions(&sampler, err, o,
5526  store_original_json_for_extras_and_extensions);
5527 
5528  animation->samplers.emplace_back(std::move(sampler));
5529  }
5530  }
5531  }
5532 
5533  ParseStringProperty(&animation->name, err, o, "name", false);
5534 
5535  ParseExtrasAndExtensions(animation, err, o,
5536  store_original_json_for_extras_and_extensions);
5537 
5538  return true;
5539 }
5540 
5541 static bool ParseSampler(Sampler *sampler, std::string *err,
5542  const detail::json &o,
5543  bool store_original_json_for_extras_and_extensions) {
5544  ParseStringProperty(&sampler->name, err, o, "name", false);
5545 
5546  int minFilter = -1;
5547  int magFilter = -1;
5548  int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5549  int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
5550  // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
5551  ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5552  ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5553  ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5554  ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
5555  // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5556  // extension
5557 
5558  // TODO(syoyo): Check the value is allowed one.
5559  // (e.g. we allow 9728(NEAREST), but don't allow 9727)
5560 
5561  sampler->minFilter = minFilter;
5562  sampler->magFilter = magFilter;
5563  sampler->wrapS = wrapS;
5564  sampler->wrapT = wrapT;
5565  // sampler->wrapR = wrapR;
5566 
5567  ParseExtrasAndExtensions(sampler, err, o,
5568  store_original_json_for_extras_and_extensions);
5569 
5570  return true;
5571 }
5572 
5573 static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o,
5574  bool store_original_json_for_extras_and_extensions) {
5575  ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
5576 
5577  std::vector<int> joints;
5578  if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
5579  return false;
5580  }
5581  skin->joints = std::move(joints);
5582 
5583  int skeleton = -1;
5584  ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5585  skin->skeleton = skeleton;
5586 
5587  int invBind = -1;
5588  ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5589  skin->inverseBindMatrices = invBind;
5590 
5591  ParseExtrasAndExtensions(skin, err, o,
5592  store_original_json_for_extras_and_extensions);
5593 
5594  return true;
5595 }
5596 
5597 static bool ParsePerspectiveCamera(
5598  PerspectiveCamera *camera, std::string *err, const detail::json &o,
5599  bool store_original_json_for_extras_and_extensions) {
5600  double yfov = 0.0;
5601  if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5602  return false;
5603  }
5604 
5605  double znear = 0.0;
5606  if (!ParseNumberProperty(&znear, err, o, "znear", true,
5607  "PerspectiveCamera")) {
5608  return false;
5609  }
5610 
5611  double aspectRatio = 0.0; // = invalid
5612  ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5613  "PerspectiveCamera");
5614 
5615  double zfar = 0.0; // = invalid
5616  ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5617 
5618  camera->aspectRatio = aspectRatio;
5619  camera->zfar = zfar;
5620  camera->yfov = yfov;
5621  camera->znear = znear;
5622 
5623  ParseExtrasAndExtensions(camera, err, o,
5624  store_original_json_for_extras_and_extensions);
5625 
5626  // TODO(syoyo): Validate parameter values.
5627 
5628  return true;
5629 }
5630 
5631 static bool ParseSpotLight(SpotLight *light, std::string *err,
5632  const detail::json &o,
5633  bool store_original_json_for_extras_and_extensions) {
5634  ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5635  ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
5636 
5637  ParseExtrasAndExtensions(light, err, o,
5638  store_original_json_for_extras_and_extensions);
5639 
5640  // TODO(syoyo): Validate parameter values.
5641 
5642  return true;
5643 }
5644 
5645 static bool ParseOrthographicCamera(
5646  OrthographicCamera *camera, std::string *err, const detail::json &o,
5647  bool store_original_json_for_extras_and_extensions) {
5648  double xmag = 0.0;
5649  if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5650  return false;
5651  }
5652 
5653  double ymag = 0.0;
5654  if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5655  return false;
5656  }
5657 
5658  double zfar = 0.0;
5659  if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5660  return false;
5661  }
5662 
5663  double znear = 0.0;
5664  if (!ParseNumberProperty(&znear, err, o, "znear", true,
5665  "OrthographicCamera")) {
5666  return false;
5667  }
5668 
5669  ParseExtrasAndExtensions(camera, err, o,
5670  store_original_json_for_extras_and_extensions);
5671 
5672  camera->xmag = xmag;
5673  camera->ymag = ymag;
5674  camera->zfar = zfar;
5675  camera->znear = znear;
5676 
5677  // TODO(syoyo): Validate parameter values.
5678 
5679  return true;
5680 }
5681 
5682 static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o,
5683  bool store_original_json_for_extras_and_extensions) {
5684  if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5685  return false;
5686  }
5687 
5688  if (camera->type.compare("orthographic") == 0) {
5689  detail::json_const_iterator orthoIt;
5690  if (!detail::FindMember(o, "orthographic", orthoIt)) {
5691  if (err) {
5692  std::stringstream ss;
5693  ss << "Orthographic camera description not found." << std::endl;
5694  (*err) += ss.str();
5695  }
5696  return false;
5697  }
5698 
5699  const detail::json &v = detail::GetValue(orthoIt);
5700  if (!detail::IsObject(v)) {
5701  if (err) {
5702  std::stringstream ss;
5703  ss << "\"orthographic\" is not a JSON object." << std::endl;
5704  (*err) += ss.str();
5705  }
5706  return false;
5707  }
5708 
5709  if (!ParseOrthographicCamera(
5710  &camera->orthographic, err, v,
5711  store_original_json_for_extras_and_extensions)) {
5712  return false;
5713  }
5714  } else if (camera->type.compare("perspective") == 0) {
5715  detail::json_const_iterator perspIt;
5716  if (!detail::FindMember(o, "perspective", perspIt)) {
5717  if (err) {
5718  std::stringstream ss;
5719  ss << "Perspective camera description not found." << std::endl;
5720  (*err) += ss.str();
5721  }
5722  return false;
5723  }
5724 
5725  const detail::json &v = detail::GetValue(perspIt);
5726  if (!detail::IsObject(v)) {
5727  if (err) {
5728  std::stringstream ss;
5729  ss << "\"perspective\" is not a JSON object." << std::endl;
5730  (*err) += ss.str();
5731  }
5732  return false;
5733  }
5734 
5735  if (!ParsePerspectiveCamera(
5736  &camera->perspective, err, v,
5737  store_original_json_for_extras_and_extensions)) {
5738  return false;
5739  }
5740  } else {
5741  if (err) {
5742  std::stringstream ss;
5743  ss << "Invalid camera type: \"" << camera->type
5744  << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5745  (*err) += ss.str();
5746  }
5747  return false;
5748  }
5749 
5750  ParseStringProperty(&camera->name, err, o, "name", false);
5751 
5752  ParseExtrasAndExtensions(camera, err, o,
5753  store_original_json_for_extras_and_extensions);
5754 
5755  return true;
5756 }
5757 
5758 static bool ParseLight(Light *light, std::string *err, const detail::json &o,
5759  bool store_original_json_for_extras_and_extensions) {
5760  if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5761  return false;
5762  }
5763 
5764  if (light->type == "spot") {
5765  detail::json_const_iterator spotIt;
5766  if (!detail::FindMember(o, "spot", spotIt)) {
5767  if (err) {
5768  std::stringstream ss;
5769  ss << "Spot light description not found." << std::endl;
5770  (*err) += ss.str();
5771  }
5772  return false;
5773  }
5774 
5775  const detail::json &v = detail::GetValue(spotIt);
5776  if (!detail::IsObject(v)) {
5777  if (err) {
5778  std::stringstream ss;
5779  ss << "\"spot\" is not a JSON object." << std::endl;
5780  (*err) += ss.str();
5781  }
5782  return false;
5783  }
5784 
5785  if (!ParseSpotLight(&light->spot, err, v,
5786  store_original_json_for_extras_and_extensions)) {
5787  return false;
5788  }
5789  }
5790 
5791  ParseStringProperty(&light->name, err, o, "name", false);
5792  ParseNumberArrayProperty(&light->color, err, o, "color", false);
5793  ParseNumberProperty(&light->range, err, o, "range", false);
5794  ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5795 
5796  ParseExtrasAndExtensions(light, err, o,
5797  store_original_json_for_extras_and_extensions);
5798 
5799  return true;
5800 }
5801 
5802 static bool ParsePositionalEmitter(
5803  PositionalEmitter *positional, std::string *err, const detail::json &o,
5804  bool store_original_json_for_extras_and_extensions) {
5805  ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle",
5806  false);
5807  ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle",
5808  false);
5809  ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain",
5810  false);
5811  ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false);
5812  ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false);
5813  ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor",
5814  false);
5815 
5816  ParseExtrasAndExtensions(positional, err, o,
5817  store_original_json_for_extras_and_extensions);
5818 
5819  return true;
5820 }
5821 
5822 static bool ParseAudioEmitter(
5823  AudioEmitter *emitter, std::string *err, const detail::json &o,
5824  bool store_original_json_for_extras_and_extensions) {
5825  if (!ParseStringProperty(&emitter->type, err, o, "type", true)) {
5826  return false;
5827  }
5828 
5829  if (emitter->type == "positional") {
5830  detail::json_const_iterator positionalIt;
5831  if (!detail::FindMember(o, "positional", positionalIt)) {
5832  if (err) {
5833  std::stringstream ss;
5834  ss << "Positional emitter description not found." << std::endl;
5835  (*err) += ss.str();
5836  }
5837  return false;
5838  }
5839 
5840  const detail::json &v = detail::GetValue(positionalIt);
5841  if (!detail::IsObject(v)) {
5842  if (err) {
5843  std::stringstream ss;
5844  ss << "\"positional\" is not a JSON object." << std::endl;
5845  (*err) += ss.str();
5846  }
5847  return false;
5848  }
5849 
5850  if (!ParsePositionalEmitter(
5851  &emitter->positional, err, v,
5852  store_original_json_for_extras_and_extensions)) {
5853  return false;
5854  }
5855  }
5856 
5857  ParseStringProperty(&emitter->name, err, o, "name", false);
5858  ParseNumberProperty(&emitter->gain, err, o, "gain", false);
5859  ParseBooleanProperty(&emitter->loop, err, o, "loop", false);
5860  ParseBooleanProperty(&emitter->playing, err, o, "playing", false);
5861  ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false);
5862  ParseIntegerProperty(&emitter->source, err, o, "source", true);
5863 
5864  ParseExtrasAndExtensions(emitter, err, o,
5865  store_original_json_for_extras_and_extensions);
5866 
5867  return true;
5868 }
5869 
5870 static bool ParseAudioSource(
5871  AudioSource *source, std::string *err, const detail::json &o,
5872  bool store_original_json_for_extras_and_extensions) {
5873  ParseStringProperty(&source->name, err, o, "name", false);
5874  ParseStringProperty(&source->uri, err, o, "uri", false);
5875 
5876  if (source->uri.empty()) {
5877  ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true);
5878  ParseStringProperty(&source->mimeType, err, o, "mimeType", true);
5879  }
5880 
5881  ParseExtrasAndExtensions(source, err, o,
5882  store_original_json_for_extras_and_extensions);
5883 
5884  return true;
5885 }
5886 
5887 namespace detail {
5888 
5889 template <typename Callback>
5890 bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5891  detail::json_const_iterator itm;
5892  if (detail::FindMember(_v, member, itm) &&
5893  detail::IsArray(detail::GetValue(itm))) {
5894  const detail::json &root = detail::GetValue(itm);
5895  auto it = detail::ArrayBegin(root);
5896  auto end = detail::ArrayEnd(root);
5897  for (; it != end; ++it) {
5898  if (!cb(*it)) return false;
5899  }
5900  }
5901  return true;
5902 };
5903 
5904 } // end of namespace detail
5905 
5906 bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5907  const char *json_str,
5908  unsigned int json_str_length,
5909  const std::string &base_dir,
5910  unsigned int check_sections) {
5911  if (json_str_length < 4) {
5912  if (err) {
5913  (*err) = "JSON string too short.\n";
5914  }
5915  return false;
5916  }
5917 
5918  detail::JsonDocument v;
5919 
5920 #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5921  defined(_CPPUNWIND)) && \
5922  !defined(TINYGLTF_NOEXCEPTION)
5923  try {
5924  detail::JsonParse(v, json_str, json_str_length, true);
5925 
5926  } catch (const std::exception &e) {
5927  if (err) {
5928  (*err) = e.what();
5929  }
5930  return false;
5931  }
5932 #else
5933  {
5934  detail::JsonParse(v, json_str, json_str_length);
5935 
5936  if (!detail::IsObject(v)) {
5937  // Assume parsing was failed.
5938  if (err) {
5939  (*err) = "Failed to parse JSON object\n";
5940  }
5941  return false;
5942  }
5943  }
5944 #endif
5945 
5946  if (!detail::IsObject(v)) {
5947  // root is not an object.
5948  if (err) {
5949  (*err) = "Root element is not a JSON object\n";
5950  }
5951  return false;
5952  }
5953 
5954  {
5955  bool version_found = false;
5956  detail::json_const_iterator it;
5957  if (detail::FindMember(v, "asset", it) &&
5958  detail::IsObject(detail::GetValue(it))) {
5959  auto &itObj = detail::GetValue(it);
5960  detail::json_const_iterator version_it;
5961  std::string versionStr;
5962  if (detail::FindMember(itObj, "version", version_it) &&
5963  detail::GetString(detail::GetValue(version_it), versionStr)) {
5964  version_found = true;
5965  }
5966  }
5967  if (version_found) {
5968  // OK
5969  } else if (check_sections & REQUIRE_VERSION) {
5970  if (err) {
5971  (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5972  }
5973  return false;
5974  }
5975  }
5976 
5977  // scene is not mandatory.
5978  // FIXME Maybe a better way to handle it than removing the code
5979 
5980  auto IsArrayMemberPresent = [](const detail::json &_v,
5981  const char *name) -> bool {
5982  detail::json_const_iterator it;
5983  return detail::FindMember(_v, name, it) &&
5984  detail::IsArray(detail::GetValue(it));
5985  };
5986 
5987  {
5988  if ((check_sections & REQUIRE_SCENES) &&
5989  !IsArrayMemberPresent(v, "scenes")) {
5990  if (err) {
5991  (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5992  }
5993  return false;
5994  }
5995  }
5996 
5997  {
5998  if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
5999  if (err) {
6000  (*err) += "\"nodes\" object not found in .gltf\n";
6001  }
6002  return false;
6003  }
6004  }
6005 
6006  {
6007  if ((check_sections & REQUIRE_ACCESSORS) &&
6008  !IsArrayMemberPresent(v, "accessors")) {
6009  if (err) {
6010  (*err) += "\"accessors\" object not found in .gltf\n";
6011  }
6012  return false;
6013  }
6014  }
6015 
6016  {
6017  if ((check_sections & REQUIRE_BUFFERS) &&
6018  !IsArrayMemberPresent(v, "buffers")) {
6019  if (err) {
6020  (*err) += "\"buffers\" object not found in .gltf\n";
6021  }
6022  return false;
6023  }
6024  }
6025 
6026  {
6027  if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
6028  !IsArrayMemberPresent(v, "bufferViews")) {
6029  if (err) {
6030  (*err) += "\"bufferViews\" object not found in .gltf\n";
6031  }
6032  return false;
6033  }
6034  }
6035 
6036  model->buffers.clear();
6037  model->bufferViews.clear();
6038  model->accessors.clear();
6039  model->meshes.clear();
6040  model->cameras.clear();
6041  model->nodes.clear();
6042  model->extensionsUsed.clear();
6043  model->extensionsRequired.clear();
6044  model->extensions.clear();
6045  model->defaultScene = -1;
6046 
6047  // 1. Parse Asset
6048  {
6049  detail::json_const_iterator it;
6050  if (detail::FindMember(v, "asset", it) &&
6051  detail::IsObject(detail::GetValue(it))) {
6052  const detail::json &root = detail::GetValue(it);
6053 
6054  ParseAsset(&model->asset, err, root,
6055  store_original_json_for_extras_and_extensions_);
6056  }
6057  }
6058 
6059  using detail::ForEachInArray;
6060 
6061  // 2. Parse extensionUsed
6062  {
6063  ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
6064  std::string str;
6065  detail::GetString(o, str);
6066  model->extensionsUsed.emplace_back(std::move(str));
6067  return true;
6068  });
6069  }
6070 
6071  {
6072  ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
6073  std::string str;
6074  detail::GetString(o, str);
6075  model->extensionsRequired.emplace_back(std::move(str));
6076  return true;
6077  });
6078  }
6079 
6080  // 3. Parse Buffer
6081  {
6082  bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) {
6083  if (!detail::IsObject(o)) {
6084  if (err) {
6085  (*err) += "`buffers' does not contain an JSON object.";
6086  }
6087  return false;
6088  }
6089  Buffer buffer;
6090  if (!ParseBuffer(&buffer, err, o,
6091  store_original_json_for_extras_and_extensions_, &fs,
6092  &uri_cb, base_dir, max_external_file_size_, is_binary_,
6093  bin_data_, bin_size_)) {
6094  return false;
6095  }
6096 
6097  model->buffers.emplace_back(std::move(buffer));
6098  return true;
6099  });
6100 
6101  if (!success) {
6102  return false;
6103  }
6104  }
6105  // 4. Parse BufferView
6106  {
6107  bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) {
6108  if (!detail::IsObject(o)) {
6109  if (err) {
6110  (*err) += "`bufferViews' does not contain an JSON object.";
6111  }
6112  return false;
6113  }
6114  BufferView bufferView;
6115  if (!ParseBufferView(&bufferView, err, o,
6116  store_original_json_for_extras_and_extensions_)) {
6117  return false;
6118  }
6119 
6120  model->bufferViews.emplace_back(std::move(bufferView));
6121  return true;
6122  });
6123 
6124  if (!success) {
6125  return false;
6126  }
6127  }
6128 
6129  // 5. Parse Accessor
6130  {
6131  bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) {
6132  if (!detail::IsObject(o)) {
6133  if (err) {
6134  (*err) += "`accessors' does not contain an JSON object.";
6135  }
6136  return false;
6137  }
6138  Accessor accessor;
6139  if (!ParseAccessor(&accessor, err, o,
6140  store_original_json_for_extras_and_extensions_)) {
6141  return false;
6142  }
6143 
6144  model->accessors.emplace_back(std::move(accessor));
6145  return true;
6146  });
6147 
6148  if (!success) {
6149  return false;
6150  }
6151  }
6152 
6153  // 6. Parse Mesh
6154  {
6155  bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
6156  if (!detail::IsObject(o)) {
6157  if (err) {
6158  (*err) += "`meshes' does not contain an JSON object.";
6159  }
6160  return false;
6161  }
6162  Mesh mesh;
6163  if (!ParseMesh(&mesh, model, err, warn, o,
6164  store_original_json_for_extras_and_extensions_,
6165  strictness_)) {
6166  return false;
6167  }
6168 
6169  model->meshes.emplace_back(std::move(mesh));
6170  return true;
6171  });
6172 
6173  if (!success) {
6174  return false;
6175  }
6176  }
6177 
6178  // Assign missing bufferView target types
6179  // - Look for missing Mesh indices
6180  // - Look for missing Mesh attributes
6181  for (auto &mesh : model->meshes) {
6182  for (auto &primitive : mesh.primitives) {
6183  if (primitive.indices >
6184  -1) // has indices from parsing step, must be Element Array Buffer
6185  {
6186  if (size_t(primitive.indices) >= model->accessors.size()) {
6187  if (err) {
6188  (*err) += "primitive indices accessor out of bounds";
6189  }
6190  return false;
6191  }
6192 
6193  const auto bufferView =
6194  model->accessors[size_t(primitive.indices)].bufferView;
6195  if (bufferView < 0) {
6196  // skip, bufferView could be null(-1) for certain extensions
6197  } else if (size_t(bufferView) >= model->bufferViews.size()) {
6198  if (err) {
6199  (*err) += "accessor[" + std::to_string(primitive.indices) +
6200  "] invalid bufferView";
6201  }
6202  return false;
6203  } else {
6204  model->bufferViews[size_t(bufferView)].target =
6206  // we could optionally check if accessors' bufferView type is Scalar, as
6207  // it should be
6208  }
6209  }
6210 
6211  for (auto &attribute : primitive.attributes) {
6212  const auto accessorsIndex = size_t(attribute.second);
6213  if (accessorsIndex < model->accessors.size()) {
6214  const auto bufferView = model->accessors[accessorsIndex].bufferView;
6215  // bufferView could be null(-1) for sparse morph target
6216  if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
6217  model->bufferViews[size_t(bufferView)].target =
6219  }
6220  }
6221  }
6222 
6223  for (auto &target : primitive.targets) {
6224  for (auto &attribute : target) {
6225  const auto accessorsIndex = size_t(attribute.second);
6226  if (accessorsIndex < model->accessors.size()) {
6227  const auto bufferView = model->accessors[accessorsIndex].bufferView;
6228  // bufferView could be null(-1) for sparse morph target
6229  if (bufferView >= 0 &&
6230  bufferView < (int)model->bufferViews.size()) {
6231  model->bufferViews[size_t(bufferView)].target =
6233  }
6234  }
6235  }
6236  }
6237  }
6238  }
6239 
6240  // 7. Parse Node
6241  {
6242  bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
6243  if (!detail::IsObject(o)) {
6244  if (err) {
6245  (*err) += "`nodes' does not contain an JSON object.";
6246  }
6247  return false;
6248  }
6249  Node node;
6250  if (!ParseNode(&node, err, o,
6251  store_original_json_for_extras_and_extensions_)) {
6252  return false;
6253  }
6254 
6255  model->nodes.emplace_back(std::move(node));
6256  return true;
6257  });
6258 
6259  if (!success) {
6260  return false;
6261  }
6262  }
6263 
6264  // 8. Parse scenes.
6265  {
6266  bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
6267  if (!detail::IsObject(o)) {
6268  if (err) {
6269  (*err) += "`scenes' does not contain an JSON object.";
6270  }
6271  return false;
6272  }
6273 
6274  Scene scene;
6275  if (!ParseScene(&scene, err, o,
6276  store_original_json_for_extras_and_extensions_)) {
6277  return false;
6278  }
6279 
6280  model->scenes.emplace_back(std::move(scene));
6281  return true;
6282  });
6283 
6284  if (!success) {
6285  return false;
6286  }
6287  }
6288 
6289  // 9. Parse default scenes.
6290  {
6291  detail::json_const_iterator rootIt;
6292  int iVal;
6293  if (detail::FindMember(v, "scene", rootIt) &&
6294  detail::GetInt(detail::GetValue(rootIt), iVal)) {
6295  model->defaultScene = iVal;
6296  }
6297  }
6298 
6299  // 10. Parse Material
6300  {
6301  bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
6302  if (!detail::IsObject(o)) {
6303  if (err) {
6304  (*err) += "`materials' does not contain an JSON object.";
6305  }
6306  return false;
6307  }
6308  Material material;
6309  ParseStringProperty(&material.name, err, o, "name", false);
6310 
6311  if (!ParseMaterial(&material, err, warn, o,
6312  store_original_json_for_extras_and_extensions_,
6313  strictness_)) {
6314  return false;
6315  }
6316 
6317  model->materials.emplace_back(std::move(material));
6318  return true;
6319  });
6320 
6321  if (!success) {
6322  return false;
6323  }
6324  }
6325 
6326  // 11. Parse Image
6327  void *load_image_user_data{nullptr};
6328 
6329  LoadImageDataOption load_image_option;
6330 
6331  if (user_image_loader_) {
6332  // Use user supplied pointer
6333  load_image_user_data = load_image_user_data_;
6334  } else {
6335  load_image_option.preserve_channels = preserve_image_channels_;
6336  load_image_user_data = reinterpret_cast<void *>(&load_image_option);
6337  }
6338 
6339  {
6340  int idx = 0;
6341  bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
6342  if (!detail::IsObject(o)) {
6343  if (err) {
6344  (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
6345  }
6346  return false;
6347  }
6348  Image image;
6349  if (!ParseImage(&image, idx, err, warn, o,
6350  store_original_json_for_extras_and_extensions_, base_dir,
6351  max_external_file_size_, &fs, &uri_cb,
6352  &this->LoadImageData, load_image_user_data)) {
6353  return false;
6354  }
6355 
6356  if (image.bufferView != -1) {
6357  // Load image from the buffer view.
6358  if (size_t(image.bufferView) >= model->bufferViews.size()) {
6359  if (err) {
6360  std::stringstream ss;
6361  ss << "image[" << idx << "] bufferView \"" << image.bufferView
6362  << "\" not found in the scene." << std::endl;
6363  (*err) += ss.str();
6364  }
6365  return false;
6366  }
6367 
6368  const BufferView &bufferView =
6369  model->bufferViews[size_t(image.bufferView)];
6370  if (size_t(bufferView.buffer) >= model->buffers.size()) {
6371  if (err) {
6372  std::stringstream ss;
6373  ss << "image[" << idx << "] buffer \"" << bufferView.buffer
6374  << "\" not found in the scene." << std::endl;
6375  (*err) += ss.str();
6376  }
6377  return false;
6378  }
6379  const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6380 
6381  if (*LoadImageData == nullptr) {
6382  if (err) {
6383  (*err) += "No LoadImageData callback specified.\n";
6384  }
6385  return false;
6386  }
6387  bool ret = LoadImageData(
6388  &image, idx, err, warn, image.width, image.height,
6389  &buffer.data[bufferView.byteOffset],
6390  static_cast<int>(bufferView.byteLength), load_image_user_data);
6391  if (!ret) {
6392  return false;
6393  }
6394  }
6395 
6396  model->images.emplace_back(std::move(image));
6397  ++idx;
6398  return true;
6399  });
6400 
6401  if (!success) {
6402  return false;
6403  }
6404  }
6405 
6406  // 12. Parse Texture
6407  {
6408  bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
6409  if (!detail::IsObject(o)) {
6410  if (err) {
6411  (*err) += "`textures' does not contain an JSON object.";
6412  }
6413  return false;
6414  }
6415  Texture texture;
6416  if (!ParseTexture(&texture, err, o,
6417  store_original_json_for_extras_and_extensions_,
6418  base_dir)) {
6419  return false;
6420  }
6421 
6422  model->textures.emplace_back(std::move(texture));
6423  return true;
6424  });
6425 
6426  if (!success) {
6427  return false;
6428  }
6429  }
6430 
6431  // 13. Parse Animation
6432  {
6433  bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
6434  if (!detail::IsObject(o)) {
6435  if (err) {
6436  (*err) += "`animations' does not contain an JSON object.";
6437  }
6438  return false;
6439  }
6440  Animation animation;
6441  if (!ParseAnimation(&animation, err, o,
6442  store_original_json_for_extras_and_extensions_)) {
6443  return false;
6444  }
6445 
6446  model->animations.emplace_back(std::move(animation));
6447  return true;
6448  });
6449 
6450  if (!success) {
6451  return false;
6452  }
6453  }
6454 
6455  // 14. Parse Skin
6456  {
6457  bool success = ForEachInArray(v, "skins", [&](const detail::json &o) {
6458  if (!detail::IsObject(o)) {
6459  if (err) {
6460  (*err) += "`skins' does not contain an JSON object.";
6461  }
6462  return false;
6463  }
6464  Skin skin;
6465  if (!ParseSkin(&skin, err, o,
6466  store_original_json_for_extras_and_extensions_)) {
6467  return false;
6468  }
6469 
6470  model->skins.emplace_back(std::move(skin));
6471  return true;
6472  });
6473 
6474  if (!success) {
6475  return false;
6476  }
6477  }
6478 
6479  // 15. Parse Sampler
6480  {
6481  bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
6482  if (!detail::IsObject(o)) {
6483  if (err) {
6484  (*err) += "`samplers' does not contain an JSON object.";
6485  }
6486  return false;
6487  }
6488  Sampler sampler;
6489  if (!ParseSampler(&sampler, err, o,
6490  store_original_json_for_extras_and_extensions_)) {
6491  return false;
6492  }
6493 
6494  model->samplers.emplace_back(std::move(sampler));
6495  return true;
6496  });
6497 
6498  if (!success) {
6499  return false;
6500  }
6501  }
6502 
6503  // 16. Parse Camera
6504  {
6505  bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
6506  if (!detail::IsObject(o)) {
6507  if (err) {
6508  (*err) += "`cameras' does not contain an JSON object.";
6509  }
6510  return false;
6511  }
6512  Camera camera;
6513  if (!ParseCamera(&camera, err, o,
6514  store_original_json_for_extras_and_extensions_)) {
6515  return false;
6516  }
6517 
6518  model->cameras.emplace_back(std::move(camera));
6519  return true;
6520  });
6521 
6522  if (!success) {
6523  return false;
6524  }
6525  }
6526 
6527  // 17. Parse Extras & Extensions
6528  ParseExtrasAndExtensions(model, err, v,
6529  store_original_json_for_extras_and_extensions_);
6530 
6531  // 18. Specific extension implementations
6532  {
6533  detail::json_const_iterator rootIt;
6534  if (detail::FindMember(v, "extensions", rootIt) &&
6535  detail::IsObject(detail::GetValue(rootIt))) {
6536  const detail::json &root = detail::GetValue(rootIt);
6537 
6538  detail::json_const_iterator it(detail::ObjectBegin(root));
6539  detail::json_const_iterator itEnd(detail::ObjectEnd(root));
6540  for (; it != itEnd; ++it) {
6541  // parse KHR_lights_punctual extension
6542  std::string key(detail::GetKey(it));
6543  if ((key == "KHR_lights_punctual") &&
6544  detail::IsObject(detail::GetValue(it))) {
6545  const detail::json &object = detail::GetValue(it);
6546  detail::json_const_iterator itLight;
6547  if (detail::FindMember(object, "lights", itLight)) {
6548  const detail::json &lights = detail::GetValue(itLight);
6549  if (!detail::IsArray(lights)) {
6550  continue;
6551  }
6552 
6553  auto arrayIt(detail::ArrayBegin(lights));
6554  auto arrayItEnd(detail::ArrayEnd(lights));
6555  for (; arrayIt != arrayItEnd; ++arrayIt) {
6556  Light light;
6557  if (!ParseLight(&light, err, *arrayIt,
6558  store_original_json_for_extras_and_extensions_)) {
6559  return false;
6560  }
6561  model->lights.emplace_back(std::move(light));
6562  }
6563  }
6564  }
6565  // parse KHR_audio extension
6566  if ((key == "KHR_audio") && detail::IsObject(detail::GetValue(it))) {
6567  const detail::json &object = detail::GetValue(it);
6568  detail::json_const_iterator itKhrAudio;
6569  if (detail::FindMember(object, "emitters", itKhrAudio)) {
6570  const detail::json &emitters = detail::GetValue(itKhrAudio);
6571  if (!detail::IsArray(emitters)) {
6572  continue;
6573  }
6574 
6575  auto arrayIt(detail::ArrayBegin(emitters));
6576  auto arrayItEnd(detail::ArrayEnd(emitters));
6577  for (; arrayIt != arrayItEnd; ++arrayIt) {
6578  AudioEmitter emitter;
6579  if (!ParseAudioEmitter(
6580  &emitter, err, *arrayIt,
6581  store_original_json_for_extras_and_extensions_)) {
6582  return false;
6583  }
6584  model->audioEmitters.emplace_back(std::move(emitter));
6585  }
6586  }
6587 
6588  if (detail::FindMember(object, "sources", itKhrAudio)) {
6589  const detail::json &sources = detail::GetValue(itKhrAudio);
6590  if (!detail::IsArray(sources)) {
6591  continue;
6592  }
6593 
6594  auto arrayIt(detail::ArrayBegin(sources));
6595  auto arrayItEnd(detail::ArrayEnd(sources));
6596  for (; arrayIt != arrayItEnd; ++arrayIt) {
6597  AudioSource source;
6598  if (!ParseAudioSource(
6599  &source, err, *arrayIt,
6600  store_original_json_for_extras_and_extensions_)) {
6601  return false;
6602  }
6603  model->audioSources.emplace_back(std::move(source));
6604  }
6605  }
6606  }
6607  }
6608  }
6609  }
6610 
6611  return true;
6612 }
6613 
6614 bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
6615  std::string *warn, const char *str,
6616  unsigned int length,
6617  const std::string &base_dir,
6618  unsigned int check_sections) {
6619  is_binary_ = false;
6620  bin_data_ = nullptr;
6621  bin_size_ = 0;
6622 
6623  return LoadFromString(model, err, warn, str, length, base_dir,
6624  check_sections);
6625 }
6626 
6627 bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
6628  std::string *warn, const std::string &filename,
6629  unsigned int check_sections) {
6630  std::stringstream ss;
6631 
6632  if (fs.ReadWholeFile == nullptr) {
6633  // Programmer error, assert() ?
6634  ss << "Failed to read file: " << filename
6635  << ": one or more FS callback not set" << std::endl;
6636  if (err) {
6637  (*err) = ss.str();
6638  }
6639  return false;
6640  }
6641 
6642  std::vector<unsigned char> data;
6643  std::string fileerr;
6644  bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
6645  if (!fileread) {
6646  ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6647  if (err) {
6648  (*err) = ss.str();
6649  }
6650  return false;
6651  }
6652 
6653  size_t sz = data.size();
6654  if (sz == 0) {
6655  if (err) {
6656  (*err) = "Empty file.";
6657  }
6658  return false;
6659  }
6660 
6661  std::string basedir = GetBaseDir(filename);
6662 
6663  bool ret = LoadASCIIFromString(
6664  model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6665  static_cast<unsigned int>(data.size()), basedir, check_sections);
6666 
6667  return ret;
6668 }
6669 
6670 bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
6671  std::string *warn,
6672  const unsigned char *bytes,
6673  unsigned int size,
6674  const std::string &base_dir,
6675  unsigned int check_sections) {
6676  if (size < 20) {
6677  if (err) {
6678  (*err) = "Too short data size for glTF Binary.";
6679  }
6680  return false;
6681  }
6682 
6683  if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6684  bytes[3] == 'F') {
6685  // ok
6686  } else {
6687  if (err) {
6688  (*err) = "Invalid magic.";
6689  }
6690  return false;
6691  }
6692 
6693  unsigned int version; // 4 bytes
6694  unsigned int length; // 4 bytes
6695  unsigned int chunk0_length; // 4 bytes
6696  unsigned int chunk0_format; // 4 bytes;
6697 
6698  memcpy(&version, bytes + 4, 4);
6699  swap4(&version);
6700  memcpy(&length, bytes + 8, 4); // Total glb size, including header and all chunks.
6701  swap4(&length);
6702  memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6703  swap4(&chunk0_length);
6704  memcpy(&chunk0_format, bytes + 16, 4);
6705  swap4(&chunk0_format);
6706 
6707  // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6708  //
6709  // In case the Bin buffer is not present, the size is exactly 20 + size of
6710  // JSON contents,
6711  // so use "greater than" operator.
6712  //
6713  // https://github.com/syoyo/tinygltf/issues/372
6714  // Use 64bit uint to avoid integer overflow.
6715  uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
6716 
6717  if (header_and_json_size > (std::numeric_limits<uint32_t>::max)()) {
6718  // Do not allow 4GB or more GLB data.
6719  if (err) {
6720  (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6721  }
6722  return false;
6723  }
6724 
6725  if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) ||
6726  (length > size) || (header_and_json_size > uint64_t(length)) ||
6727  (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
6728  if (err) {
6729  (*err) = "Invalid glTF binary.";
6730  }
6731  return false;
6732  }
6733 
6734  // Padding check
6735  // The start and the end of each chunk must be aligned to a 4-byte boundary.
6736  // No padding check for chunk0 start since its 4byte-boundary is ensured.
6737  if ((header_and_json_size % 4) != 0) {
6738  if (err) {
6739  (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6740  }
6741  return false;
6742  }
6743 
6744  // std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6745  // std::cout << "length = " << length << "\n";
6746 
6747  // Chunk1(BIN) data
6748  // The spec says: When the binary buffer is empty or when it is stored by
6749  // other means, this chunk SHOULD be omitted. So when header + JSON data ==
6750  // binary size, Chunk1 is omitted.
6751  if (header_and_json_size == uint64_t(length)) {
6752  bin_data_ = nullptr;
6753  bin_size_ = 0;
6754  } else {
6755  // Read Chunk1 info(BIN data)
6756  //
6757  // issue-440:
6758  // 'SHOULD' in glTF spec means 'RECOMMENDED',
6759  // So there is a situation that Chunk1(BIN) is composed of zero-sized BIN data
6760  // (chunksize(0) + binformat(BIN) = 8bytes).
6761  //
6762  if ((header_and_json_size + 8ull) > uint64_t(length)) {
6763  if (err) {
6764  (*err) =
6765  "Insufficient storage space for Chunk1(BIN data). At least Chunk1 "
6766  "Must have 8 or more bytes, but got " +
6767  std::to_string((header_and_json_size + 8ull) - uint64_t(length)) +
6768  ".\n";
6769  }
6770  return false;
6771  }
6772 
6773  unsigned int chunk1_length{0}; // 4 bytes
6774  unsigned int chunk1_format{0}; // 4 bytes;
6775  memcpy(&chunk1_length, bytes + header_and_json_size,
6776  4); // Bin data length
6777  swap4(&chunk1_length);
6778  memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6779  swap4(&chunk1_format);
6780 
6781  if (chunk1_format != 0x004e4942) {
6782  if (err) {
6783  (*err) = "Invalid chunkType for Chunk1.";
6784  }
6785  return false;
6786  }
6787 
6788  if (chunk1_length == 0) {
6789 
6790  if (header_and_json_size + 8 > uint64_t(length)) {
6791  if (err) {
6792  (*err) = "BIN Chunk header location exceeds the GLB size.";
6793  }
6794  return false;
6795  }
6796 
6797  bin_data_ = nullptr;
6798 
6799  } else {
6800 
6801  // When BIN chunk size is not zero, at least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin
6802  // payload could be 1~3 bytes, but need to be aligned to 4 bytes)
6803 
6804  if (chunk1_length < 4) {
6805  if (err) {
6806  (*err) = "Insufficient Chunk1(BIN) data size.";
6807  }
6808  return false;
6809  }
6810 
6811  if ((chunk1_length % 4) != 0) {
6812  if (strictness_==ParseStrictness::Permissive) {
6813  if (warn) {
6814  (*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n";
6815  }
6816  }
6817  else {
6818  if (err) {
6819  (*err) = "BIN Chunk end is not aligned to a 4-byte boundary.";
6820  }
6821  return false;
6822  }
6823  }
6824 
6825  // +8 chunk1 header size.
6826  if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) {
6827  if (err) {
6828  (*err) = "BIN Chunk data length exceeds the GLB size.";
6829  }
6830  return false;
6831  }
6832 
6833  bin_data_ = bytes + header_and_json_size +
6834  8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6835  }
6836 
6837  bin_size_ = size_t(chunk1_length);
6838  }
6839 
6840  is_binary_ = true;
6841 
6842  bool ret = LoadFromString(model, err, warn,
6843  reinterpret_cast<const char *>(&bytes[20]),
6844  chunk0_length, base_dir, check_sections);
6845  if (!ret) {
6846  return ret;
6847  }
6848 
6849  return true;
6850 }
6851 
6852 bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
6853  std::string *warn,
6854  const std::string &filename,
6855  unsigned int check_sections) {
6856  std::stringstream ss;
6857 
6858  if (fs.ReadWholeFile == nullptr) {
6859  // Programmer error, assert() ?
6860  ss << "Failed to read file: " << filename
6861  << ": one or more FS callback not set" << std::endl;
6862  if (err) {
6863  (*err) = ss.str();
6864  }
6865  return false;
6866  }
6867 
6868  std::vector<unsigned char> data;
6869  std::string fileerr;
6870  bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
6871  if (!fileread) {
6872  ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6873  if (err) {
6874  (*err) = ss.str();
6875  }
6876  return false;
6877  }
6878 
6879  std::string basedir = GetBaseDir(filename);
6880 
6881  bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6882  static_cast<unsigned int>(data.size()),
6883  basedir, check_sections);
6884 
6885  return ret;
6886 }
6887 
6889 // GLTF Serialization
6891 namespace detail {
6892 detail::json JsonFromString(const char *s) {
6893 #ifdef TINYGLTF_USE_RAPIDJSON
6894  return detail::json(s, detail::GetAllocator());
6895 #else
6896  return detail::json(s);
6897 #endif
6898 }
6899 
6900 void JsonAssign(detail::json &dest, const detail::json &src) {
6901 #ifdef TINYGLTF_USE_RAPIDJSON
6902  dest.CopyFrom(src, detail::GetAllocator());
6903 #else
6904  dest = src;
6905 #endif
6906 }
6907 
6908 void JsonAddMember(detail::json &o, const char *key, detail::json &&value) {
6909 #ifdef TINYGLTF_USE_RAPIDJSON
6910  if (!o.IsObject()) {
6911  o.SetObject();
6912  }
6913 
6914  // Issue 420.
6915  // AddMember may create duplicated key, so use [] API when a key already
6916  // exists.
6917  // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863
6918  detail::json_const_iterator it;
6919  if (detail::FindMember(o, key, it)) {
6920  o[key] = std::move(value); // replace
6921  } else {
6922  o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value),
6923  detail::GetAllocator());
6924  }
6925 #else
6926  o[key] = std::move(value);
6927 #endif
6928 }
6929 
6930 void JsonPushBack(detail::json &o, detail::json &&value) {
6931 #ifdef TINYGLTF_USE_RAPIDJSON
6932  o.PushBack(std::move(value), detail::GetAllocator());
6933 #else
6934  o.push_back(std::move(value));
6935 #endif
6936 }
6937 
6938 bool JsonIsNull(const detail::json &o) {
6939 #ifdef TINYGLTF_USE_RAPIDJSON
6940  return o.IsNull();
6941 #else
6942  return o.is_null();
6943 #endif
6944 }
6945 
6946 void JsonSetObject(detail::json &o) {
6947 #ifdef TINYGLTF_USE_RAPIDJSON
6948  o.SetObject();
6949 #else
6950  o = o.object({});
6951 #endif
6952 }
6953 
6954 void JsonReserveArray(detail::json &o, size_t s) {
6955 #ifdef TINYGLTF_USE_RAPIDJSON
6956  o.SetArray();
6957  o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator());
6958 #endif
6959  (void)(o);
6960  (void)(s);
6961 }
6962 } // namespace detail
6963 
6964 // typedef std::pair<std::string, detail::json> json_object_pair;
6965 
6966 template <typename T>
6967 static void SerializeNumberProperty(const std::string &key, T number,
6968  detail::json &obj) {
6969  // obj.insert(
6970  // json_object_pair(key, detail::json(static_cast<double>(number))));
6971  // obj[key] = static_cast<double>(number);
6972  detail::JsonAddMember(obj, key.c_str(), detail::json(number));
6973 }
6974 
6975 #ifdef TINYGLTF_USE_RAPIDJSON
6976 template <>
6977 void SerializeNumberProperty(const std::string &key, size_t number,
6978  detail::json &obj) {
6979  detail::JsonAddMember(obj, key.c_str(),
6980  detail::json(static_cast<uint64_t>(number)));
6981 }
6982 #endif
6983 
6984 template <typename T>
6985 static void SerializeNumberArrayProperty(const std::string &key,
6986  const std::vector<T> &value,
6987  detail::json &obj) {
6988  if (value.empty()) return;
6989 
6990  detail::json ary;
6991  detail::JsonReserveArray(ary, value.size());
6992  for (const auto &s : value) {
6993  detail::JsonPushBack(ary, detail::json(s));
6994  }
6995  detail::JsonAddMember(obj, key.c_str(), std::move(ary));
6996 }
6997 
6998 static void SerializeStringProperty(const std::string &key,
6999  const std::string &value,
7000  detail::json &obj) {
7001  detail::JsonAddMember(obj, key.c_str(),
7002  detail::JsonFromString(value.c_str()));
7003 }
7004 
7005 static void SerializeStringArrayProperty(const std::string &key,
7006  const std::vector<std::string> &value,
7007  detail::json &obj) {
7008  detail::json ary;
7009  detail::JsonReserveArray(ary, value.size());
7010  for (auto &s : value) {
7011  detail::JsonPushBack(ary, detail::JsonFromString(s.c_str()));
7012  }
7013  detail::JsonAddMember(obj, key.c_str(), std::move(ary));
7014 }
7015 
7016 static bool ValueToJson(const Value &value, detail::json *ret) {
7017  detail::json obj;
7018 #ifdef TINYGLTF_USE_RAPIDJSON
7019  switch (value.Type()) {
7020  case REAL_TYPE:
7021  obj.SetDouble(value.Get<double>());
7022  break;
7023  case INT_TYPE:
7024  obj.SetInt(value.Get<int>());
7025  break;
7026  case BOOL_TYPE:
7027  obj.SetBool(value.Get<bool>());
7028  break;
7029  case STRING_TYPE:
7030  obj.SetString(value.Get<std::string>().c_str(), detail::GetAllocator());
7031  break;
7032  case ARRAY_TYPE: {
7033  obj.SetArray();
7034  obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
7035  detail::GetAllocator());
7036  for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
7037  Value elementValue = value.Get(int(i));
7038  detail::json elementJson;
7039  if (ValueToJson(value.Get(int(i)), &elementJson))
7040  obj.PushBack(std::move(elementJson), detail::GetAllocator());
7041  }
7042  break;
7043  }
7044  case BINARY_TYPE:
7045  // TODO
7046  // obj = detail::json(value.Get<std::vector<unsigned char>>());
7047  return false;
7048  break;
7049  case OBJECT_TYPE: {
7050  obj.SetObject();
7051  Value::Object objMap = value.Get<Value::Object>();
7052  for (auto &it : objMap) {
7053  detail::json elementJson;
7054  if (ValueToJson(it.second, &elementJson)) {
7055  obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()),
7056  std::move(elementJson), detail::GetAllocator());
7057  }
7058  }
7059  break;
7060  }
7061  case NULL_TYPE:
7062  default:
7063  return false;
7064  }
7065 #else
7066  switch (value.Type()) {
7067  case REAL_TYPE:
7068  obj = detail::json(value.Get<double>());
7069  break;
7070  case INT_TYPE:
7071  obj = detail::json(value.Get<int>());
7072  break;
7073  case BOOL_TYPE:
7074  obj = detail::json(value.Get<bool>());
7075  break;
7076  case STRING_TYPE:
7077  obj = detail::json(value.Get<std::string>());
7078  break;
7079  case ARRAY_TYPE: {
7080  for (size_t i = 0; i < value.ArrayLen(); ++i) {
7081  Value elementValue = value.Get(i);
7082  detail::json elementJson;
7083  if (ValueToJson(value.Get(i), &elementJson))
7084  obj.push_back(elementJson);
7085  }
7086  break;
7087  }
7088  case BINARY_TYPE:
7089  // TODO
7090  // obj = json(value.Get<std::vector<unsigned char>>());
7091  return false;
7092  break;
7093  case OBJECT_TYPE: {
7094  Value::Object objMap = value.Get<Value::Object>();
7095  for (auto &it : objMap) {
7096  detail::json elementJson;
7097  if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
7098  }
7099  break;
7100  }
7101  case NULL_TYPE:
7102  default:
7103  return false;
7104  }
7105 #endif
7106  if (ret) *ret = std::move(obj);
7107  return true;
7108 }
7109 
7110 static void SerializeValue(const std::string &key, const Value &value,
7111  detail::json &obj) {
7112  detail::json ret;
7113  if (ValueToJson(value, &ret)) {
7114  detail::JsonAddMember(obj, key.c_str(), std::move(ret));
7115  }
7116 }
7117 
7118 static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
7119  detail::json &o) {
7120  std::string header = "data:application/octet-stream;base64,";
7121  if (data.size() > 0) {
7122  std::string encodedData =
7123  base64_encode(&data[0], static_cast<unsigned int>(data.size()));
7124  SerializeStringProperty("uri", header + encodedData, o);
7125  } else {
7126  // Issue #229
7127  // size 0 is allowed. Just emit mime header.
7128  SerializeStringProperty("uri", header, o);
7129  }
7130 }
7131 
7132 static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
7133  const std::string &binFilename) {
7134 #ifdef _WIN32
7135 #if defined(__GLIBCXX__) // mingw
7136  int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
7137  _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
7138  __gnu_cxx::stdio_filebuf<char> wfile_buf(
7139  file_descriptor, std::ios_base::out | std::ios_base::binary);
7140  std::ostream output(&wfile_buf);
7141  if (!wfile_buf.is_open()) return false;
7142 #elif defined(_MSC_VER)
7143  std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
7144  if (!output.is_open()) return false;
7145 #else
7146  std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7147  if (!output.is_open()) return false;
7148 #endif
7149 #else
7150  std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7151  if (!output.is_open()) return false;
7152 #endif
7153  if (data.size() > 0) {
7154  output.write(reinterpret_cast<const char *>(&data[0]),
7155  std::streamsize(data.size()));
7156  } else {
7157  // Issue #229
7158  // size 0 will be still valid buffer data.
7159  // write empty file.
7160  }
7161  return true;
7162 }
7163 
7164 #if 0 // FIXME(syoyo): not used. will be removed in the future release.
7165 static void SerializeParameterMap(ParameterMap &param, detail::json &o) {
7166  for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
7167  ++paramIt) {
7168  if (paramIt->second.number_array.size()) {
7169  SerializeNumberArrayProperty<double>(paramIt->first,
7170  paramIt->second.number_array, o);
7171  } else if (paramIt->second.json_double_value.size()) {
7172  detail::json json_double_value;
7173  for (std::map<std::string, double>::iterator it =
7174  paramIt->second.json_double_value.begin();
7175  it != paramIt->second.json_double_value.end(); ++it) {
7176  if (it->first == "index") {
7177  json_double_value[it->first] = paramIt->second.TextureIndex();
7178  } else {
7179  json_double_value[it->first] = it->second;
7180  }
7181  }
7182 
7183  o[paramIt->first] = json_double_value;
7184  } else if (!paramIt->second.string_value.empty()) {
7185  SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
7186  } else if (paramIt->second.has_number_value) {
7187  o[paramIt->first] = paramIt->second.number_value;
7188  } else {
7189  o[paramIt->first] = paramIt->second.bool_value;
7190  }
7191  }
7192 }
7193 #endif
7194 
7195 static void SerializeExtensionMap(const ExtensionMap &extensions,
7196  detail::json &o) {
7197  if (!extensions.size()) return;
7198 
7199  detail::json extMap;
7200  for (ExtensionMap::const_iterator extIt = extensions.begin();
7201  extIt != extensions.end(); ++extIt) {
7202  // Allow an empty object for extension(#97)
7203  detail::json ret;
7204  bool isNull = true;
7205  if (ValueToJson(extIt->second, &ret)) {
7206  isNull = detail::JsonIsNull(ret);
7207  detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
7208  }
7209  if (isNull) {
7210  if (!(extIt->first.empty())) { // name should not be empty, but for sure
7211  // create empty object so that an extension name is still included in
7212  // json.
7213  detail::json empty;
7214  detail::JsonSetObject(empty);
7215  detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
7216  }
7217  }
7218  }
7219  detail::JsonAddMember(o, "extensions", std::move(extMap));
7220 }
7221 
7222 static void SerializeExtras(const Value &extras, detail::json &o) {
7223  if (extras.Type() != NULL_TYPE) SerializeValue("extras", extras, o);
7224 }
7225 
7226 template <typename GltfType>
7227 void SerializeExtrasAndExtensions(const GltfType &obj, detail::json &o) {
7228  SerializeExtensionMap(obj.extensions, o);
7229  SerializeExtras(obj.extras, o);
7230 }
7231 
7232 static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
7233  if (accessor.bufferView >= 0)
7234  SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
7235 
7236  if (accessor.byteOffset != 0)
7237  SerializeNumberProperty<size_t>("byteOffset", accessor.byteOffset, o);
7238 
7239  SerializeNumberProperty<int>("componentType", accessor.componentType, o);
7240  SerializeNumberProperty<size_t>("count", accessor.count, o);
7241 
7242  if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
7243  (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
7244  SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
7245  SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
7246  } else {
7247  // Issue #301. Serialize as integer.
7248  // Assume int value is within [-2**31-1, 2**31-1]
7249  {
7250  std::vector<int> values;
7251  std::transform(accessor.minValues.begin(), accessor.minValues.end(),
7252  std::back_inserter(values),
7253  [](double v) { return static_cast<int>(v); });
7254 
7255  SerializeNumberArrayProperty<int>("min", values, o);
7256  }
7257 
7258  {
7259  std::vector<int> values;
7260  std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
7261  std::back_inserter(values),
7262  [](double v) { return static_cast<int>(v); });
7263 
7264  SerializeNumberArrayProperty<int>("max", values, o);
7265  }
7266  }
7267 
7268  if (accessor.normalized)
7269  SerializeValue("normalized", Value(accessor.normalized), o);
7270  std::string type;
7271  switch (accessor.type) {
7272  case TINYGLTF_TYPE_SCALAR:
7273  type = "SCALAR";
7274  break;
7275  case TINYGLTF_TYPE_VEC2:
7276  type = "VEC2";
7277  break;
7278  case TINYGLTF_TYPE_VEC3:
7279  type = "VEC3";
7280  break;
7281  case TINYGLTF_TYPE_VEC4:
7282  type = "VEC4";
7283  break;
7284  case TINYGLTF_TYPE_MAT2:
7285  type = "MAT2";
7286  break;
7287  case TINYGLTF_TYPE_MAT3:
7288  type = "MAT3";
7289  break;
7290  case TINYGLTF_TYPE_MAT4:
7291  type = "MAT4";
7292  break;
7293  }
7294 
7295  SerializeStringProperty("type", type, o);
7296  if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
7297 
7298  SerializeExtrasAndExtensions(accessor, o);
7299 
7300  // sparse
7301  if (accessor.sparse.isSparse) {
7302  detail::json sparse;
7303  SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
7304  {
7305  detail::json indices;
7306  SerializeNumberProperty<int>("bufferView",
7307  accessor.sparse.indices.bufferView, indices);
7308  SerializeNumberProperty<size_t>("byteOffset",
7309  accessor.sparse.indices.byteOffset, indices);
7310  SerializeNumberProperty<int>(
7311  "componentType", accessor.sparse.indices.componentType, indices);
7312  SerializeExtrasAndExtensions(accessor.sparse.indices, indices);
7313  detail::JsonAddMember(sparse, "indices", std::move(indices));
7314  }
7315  {
7316  detail::json values;
7317  SerializeNumberProperty<int>("bufferView",
7318  accessor.sparse.values.bufferView, values);
7319  SerializeNumberProperty<size_t>("byteOffset",
7320  accessor.sparse.values.byteOffset, values);
7321  SerializeExtrasAndExtensions(accessor.sparse.values, values);
7322  detail::JsonAddMember(sparse, "values", std::move(values));
7323  }
7324  SerializeExtrasAndExtensions(accessor.sparse, sparse);
7325  detail::JsonAddMember(o, "sparse", std::move(sparse));
7326  }
7327 }
7328 
7329 static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
7330  detail::json &o) {
7331  SerializeNumberProperty("sampler", channel.sampler, o);
7332  {
7333  detail::json target;
7334 
7335  if (channel.target_node >= 0) {
7336  SerializeNumberProperty("node", channel.target_node, target);
7337  }
7338 
7339  SerializeStringProperty("path", channel.target_path, target);
7340 
7341  SerializeExtensionMap(channel.target_extensions, target);
7342  SerializeExtras(channel.target_extras, target);
7343 
7344  detail::JsonAddMember(o, "target", std::move(target));
7345  }
7346 
7347  SerializeExtrasAndExtensions(channel, o);
7348 }
7349 
7350 static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
7351  detail::json &o) {
7352  SerializeNumberProperty("input", sampler.input, o);
7353  SerializeNumberProperty("output", sampler.output, o);
7354  SerializeStringProperty("interpolation", sampler.interpolation, o);
7355 
7356  SerializeExtrasAndExtensions(sampler, o);
7357 }
7358 
7359 static void SerializeGltfAnimation(const Animation &animation,
7360  detail::json &o) {
7361  if (!animation.name.empty())
7362  SerializeStringProperty("name", animation.name, o);
7363 
7364  {
7365  detail::json channels;
7366  detail::JsonReserveArray(channels, animation.channels.size());
7367  for (unsigned int i = 0; i < animation.channels.size(); ++i) {
7368  detail::json channel;
7369  AnimationChannel gltfChannel = animation.channels[i];
7370  SerializeGltfAnimationChannel(gltfChannel, channel);
7371  detail::JsonPushBack(channels, std::move(channel));
7372  }
7373 
7374  detail::JsonAddMember(o, "channels", std::move(channels));
7375  }
7376 
7377  {
7378  detail::json samplers;
7379  detail::JsonReserveArray(samplers, animation.samplers.size());
7380  for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
7381  detail::json sampler;
7382  AnimationSampler gltfSampler = animation.samplers[i];
7383  SerializeGltfAnimationSampler(gltfSampler, sampler);
7384  detail::JsonPushBack(samplers, std::move(sampler));
7385  }
7386  detail::JsonAddMember(o, "samplers", std::move(samplers));
7387  }
7388 
7389  SerializeExtrasAndExtensions(animation, o);
7390 }
7391 
7392 static void SerializeGltfAsset(const Asset &asset, detail::json &o) {
7393  if (!asset.generator.empty()) {
7394  SerializeStringProperty("generator", asset.generator, o);
7395  }
7396 
7397  if (!asset.copyright.empty()) {
7398  SerializeStringProperty("copyright", asset.copyright, o);
7399  }
7400 
7401  auto version = asset.version;
7402  if (version.empty()) {
7403  // Just in case
7404  // `version` must be defined
7405  version = "2.0";
7406  }
7407 
7408  // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
7409  SerializeStringProperty("version", version, o);
7410 
7411  SerializeExtrasAndExtensions(asset, o);
7412 }
7413 
7414 static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o,
7415  std::vector<unsigned char> &binBuffer) {
7416  SerializeNumberProperty("byteLength", buffer.data.size(), o);
7417  binBuffer = buffer.data;
7418 
7419  if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7420 
7421  SerializeExtrasAndExtensions(buffer, o);
7422 }
7423 
7424 static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) {
7425  SerializeNumberProperty("byteLength", buffer.data.size(), o);
7426  SerializeGltfBufferData(buffer.data, o);
7427 
7428  if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7429 
7430  SerializeExtrasAndExtensions(buffer, o);
7431 }
7432 
7433 static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o,
7434  const std::string &binFilename,
7435  const std::string &binUri) {
7436  if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
7437  SerializeNumberProperty("byteLength", buffer.data.size(), o);
7438  SerializeStringProperty("uri", binUri, o);
7439 
7440  if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7441 
7442  SerializeExtrasAndExtensions(buffer, o);
7443  return true;
7444 }
7445 
7446 static void SerializeGltfBufferView(const BufferView &bufferView,
7447  detail::json &o) {
7448  SerializeNumberProperty("buffer", bufferView.buffer, o);
7449  SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
7450 
7451  // byteStride is optional, minimum allowed is 4
7452  if (bufferView.byteStride >= 4) {
7453  SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
7454  }
7455  // byteOffset is optional, default is 0
7456  if (bufferView.byteOffset > 0) {
7457  SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
7458  }
7459  // Target is optional, check if it contains a valid value
7460  if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
7461  bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
7462  SerializeNumberProperty("target", bufferView.target, o);
7463  }
7464  if (bufferView.name.size()) {
7465  SerializeStringProperty("name", bufferView.name, o);
7466  }
7467 
7468  SerializeExtrasAndExtensions(bufferView, o);
7469 }
7470 
7471 static void SerializeGltfImage(const Image &image, const std::string &uri,
7472  detail::json &o) {
7473  // From 2.7.0, we look for `uri` parameter, not `Image.uri`
7474  // if uri is empty, the mimeType and bufferview should be set
7475  if (uri.empty()) {
7476  SerializeStringProperty("mimeType", image.mimeType, o);
7477  SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7478  } else {
7479  SerializeStringProperty("uri", uri, o);
7480  }
7481 
7482  if (image.name.size()) {
7483  SerializeStringProperty("name", image.name, o);
7484  }
7485 
7486  SerializeExtrasAndExtensions(image, o);
7487 }
7488 
7489 static void SerializeGltfTextureInfo(const TextureInfo &texinfo,
7490  detail::json &o) {
7491  SerializeNumberProperty("index", texinfo.index, o);
7492 
7493  if (texinfo.texCoord != 0) {
7494  SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7495  }
7496 
7497  SerializeExtrasAndExtensions(texinfo, o);
7498 }
7499 
7500 static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
7501  detail::json &o) {
7502  SerializeNumberProperty("index", texinfo.index, o);
7503 
7504  if (texinfo.texCoord != 0) {
7505  SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7506  }
7507 
7508  if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
7509  SerializeNumberProperty("scale", texinfo.scale, o);
7510  }
7511 
7512  SerializeExtrasAndExtensions(texinfo, o);
7513 }
7514 
7515 static void SerializeGltfOcclusionTextureInfo(
7516  const OcclusionTextureInfo &texinfo, detail::json &o) {
7517  SerializeNumberProperty("index", texinfo.index, o);
7518 
7519  if (texinfo.texCoord != 0) {
7520  SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7521  }
7522 
7523  if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
7524  SerializeNumberProperty("strength", texinfo.strength, o);
7525  }
7526 
7527  SerializeExtrasAndExtensions(texinfo, o);
7528 }
7529 
7530 static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
7531  detail::json &o) {
7532  std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7533  if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7534  SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7535  o);
7536  }
7537 
7538  if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
7539  SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7540  }
7541 
7542  if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
7543  SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7544  }
7545 
7546  if (pbr.baseColorTexture.index > -1) {
7547  detail::json texinfo;
7548  SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
7549  detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo));
7550  }
7551 
7552  if (pbr.metallicRoughnessTexture.index > -1) {
7553  detail::json texinfo;
7554  SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
7555  detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
7556  }
7557 
7558  SerializeExtrasAndExtensions(pbr, o);
7559 }
7560 
7561 static void SerializeGltfMaterial(const Material &material, detail::json &o) {
7562  if (material.name.size()) {
7563  SerializeStringProperty("name", material.name, o);
7564  }
7565 
7566  // QUESTION(syoyo): Write material parameters regardless of its default value?
7567 
7568  if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7569  SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7570  }
7571 
7572  if (material.alphaMode.compare("OPAQUE") != 0) {
7573  SerializeStringProperty("alphaMode", material.alphaMode, o);
7574  }
7575 
7576  if (material.doubleSided != false)
7577  detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided));
7578 
7579  if (material.normalTexture.index > -1) {
7580  detail::json texinfo;
7581  SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
7582  detail::JsonAddMember(o, "normalTexture", std::move(texinfo));
7583  }
7584 
7585  if (material.occlusionTexture.index > -1) {
7586  detail::json texinfo;
7587  SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
7588  detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo));
7589  }
7590 
7591  if (material.emissiveTexture.index > -1) {
7592  detail::json texinfo;
7593  SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
7594  detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo));
7595  }
7596 
7597  std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7598  if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7599  SerializeNumberArrayProperty<double>("emissiveFactor",
7600  material.emissiveFactor, o);
7601  }
7602 
7603  {
7604  detail::json pbrMetallicRoughness;
7605  SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7606  pbrMetallicRoughness);
7607  // Issue 204
7608  // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7609  // default values(json is null). Otherwise it will serialize to
7610  // `pbrMetallicRoughness : null`, which cannot be read by other glTF
7611  // importers (and validators).
7612  //
7613  if (!detail::JsonIsNull(pbrMetallicRoughness)) {
7614  detail::JsonAddMember(o, "pbrMetallicRoughness",
7615  std::move(pbrMetallicRoughness));
7616  }
7617  }
7618 
7619 #if 0 // legacy way. just for the record.
7620  if (material.values.size()) {
7621  detail::json pbrMetallicRoughness;
7622  SerializeParameterMap(material.values, pbrMetallicRoughness);
7623  detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7624  }
7625 
7626  SerializeParameterMap(material.additionalValues, o);
7627 #endif
7628 
7629  SerializeExtrasAndExtensions(material, o);
7630 
7631  // MSFT_lod
7632  if (!material.lods.empty()) {
7633  detail::json_iterator it;
7634  if (!detail::FindMember(o, "extensions", it)) {
7635  detail::json extensions;
7636  detail::JsonSetObject(extensions);
7637  detail::JsonAddMember(o, "extensions", std::move(extensions));
7638  detail::FindMember(o, "extensions", it);
7639  }
7640  auto &extensions = detail::GetValue(it);
7641  if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7642  detail::json lod;
7643  detail::JsonSetObject(lod);
7644  detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7645  detail::FindMember(extensions, "MSFT_lod", it);
7646  }
7647  SerializeNumberArrayProperty<int>("ids", material.lods, detail::GetValue(it));
7648  } else {
7649  detail::json_iterator ext_it;
7650  if (detail::FindMember(o, "extensions", ext_it)) {
7651  auto &extensions = detail::GetValue(ext_it);
7652  detail::json_iterator lp_it;
7653  if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7654  detail::Erase(extensions, lp_it);
7655  }
7656  if (detail::IsEmpty(extensions)) {
7657  detail::Erase(o, ext_it);
7658  }
7659  }
7660  }
7661 }
7662 
7663 static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
7664  detail::json primitives;
7665  detail::JsonReserveArray(primitives, mesh.primitives.size());
7666  for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
7667  detail::json primitive;
7668  const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
7669  {
7670  detail::json attributes;
7671  for (auto attrIt = gltfPrimitive.attributes.begin();
7672  attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
7673  SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7674  }
7675 
7676  detail::JsonAddMember(primitive, "attributes", std::move(attributes));
7677  }
7678 
7679  // Indices is optional
7680  if (gltfPrimitive.indices > -1) {
7681  SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7682  }
7683  // Material is optional
7684  if (gltfPrimitive.material > -1) {
7685  SerializeNumberProperty<int>("material", gltfPrimitive.material,
7686  primitive);
7687  }
7688  SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
7689 
7690  // Morph targets
7691  if (gltfPrimitive.targets.size()) {
7692  detail::json targets;
7693  detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
7694  for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
7695  detail::json targetAttributes;
7696  std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7697  for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7698  attrIt != targetData.end(); ++attrIt) {
7699  SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7700  targetAttributes);
7701  }
7702  detail::JsonPushBack(targets, std::move(targetAttributes));
7703  }
7704  detail::JsonAddMember(primitive, "targets", std::move(targets));
7705  }
7706 
7707  SerializeExtrasAndExtensions(gltfPrimitive, primitive);
7708 
7709  detail::JsonPushBack(primitives, std::move(primitive));
7710  }
7711 
7712  detail::JsonAddMember(o, "primitives", std::move(primitives));
7713 
7714  if (mesh.weights.size()) {
7715  SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7716  }
7717 
7718  if (mesh.name.size()) {
7719  SerializeStringProperty("name", mesh.name, o);
7720  }
7721 
7722  SerializeExtrasAndExtensions(mesh, o);
7723 }
7724 
7725 static void SerializeSpotLight(const SpotLight &spot, detail::json &o) {
7726  SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7727  SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7728  SerializeExtrasAndExtensions(spot, o);
7729 }
7730 
7731 static void SerializeGltfLight(const Light &light, detail::json &o) {
7732  if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
7733  SerializeNumberProperty("intensity", light.intensity, o);
7734  if (light.range > 0.0) {
7735  SerializeNumberProperty("range", light.range, o);
7736  }
7737  SerializeNumberArrayProperty("color", light.color, o);
7738  SerializeStringProperty("type", light.type, o);
7739  if (light.type == "spot") {
7740  detail::json spot;
7741  SerializeSpotLight(light.spot, spot);
7742  detail::JsonAddMember(o, "spot", std::move(spot));
7743  }
7744  SerializeExtrasAndExtensions(light, o);
7745 }
7746 
7747 static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional,
7748  detail::json &o) {
7749  if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586))
7750  SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o);
7751  if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586))
7752  SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o);
7753  if (positional.coneOuterGain > 0.0)
7754  SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o);
7755  if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0))
7756  SerializeNumberProperty("maxDistance", positional.maxDistance, o);
7757  if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0))
7758  SerializeNumberProperty("refDistance", positional.refDistance, o);
7759  if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0))
7760  SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o);
7761 
7762  SerializeExtrasAndExtensions(positional, o);
7763 }
7764 
7765 static void SerializeGltfAudioEmitter(const AudioEmitter &emitter,
7766  detail::json &o) {
7767  if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o);
7768  if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0))
7769  SerializeNumberProperty("gain", emitter.gain, o);
7770  if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o);
7771  if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o);
7772  if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o);
7773  if (!emitter.distanceModel.empty())
7774  SerializeStringProperty("distanceModel", emitter.distanceModel, o);
7775  if (emitter.type == "positional") {
7776  detail::json positional;
7777  SerializeGltfPositionalEmitter(emitter.positional, positional);
7778  detail::JsonAddMember(o, "positional", std::move(positional));
7779  }
7780  SerializeNumberProperty("source", emitter.source, o);
7781  SerializeExtrasAndExtensions(emitter, o);
7782 }
7783 
7784 static void SerializeGltfAudioSource(const AudioSource &source,
7785  detail::json &o) {
7786  std::string name;
7787  std::string uri;
7788  std::string mimeType; // (required if no uri) ["audio/mp3", "audio/ogg",
7789  // "audio/wav", "audio/m4a"]
7790 
7791  if (!source.name.empty()) SerializeStringProperty("name", source.name, o);
7792  if (source.uri.empty()) {
7793  SerializeStringProperty("mimeType", source.mimeType, o);
7794  SerializeNumberProperty<int>("bufferView", source.bufferView, o);
7795  } else {
7796  SerializeStringProperty("uri", source.uri, o);
7797  }
7798  SerializeExtrasAndExtensions(source, o);
7799 }
7800 
7801 static void SerializeGltfNode(const Node &node, detail::json &o) {
7802  if (node.translation.size() > 0) {
7803  SerializeNumberArrayProperty<double>("translation", node.translation, o);
7804  }
7805  if (node.rotation.size() > 0) {
7806  SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7807  }
7808  if (node.scale.size() > 0) {
7809  SerializeNumberArrayProperty<double>("scale", node.scale, o);
7810  }
7811  if (node.matrix.size() > 0) {
7812  SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7813  }
7814  if (node.mesh != -1) {
7815  SerializeNumberProperty<int>("mesh", node.mesh, o);
7816  }
7817 
7818  if (node.skin != -1) {
7819  SerializeNumberProperty<int>("skin", node.skin, o);
7820  }
7821 
7822  if (node.camera != -1) {
7823  SerializeNumberProperty<int>("camera", node.camera, o);
7824  }
7825 
7826  if (node.weights.size() > 0) {
7827  SerializeNumberArrayProperty<double>("weights", node.weights, o);
7828  }
7829 
7830  SerializeExtrasAndExtensions(node, o);
7831 
7832  // Note(agnat): If the asset was loaded from disk, the node may already
7833  // contain the KHR_lights_punctual extension. If it was constructed in
7834  // memory it does not. In any case we update the JSON property using
7835  // the value from the struct. Last, if the node does not have a light
7836  // reference but the extension is still present, we remove it.
7837  if (node.light != -1) {
7838  detail::json_iterator it;
7839  if (!detail::FindMember(o, "extensions", it)) {
7840  detail::json extensions;
7841  detail::JsonSetObject(extensions);
7842  detail::JsonAddMember(o, "extensions", std::move(extensions));
7843  detail::FindMember(o, "extensions", it);
7844  }
7845  auto &extensions = detail::GetValue(it);
7846  if (!detail::FindMember(extensions, "KHR_lights_punctual", it)) {
7847  detail::json lights_punctual;
7848  detail::JsonSetObject(lights_punctual);
7849  detail::JsonAddMember(extensions, "KHR_lights_punctual",
7850  std::move(lights_punctual));
7851  detail::FindMember(extensions, "KHR_lights_punctual", it);
7852  }
7853  SerializeNumberProperty("light", node.light, detail::GetValue(it));
7854  } else {
7855  // node has no light ref (any longer)... so we clean up
7856  detail::json_iterator ext_it;
7857  if (detail::FindMember(o, "extensions", ext_it)) {
7858  auto &extensions = detail::GetValue(ext_it);
7859  detail::json_iterator lp_it;
7860  if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) {
7861  detail::Erase(extensions, lp_it);
7862  }
7863  if (detail::IsEmpty(extensions)) {
7864  detail::Erase(o, ext_it);
7865  }
7866  }
7867  }
7868 
7869  // KHR_audio
7870  if (node.emitter != -1) {
7871  detail::json_iterator it;
7872  if (!detail::FindMember(o, "extensions", it)) {
7873  detail::json extensions;
7874  detail::JsonSetObject(extensions);
7875  detail::JsonAddMember(o, "extensions", std::move(extensions));
7876  detail::FindMember(o, "extensions", it);
7877  }
7878  auto &extensions = detail::GetValue(it);
7879  if (!detail::FindMember(extensions, "KHR_audio", it)) {
7880  detail::json audio;
7881  detail::JsonSetObject(audio);
7882  detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
7883  detail::FindMember(extensions, "KHR_audio", it);
7884  }
7885  SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it));
7886  } else {
7887  detail::json_iterator ext_it;
7888  if (detail::FindMember(o, "extensions", ext_it)) {
7889  auto &extensions = detail::GetValue(ext_it);
7890  detail::json_iterator lp_it;
7891  if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
7892  detail::Erase(extensions, lp_it);
7893  }
7894  if (detail::IsEmpty(extensions)) {
7895  detail::Erase(o, ext_it);
7896  }
7897  }
7898  }
7899 
7900  // MSFT_lod
7901  if (!node.lods.empty()) {
7902  detail::json_iterator it;
7903  if (!detail::FindMember(o, "extensions", it)) {
7904  detail::json extensions;
7905  detail::JsonSetObject(extensions);
7906  detail::JsonAddMember(o, "extensions", std::move(extensions));
7907  detail::FindMember(o, "extensions", it);
7908  }
7909  auto &extensions = detail::GetValue(it);
7910  if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7911  detail::json lod;
7912  detail::JsonSetObject(lod);
7913  detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7914  detail::FindMember(extensions, "MSFT_lod", it);
7915  }
7916  SerializeNumberArrayProperty<int>("ids", node.lods, detail::GetValue(it));
7917  } else {
7918  detail::json_iterator ext_it;
7919  if (detail::FindMember(o, "extensions", ext_it)) {
7920  auto &extensions = detail::GetValue(ext_it);
7921  detail::json_iterator lp_it;
7922  if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7923  detail::Erase(extensions, lp_it);
7924  }
7925  if (detail::IsEmpty(extensions)) {
7926  detail::Erase(o, ext_it);
7927  }
7928  }
7929  }
7930 
7931  if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
7932  SerializeNumberArrayProperty<int>("children", node.children, o);
7933 }
7934 
7935 static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) {
7936  if (!sampler.name.empty()) {
7937  SerializeStringProperty("name", sampler.name, o);
7938  }
7939  if (sampler.magFilter != -1) {
7940  SerializeNumberProperty("magFilter", sampler.magFilter, o);
7941  }
7942  if (sampler.minFilter != -1) {
7943  SerializeNumberProperty("minFilter", sampler.minFilter, o);
7944  }
7945  // SerializeNumberProperty("wrapR", sampler.wrapR, o);
7946  SerializeNumberProperty("wrapS", sampler.wrapS, o);
7947  SerializeNumberProperty("wrapT", sampler.wrapT, o);
7948 
7949  SerializeExtrasAndExtensions(sampler, o);
7950 }
7951 
7952 static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
7953  detail::json &o) {
7954  SerializeNumberProperty("zfar", camera.zfar, o);
7955  SerializeNumberProperty("znear", camera.znear, o);
7956  SerializeNumberProperty("xmag", camera.xmag, o);
7957  SerializeNumberProperty("ymag", camera.ymag, o);
7958 
7959  SerializeExtrasAndExtensions(camera, o);
7960 }
7961 
7962 static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
7963  detail::json &o) {
7964  SerializeNumberProperty("zfar", camera.zfar, o);
7965  SerializeNumberProperty("znear", camera.znear, o);
7966  if (camera.aspectRatio > 0) {
7967  SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7968  }
7969 
7970  if (camera.yfov > 0) {
7971  SerializeNumberProperty("yfov", camera.yfov, o);
7972  }
7973 
7974  SerializeExtrasAndExtensions(camera, o);
7975 }
7976 
7977 static void SerializeGltfCamera(const Camera &camera, detail::json &o) {
7978  SerializeStringProperty("type", camera.type, o);
7979  if (!camera.name.empty()) {
7980  SerializeStringProperty("name", camera.name, o);
7981  }
7982 
7983  if (camera.type.compare("orthographic") == 0) {
7984  detail::json orthographic;
7985  SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
7986  detail::JsonAddMember(o, "orthographic", std::move(orthographic));
7987  } else if (camera.type.compare("perspective") == 0) {
7988  detail::json perspective;
7989  SerializeGltfPerspectiveCamera(camera.perspective, perspective);
7990  detail::JsonAddMember(o, "perspective", std::move(perspective));
7991  } else {
7992  // ???
7993  }
7994 
7995  SerializeExtrasAndExtensions(camera, o);
7996 }
7997 
7998 static void SerializeGltfScene(const Scene &scene, detail::json &o) {
7999  SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
8000 
8001  if (scene.name.size()) {
8002  SerializeStringProperty("name", scene.name, o);
8003  }
8004  SerializeExtrasAndExtensions(scene, o);
8005 
8006  // KHR_audio
8007  if (!scene.audioEmitters.empty()) {
8008  detail::json_iterator it;
8009  if (!detail::FindMember(o, "extensions", it)) {
8010  detail::json extensions;
8011  detail::JsonSetObject(extensions);
8012  detail::JsonAddMember(o, "extensions", std::move(extensions));
8013  detail::FindMember(o, "extensions", it);
8014  }
8015  auto &extensions = detail::GetValue(it);
8016  if (!detail::FindMember(extensions, "KHR_audio", it)) {
8017  detail::json audio;
8018  detail::JsonSetObject(audio);
8019  detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
8020  detail::FindMember(o, "KHR_audio", it);
8021  }
8022  SerializeNumberArrayProperty("emitters", scene.audioEmitters,
8023  detail::GetValue(it));
8024  } else {
8025  detail::json_iterator ext_it;
8026  if (detail::FindMember(o, "extensions", ext_it)) {
8027  auto &extensions = detail::GetValue(ext_it);
8028  detail::json_iterator lp_it;
8029  if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
8030  detail::Erase(extensions, lp_it);
8031  }
8032  if (detail::IsEmpty(extensions)) {
8033  detail::Erase(o, ext_it);
8034  }
8035  }
8036  }
8037 }
8038 
8039 static void SerializeGltfSkin(const Skin &skin, detail::json &o) {
8040  // required
8041  SerializeNumberArrayProperty<int>("joints", skin.joints, o);
8042 
8043  if (skin.inverseBindMatrices >= 0) {
8044  SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
8045  }
8046 
8047  if (skin.skeleton >= 0) {
8048  SerializeNumberProperty("skeleton", skin.skeleton, o);
8049  }
8050 
8051  if (skin.name.size()) {
8052  SerializeStringProperty("name", skin.name, o);
8053  }
8054 
8055  SerializeExtrasAndExtensions(skin, o);
8056 }
8057 
8058 static void SerializeGltfTexture(const Texture &texture, detail::json &o) {
8059  if (texture.sampler > -1) {
8060  SerializeNumberProperty("sampler", texture.sampler, o);
8061  }
8062  if (texture.source > -1) {
8063  SerializeNumberProperty("source", texture.source, o);
8064  }
8065  if (texture.name.size()) {
8066  SerializeStringProperty("name", texture.name, o);
8067  }
8068  SerializeExtrasAndExtensions(texture, o);
8069 }
8070 
8074 static void SerializeGltfModel(const Model *model, detail::json &o) {
8075  // ACCESSORS
8076  if (model->accessors.size()) {
8077  detail::json accessors;
8078  detail::JsonReserveArray(accessors, model->accessors.size());
8079  for (unsigned int i = 0; i < model->accessors.size(); ++i) {
8080  detail::json accessor;
8081  SerializeGltfAccessor(model->accessors[i], accessor);
8082  detail::JsonPushBack(accessors, std::move(accessor));
8083  }
8084  detail::JsonAddMember(o, "accessors", std::move(accessors));
8085  }
8086 
8087  // ANIMATIONS
8088  if (model->animations.size()) {
8089  detail::json animations;
8090  detail::JsonReserveArray(animations, model->animations.size());
8091  for (unsigned int i = 0; i < model->animations.size(); ++i) {
8092  if (model->animations[i].channels.size()) {
8093  detail::json animation;
8094  SerializeGltfAnimation(model->animations[i], animation);
8095  detail::JsonPushBack(animations, std::move(animation));
8096  }
8097  }
8098 
8099  detail::JsonAddMember(o, "animations", std::move(animations));
8100  }
8101 
8102  // ASSET
8103  detail::json asset;
8104  SerializeGltfAsset(model->asset, asset);
8105  detail::JsonAddMember(o, "asset", std::move(asset));
8106 
8107  // BUFFERVIEWS
8108  if (model->bufferViews.size()) {
8109  detail::json bufferViews;
8110  detail::JsonReserveArray(bufferViews, model->bufferViews.size());
8111  for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
8112  detail::json bufferView;
8113  SerializeGltfBufferView(model->bufferViews[i], bufferView);
8114  detail::JsonPushBack(bufferViews, std::move(bufferView));
8115  }
8116  detail::JsonAddMember(o, "bufferViews", std::move(bufferViews));
8117  }
8118 
8119  // Extensions required
8120  if (model->extensionsRequired.size()) {
8121  SerializeStringArrayProperty("extensionsRequired",
8122  model->extensionsRequired, o);
8123  }
8124 
8125  // MATERIALS
8126  if (model->materials.size()) {
8127  detail::json materials;
8128  detail::JsonReserveArray(materials, model->materials.size());
8129  for (unsigned int i = 0; i < model->materials.size(); ++i) {
8130  detail::json material;
8131  SerializeGltfMaterial(model->materials[i], material);
8132 
8133  if (detail::JsonIsNull(material)) {
8134  // Issue 294.
8135  // `material` does not have any required parameters
8136  // so the result may be null(unmodified) when all material parameters
8137  // have default value.
8138  //
8139  // null is not allowed thus we create an empty JSON object.
8140  detail::JsonSetObject(material);
8141  }
8142  detail::JsonPushBack(materials, std::move(material));
8143  }
8144  detail::JsonAddMember(o, "materials", std::move(materials));
8145  }
8146 
8147  // MESHES
8148  if (model->meshes.size()) {
8149  detail::json meshes;
8150  detail::JsonReserveArray(meshes, model->meshes.size());
8151  for (unsigned int i = 0; i < model->meshes.size(); ++i) {
8152  detail::json mesh;
8153  SerializeGltfMesh(model->meshes[i], mesh);
8154  detail::JsonPushBack(meshes, std::move(mesh));
8155  }
8156  detail::JsonAddMember(o, "meshes", std::move(meshes));
8157  }
8158 
8159  // NODES
8160  if (model->nodes.size()) {
8161  detail::json nodes;
8162  detail::JsonReserveArray(nodes, model->nodes.size());
8163  for (unsigned int i = 0; i < model->nodes.size(); ++i) {
8164  detail::json node;
8165  SerializeGltfNode(model->nodes[i], node);
8166 
8167  if (detail::JsonIsNull(node)) {
8168  // Issue 457.
8169  // `node` does not have any required parameters,
8170  // so the result may be null(unmodified) when all node parameters
8171  // have default value.
8172  //
8173  // null is not allowed thus we create an empty JSON object.
8174  detail::JsonSetObject(node);
8175  }
8176  detail::JsonPushBack(nodes, std::move(node));
8177  }
8178  detail::JsonAddMember(o, "nodes", std::move(nodes));
8179  }
8180 
8181  // SCENE
8182  if (model->defaultScene > -1) {
8183  SerializeNumberProperty<int>("scene", model->defaultScene, o);
8184  }
8185 
8186  // SCENES
8187  if (model->scenes.size()) {
8188  detail::json scenes;
8189  detail::JsonReserveArray(scenes, model->scenes.size());
8190  for (unsigned int i = 0; i < model->scenes.size(); ++i) {
8191  detail::json currentScene;
8192  SerializeGltfScene(model->scenes[i], currentScene);
8193  if (detail::JsonIsNull(currentScene)) {
8194  // Issue 464.
8195  // `scene` does not have any required parameters,
8196  // so the result may be null(unmodified) when all scene parameters
8197  // have default value.
8198  //
8199  // null is not allowed thus we create an empty JSON object.
8200  detail::JsonSetObject(currentScene);
8201  }
8202  detail::JsonPushBack(scenes, std::move(currentScene));
8203  }
8204  detail::JsonAddMember(o, "scenes", std::move(scenes));
8205  }
8206 
8207  // SKINS
8208  if (model->skins.size()) {
8209  detail::json skins;
8210  detail::JsonReserveArray(skins, model->skins.size());
8211  for (unsigned int i = 0; i < model->skins.size(); ++i) {
8212  detail::json skin;
8213  SerializeGltfSkin(model->skins[i], skin);
8214  detail::JsonPushBack(skins, std::move(skin));
8215  }
8216  detail::JsonAddMember(o, "skins", std::move(skins));
8217  }
8218 
8219  // TEXTURES
8220  if (model->textures.size()) {
8221  detail::json textures;
8222  detail::JsonReserveArray(textures, model->textures.size());
8223  for (unsigned int i = 0; i < model->textures.size(); ++i) {
8224  detail::json texture;
8225  SerializeGltfTexture(model->textures[i], texture);
8226  detail::JsonPushBack(textures, std::move(texture));
8227  }
8228  detail::JsonAddMember(o, "textures", std::move(textures));
8229  }
8230 
8231  // SAMPLERS
8232  if (model->samplers.size()) {
8233  detail::json samplers;
8234  detail::JsonReserveArray(samplers, model->samplers.size());
8235  for (unsigned int i = 0; i < model->samplers.size(); ++i) {
8236  detail::json sampler;
8237  SerializeGltfSampler(model->samplers[i], sampler);
8238  detail::JsonPushBack(samplers, std::move(sampler));
8239  }
8240  detail::JsonAddMember(o, "samplers", std::move(samplers));
8241  }
8242 
8243  // CAMERAS
8244  if (model->cameras.size()) {
8245  detail::json cameras;
8246  detail::JsonReserveArray(cameras, model->cameras.size());
8247  for (unsigned int i = 0; i < model->cameras.size(); ++i) {
8248  detail::json camera;
8249  SerializeGltfCamera(model->cameras[i], camera);
8250  detail::JsonPushBack(cameras, std::move(camera));
8251  }
8252  detail::JsonAddMember(o, "cameras", std::move(cameras));
8253  }
8254 
8255  // EXTRAS & EXTENSIONS
8256  SerializeExtrasAndExtensions(*model, o);
8257 
8258  auto extensionsUsed = model->extensionsUsed;
8259 
8260  // LIGHTS as KHR_lights_punctual
8261  if (model->lights.size()) {
8262  detail::json lights;
8263  detail::JsonReserveArray(lights, model->lights.size());
8264  for (unsigned int i = 0; i < model->lights.size(); ++i) {
8265  detail::json light;
8266  SerializeGltfLight(model->lights[i], light);
8267  detail::JsonPushBack(lights, std::move(light));
8268  }
8269  detail::json khr_lights_cmn;
8270  detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
8271  detail::json ext_j;
8272 
8273  {
8274  detail::json_const_iterator it;
8275  if (detail::FindMember(o, "extensions", it)) {
8276  detail::JsonAssign(ext_j, detail::GetValue(it));
8277  }
8278  }
8279 
8280  detail::JsonAddMember(ext_j, "KHR_lights_punctual",
8281  std::move(khr_lights_cmn));
8282 
8283  detail::JsonAddMember(o, "extensions", std::move(ext_j));
8284 
8285  // Also add "KHR_lights_punctual" to `extensionsUsed`
8286  {
8287  auto has_khr_lights_punctual =
8288  std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
8289  [](const std::string &s) {
8290  return (s.compare("KHR_lights_punctual") == 0);
8291  });
8292 
8293  if (has_khr_lights_punctual == extensionsUsed.end()) {
8294  extensionsUsed.push_back("KHR_lights_punctual");
8295  }
8296  }
8297  }
8298 
8299  // KHR_audio
8300  if (!model->audioEmitters.empty() || !model->audioSources.empty()) {
8301  detail::json emitters;
8302  detail::JsonReserveArray(emitters, model->audioEmitters.size());
8303  for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) {
8304  detail::json emitter;
8305  SerializeGltfAudioEmitter(model->audioEmitters[i], emitter);
8306  detail::JsonPushBack(emitters, std::move(emitter));
8307  }
8308  detail::json khr_audio_cmn;
8309  detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters));
8310 
8311  detail::json sources;
8312  detail::JsonReserveArray(sources, model->audioSources.size());
8313  for (unsigned int i = 0; i < model->audioSources.size(); ++i) {
8315  SerializeGltfAudioSource(model->audioSources[i], source);
8316  detail::JsonPushBack(sources, std::move(source));
8317  }
8318  detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources));
8319 
8320  detail::json ext_j;
8321  {
8322  detail::json_const_iterator it;
8323  if (detail::FindMember(o, "extensions", it)) {
8324  detail::JsonAssign(ext_j, detail::GetValue(it));
8325  }
8326  }
8327 
8328  detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn));
8329 
8330  detail::JsonAddMember(o, "extensions", std::move(ext_j));
8331 
8332  // Also add "KHR_audio" to `extensionsUsed`
8333  {
8334  auto has_khr_audio = std::find_if(
8335  extensionsUsed.begin(), extensionsUsed.end(),
8336  [](const std::string &s) { return (s.compare("KHR_audio") == 0); });
8337 
8338  if (has_khr_audio == extensionsUsed.end()) {
8339  extensionsUsed.push_back("KHR_audio");
8340  }
8341  }
8342  }
8343 
8344  // MSFT_lod
8345 
8346  // Look if there is a node that employs MSFT_lod
8347  auto msft_lod_nodes_it = std::find_if(
8348  model->nodes.begin(), model->nodes.end(),
8349  [](const Node& node) { return !node.lods.empty(); });
8350 
8351  // Look if there is a material that employs MSFT_lod
8352  auto msft_lod_materials_it = std::find_if(
8353  model->materials.begin(), model->materials.end(),
8354  [](const Material& material) {return !material.lods.empty(); });
8355 
8356  // If either a node or a material employ MSFT_lod, then we need
8357  // to add MSFT_lod to the list of used extensions.
8358  if (msft_lod_nodes_it != model->nodes.end() || msft_lod_materials_it != model->materials.end()) {
8359  // First check if MSFT_lod is already registered as used extension
8360  auto has_msft_lod = std::find_if(
8361  extensionsUsed.begin(), extensionsUsed.end(),
8362  [](const std::string &s) { return (s.compare("MSFT_lod") == 0); });
8363 
8364  // If MSFT_lod is not registered yet, add it
8365  if (has_msft_lod == extensionsUsed.end()) {
8366  extensionsUsed.push_back("MSFT_lod");
8367  }
8368  }
8369 
8370  // Extensions used
8371  if (extensionsUsed.size()) {
8372  SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
8373  }
8374 }
8375 
8376 static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
8377  stream << content << std::endl;
8378  return stream.good();
8379 }
8380 
8381 static bool WriteGltfFile(const std::string &output,
8382  const std::string &content) {
8383 #ifdef _WIN32
8384 #if defined(_MSC_VER)
8385  std::ofstream gltfFile(UTF8ToWchar(output).c_str());
8386 #elif defined(__GLIBCXX__)
8387  int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8388  _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
8389  __gnu_cxx::stdio_filebuf<char> wfile_buf(
8390  file_descriptor, std::ios_base::out | std::ios_base::binary);
8391  std::ostream gltfFile(&wfile_buf);
8392  if (!wfile_buf.is_open()) return false;
8393 #else
8394  std::ofstream gltfFile(output.c_str());
8395  if (!gltfFile.is_open()) return false;
8396 #endif
8397 #else
8398  std::ofstream gltfFile(output.c_str());
8399  if (!gltfFile.is_open()) return false;
8400 #endif
8401  return WriteGltfStream(gltfFile, content);
8402 }
8403 
8404 static bool WriteBinaryGltfStream(std::ostream &stream,
8405  const std::string &content,
8406  const std::vector<unsigned char> &binBuffer) {
8407  const std::string header = "glTF";
8408  const int version = 2;
8409 
8410  const uint32_t content_size = uint32_t(content.size());
8411  const uint32_t binBuffer_size = uint32_t(binBuffer.size());
8412  // determine number of padding bytes required to ensure 4 byte alignment
8413  const uint32_t content_padding_size =
8414  content_size % 4 == 0 ? 0 : 4 - content_size % 4;
8415  const uint32_t bin_padding_size =
8416  binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
8417 
8418  // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
8419  // Chunk data must be located at 4-byte boundary, which may require padding
8420  const uint32_t length =
8421  12 + 8 + content_size + content_padding_size +
8422  (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
8423 
8424  stream.write(header.c_str(), std::streamsize(header.size()));
8425  stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
8426  stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
8427 
8428  // JSON chunk info, then JSON data
8429  const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
8430  const uint32_t model_format = 0x4E4F534A;
8431  stream.write(reinterpret_cast<const char *>(&model_length),
8432  sizeof(model_length));
8433  stream.write(reinterpret_cast<const char *>(&model_format),
8434  sizeof(model_format));
8435  stream.write(content.c_str(), std::streamsize(content.size()));
8436 
8437  // Chunk must be multiplies of 4, so pad with spaces
8438  if (content_padding_size > 0) {
8439  const std::string padding = std::string(size_t(content_padding_size), ' ');
8440  stream.write(padding.c_str(), std::streamsize(padding.size()));
8441  }
8442  if (binBuffer.size() > 0) {
8443  // BIN chunk info, then BIN data
8444  const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
8445  const uint32_t bin_format = 0x004e4942;
8446  stream.write(reinterpret_cast<const char *>(&bin_length),
8447  sizeof(bin_length));
8448  stream.write(reinterpret_cast<const char *>(&bin_format),
8449  sizeof(bin_format));
8450  stream.write(reinterpret_cast<const char *>(binBuffer.data()),
8451  std::streamsize(binBuffer.size()));
8452  // Chunksize must be multiplies of 4, so pad with zeroes
8453  if (bin_padding_size > 0) {
8454  const std::vector<unsigned char> padding =
8455  std::vector<unsigned char>(size_t(bin_padding_size), 0);
8456  stream.write(reinterpret_cast<const char *>(padding.data()),
8457  std::streamsize(padding.size()));
8458  }
8459  }
8460 
8461  stream.flush();
8462  return stream.good();
8463 }
8464 
8465 static bool WriteBinaryGltfFile(const std::string &output,
8466  const std::string &content,
8467  const std::vector<unsigned char> &binBuffer) {
8468 #ifdef _WIN32
8469 #if defined(_MSC_VER)
8470  std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
8471 #elif defined(__GLIBCXX__)
8472  int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8473  _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
8474  __gnu_cxx::stdio_filebuf<char> wfile_buf(
8475  file_descriptor, std::ios_base::out | std::ios_base::binary);
8476  std::ostream gltfFile(&wfile_buf);
8477 #else
8478  std::ofstream gltfFile(output.c_str(), std::ios::binary);
8479 #endif
8480 #else
8481  std::ofstream gltfFile(output.c_str(), std::ios::binary);
8482 #endif
8483  return WriteBinaryGltfStream(gltfFile, content, binBuffer);
8484 }
8485 
8486 bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
8487  bool prettyPrint = true,
8488  bool writeBinary = false) {
8489  detail::JsonDocument output;
8490 
8492  SerializeGltfModel(model, output);
8493 
8494  // BUFFERS
8495  std::vector<unsigned char> binBuffer;
8496  if (model->buffers.size()) {
8497  detail::json buffers;
8498  detail::JsonReserveArray(buffers, model->buffers.size());
8499  for (unsigned int i = 0; i < model->buffers.size(); ++i) {
8500  detail::json buffer;
8501  if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8502  SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
8503  } else {
8504  SerializeGltfBuffer(model->buffers[i], buffer);
8505  }
8506  detail::JsonPushBack(buffers, std::move(buffer));
8507  }
8508  detail::JsonAddMember(output, "buffers", std::move(buffers));
8509  }
8510 
8511  // IMAGES
8512  if (model->images.size()) {
8513  detail::json images;
8514  detail::JsonReserveArray(images, model->images.size());
8515  for (unsigned int i = 0; i < model->images.size(); ++i) {
8516  detail::json image;
8517 
8518  std::string dummystring;
8519  // UpdateImageObject need baseDir but only uses it if embeddedImages is
8520  // enabled, since we won't write separate images when writing to a stream
8521  // we
8522  std::string uri;
8523  if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
8524  &uri_cb, &this->WriteImageData,
8525  this->write_image_user_data_, &uri)) {
8526  return false;
8527  }
8528  SerializeGltfImage(model->images[i], uri, image);
8529  detail::JsonPushBack(images, std::move(image));
8530  }
8531  detail::JsonAddMember(output, "images", std::move(images));
8532  }
8533 
8534  if (writeBinary) {
8535  return WriteBinaryGltfStream(stream, detail::JsonToString(output),
8536  binBuffer);
8537  } else {
8538  return WriteGltfStream(stream,
8539  detail::JsonToString(output, prettyPrint ? 2 : -1));
8540  }
8541 }
8542 
8543 
8544 bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
8545  const std::string &filename,
8546  bool embedImages = false,
8547  bool embedBuffers = false,
8548  bool prettyPrint = true,
8549  bool writeBinary = false) {
8550  detail::JsonDocument output;
8551  std::string defaultBinFilename = GetBaseFilename(filename);
8552  std::string defaultBinFileExt = ".bin";
8553  std::string::size_type pos =
8554  defaultBinFilename.rfind('.', defaultBinFilename.length());
8555 
8556  if (pos != std::string::npos) {
8557  defaultBinFilename = defaultBinFilename.substr(0, pos);
8558  }
8559  std::string baseDir = GetBaseDir(filename);
8560  if (baseDir.empty()) {
8561  baseDir = "./";
8562  }
8564  SerializeGltfModel(model, output);
8565 
8566  // BUFFERS
8567  std::vector<std::string> usedFilenames;
8568  std::vector<unsigned char> binBuffer;
8569  if (model->buffers.size()) {
8570  detail::json buffers;
8571  detail::JsonReserveArray(buffers, model->buffers.size());
8572  for (unsigned int i = 0; i < model->buffers.size(); ++i) {
8573  detail::json buffer;
8574  if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8575  SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
8576  } else if (embedBuffers) {
8577  SerializeGltfBuffer(model->buffers[i], buffer);
8578  } else {
8579  std::string binSavePath;
8580  std::string binFilename;
8581  std::string binUri;
8582  if (!model->buffers[i].uri.empty() &&
8583  !IsDataURI(model->buffers[i].uri)) {
8584  binUri = model->buffers[i].uri;
8585  if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
8586  return false;
8587  }
8588  } else {
8589  binFilename = defaultBinFilename + defaultBinFileExt;
8590  bool inUse = true;
8591  int numUsed = 0;
8592  while (inUse) {
8593  inUse = false;
8594  for (const std::string &usedName : usedFilenames) {
8595  if (binFilename.compare(usedName) != 0) continue;
8596  inUse = true;
8597  binFilename = defaultBinFilename + std::to_string(numUsed++) +
8598  defaultBinFileExt;
8599  break;
8600  }
8601  }
8602 
8603  if (uri_cb.encode) {
8604  if (!uri_cb.encode(binFilename, "buffer", &binUri,
8605  uri_cb.user_data)) {
8606  return false;
8607  }
8608  } else {
8609  binUri = binFilename;
8610  }
8611  }
8612  usedFilenames.push_back(binFilename);
8613  binSavePath = JoinPath(baseDir, binFilename);
8614  if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
8615  binUri)) {
8616  return false;
8617  }
8618  }
8619  detail::JsonPushBack(buffers, std::move(buffer));
8620  }
8621  detail::JsonAddMember(output, "buffers", std::move(buffers));
8622  }
8623 
8624  // IMAGES
8625  if (model->images.size()) {
8626  detail::json images;
8627  detail::JsonReserveArray(images, model->images.size());
8628  for (unsigned int i = 0; i < model->images.size(); ++i) {
8629  detail::json image;
8630 
8631  std::string uri;
8632  if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
8633  &uri_cb, &this->WriteImageData,
8634  this->write_image_user_data_, &uri)) {
8635  return false;
8636  }
8637  SerializeGltfImage(model->images[i], uri, image);
8638  detail::JsonPushBack(images, std::move(image));
8639  }
8640  detail::JsonAddMember(output, "images", std::move(images));
8641  }
8642 
8643  if (writeBinary) {
8644  return WriteBinaryGltfFile(filename, detail::JsonToString(output),
8645  binBuffer);
8646  } else {
8647  return WriteGltfFile(filename,
8648  detail::JsonToString(output, (prettyPrint ? 2 : -1)));
8649  }
8650 }
8651 
8652 } // namespace tinygltf
8653 
8654 #ifdef __clang__
8655 #pragma clang diagnostic pop
8656 #endif
8657 
8658 #endif // TINYGLTF_IMPLEMENTATION
tinygltf::Value::Get
const T & Get() const
tinygltf::Model::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1240
tinygltf::AudioEmitter::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1183
tinygltf::Model::samplers
std::vector< Sampler > samplers
Definition: tiny_gltf.h:1226
TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT
Definition: tiny_gltf.h:93
tinygltf::Texture::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:670
tinygltf::Value::Type
char Type() const
Definition: tiny_gltf.h:296
tinygltf::URIDecode
bool URIDecode(const std::string &in_uri, std::string *out_uri, void *user_data)
tinygltf::Parameter::operator==
bool operator==(const Parameter &) const
tinygltf::Parameter::TextureTexCoord
int TextureTexCoord() const
Definition: tiny_gltf.h:457
tinygltf::Value::Object
std::map< std::string, Value > Object
Definition: tiny_gltf.h:263
tinygltf::Accessor::Sparse
Definition: tiny_gltf.h:836
tinygltf::Accessor::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:829
tinygltf::BINARY_TYPE
@ BINARY_TYPE
Definition: tiny_gltf.h:194
tinygltf::Sampler::extras
Value extras
Definition: tiny_gltf.h:614
tinygltf::Model::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1243
tinygltf::Material::alphaCutoff
double alphaCutoff
Definition: tiny_gltf.h:764
tinygltf::SpotLight::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1097
tinygltf::URIDecodeFunction
bool(* URIDecodeFunction)(const std::string &in_uri, std::string *out_uri, void *user_data)
Definition: tiny_gltf.h:1275
tinygltf::PbrMetallicRoughness::metallicRoughnessTexture
TextureInfo metallicRoughnessTexture
Definition: tiny_gltf.h:741
tinygltf::AudioSource::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1206
tinygltf::URICallbacks::encode
URIEncodeFunction encode
Definition: tiny_gltf.h:1286
nlohmann::to_string
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.hpp:26470
tinygltf::NormalTextureInfo::scale
double scale
Definition: tiny_gltf.h:700
tinygltf::PositionalEmitter
Definition: tiny_gltf.h:1126
tinygltf::PbrMetallicRoughness::baseColorTexture
TextureInfo baseColorTexture
Definition: tiny_gltf.h:738
tinygltf::Value::boolean_value_
bool boolean_value_
Definition: tiny_gltf.h:394
tinygltf::TinyGLTF::GetPreserveImageChannels
bool GetPreserveImageChannels() const
Definition: tiny_gltf.h:1549
tinygltf::Texture::sampler
int sampler
Definition: tiny_gltf.h:664
tinygltf::Animation::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:566
tinygltf::Accessor::bufferView
int bufferView
Definition: tiny_gltf.h:816
tinygltf::PbrMetallicRoughness::metallicFactor
double metallicFactor
Definition: tiny_gltf.h:739
tinygltf::Model::extras
Value extras
Definition: tiny_gltf.h:1239
tinygltf::Parameter::ColorFactor
ColorValue ColorFactor() const
Definition: tiny_gltf.h:498
tinygltf::Value::Array
std::vector< Value > Array
Definition: tiny_gltf.h:262
tinygltf::AnimationSampler::extras
Value extras
Definition: tiny_gltf.h:549
tinygltf::AnimationChannel::target_node
int target_node
Definition: tiny_gltf.h:524
tinygltf::Value::IsNumber
bool IsNumber() const
Definition: tiny_gltf.h:302
tinygltf::Value::Value
Value(std::vector< unsigned char > &&v) noexcept
Definition: tiny_gltf.h:283
tinygltf::TinyGLTF::~TinyGLTF
~TinyGLTF()=default
tinygltf::Camera::operator==
bool operator==(const Camera &) const
tinygltf::Texture::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:671
tinygltf::FileExists
bool FileExists(const std::string &abs_filename, void *)
tinygltf::REQUIRE_ACCESSORS
@ REQUIRE_ACCESSORS
Definition: tiny_gltf.h:1253
tinygltf::Primitive::operator==
bool operator==(const Primitive &) const
tinygltf::Buffer::name
std::string name
Definition: tiny_gltf.h:1038
tinygltf::Value::string_value_
std::string string_value_
Definition: tiny_gltf.h:390
tinygltf::Value::Value
Value(Array &&a) noexcept
Definition: tiny_gltf.h:287
DEFAULT_METHODS
#define DEFAULT_METHODS(x)
Definition: tiny_gltf.h:73
tinygltf::Light::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1118
tinygltf::Parameter::TextureIndex
int TextureIndex() const
Definition: tiny_gltf.h:446
jwt::error::rsa_error::ok
@ ok
tinygltf::AnimationChannel::target_extras_json_string
std::string target_extras_json_string
Definition: tiny_gltf.h:536
tinygltf::Accessor::componentType
int componentType
Definition: tiny_gltf.h:821
tinygltf::NormalTextureInfo::operator==
bool operator==(const NormalTextureInfo &) const
tinygltf::Mesh::weights
std::vector< double > weights
Definition: tiny_gltf.h:993
tinygltf::AnimationChannel
Definition: tiny_gltf.h:522
tinygltf::Node::extras
Value extras
Definition: tiny_gltf.h:1030
nlohmann::json
basic_json<> json
default JSON class
Definition: json.hpp:3472
tinygltf::PerspectiveCamera::zfar
double zfar
Definition: tiny_gltf.h:913
tinygltf::Value::Has
bool Has(const std::string &key) const
Definition: tiny_gltf.h:362
tinygltf::Buffer::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1047
tinygltf::OcclusionTextureInfo
Definition: tiny_gltf.h:716
tinygltf::PositionalEmitter::coneInnerAngle
double coneInnerAngle
Definition: tiny_gltf.h:1127
tinygltf::Type
Type
Definition: tiny_gltf.h:187
tinygltf::Node::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1033
tinygltf::OrthographicCamera::operator==
bool operator==(const OrthographicCamera &) const
tinygltf::Mesh
Definition: tiny_gltf.h:990
TINYGLTF_TYPE_MAT3
#define TINYGLTF_TYPE_MAT3
Definition: tiny_gltf.h:148
tinygltf::AudioEmitter::source
int source
Definition: tiny_gltf.h:1172
conscience_core::detector_engine::Face
@ Face
Definition: CscCommonDetectorTypes.h:19
tinygltf::TinyGLTF::GetMaxExternalFileSize
size_t GetMaxExternalFileSize() const
Definition: tiny_gltf.h:1547
tinygltf::Asset::version
std::string version
Definition: tiny_gltf.h:1056
tinygltf::BufferView::dracoDecoded
bool dracoDecoded
Definition: tiny_gltf.h:808
tinygltf::Sampler::magFilter
int magFilter
Definition: tiny_gltf.h:603
tinygltf::OrthographicCamera::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:942
stbi_is_16_bit_from_memory
STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len)
tinygltf::REQUIRE_SCENE
@ REQUIRE_SCENE
Definition: tiny_gltf.h:1250
tinygltf::OrthographicCamera::znear
double znear
Definition: tiny_gltf.h:932
tinygltf::TinyGLTF::LoadBinaryFromMemory
bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn, const unsigned char *bytes, const unsigned int length, const std::string &base_dir="", unsigned int check_sections=REQUIRE_VERSION)
tinygltf::Asset::copyright
std::string copyright
Definition: tiny_gltf.h:1059
tinygltf::SpotLight::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1102
tinygltf::Material::normalTexture
NormalTextureInfo normalTexture
Definition: tiny_gltf.h:770
tinygltf::FileExistsFunction
bool(* FileExistsFunction)(const std::string &abs_filename, void *)
Definition: tiny_gltf.h:1329
tinygltf::WriteWholeFileFunction
bool(* WriteWholeFileFunction)(std::string *, const std::string &, const std::vector< unsigned char > &, void *)
Definition: tiny_gltf.h:1346
tinygltf::PerspectiveCamera::yfov
double yfov
Definition: tiny_gltf.h:912
tinygltf::Node::matrix
std::vector< double > matrix
Definition: tiny_gltf.h:1026
tinygltf::AnimationSampler::input
int input
Definition: tiny_gltf.h:545
tinygltf::Image::component
int component
Definition: tiny_gltf.h:630
tinygltf::PerspectiveCamera::operator==
bool operator==(const PerspectiveCamera &) const
tinygltf::Image
Definition: tiny_gltf.h:626
tinygltf::Mesh::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:994
stbi_load_16_from_memory
STBIDEF stbi_us * stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
TINYGLTF_DOUBLE_EQUAL
#define TINYGLTF_DOUBLE_EQUAL(a, b)
Definition: tiny_gltf.h:175
tinygltf::Accessor::count
size_t count
Definition: tiny_gltf.h:822
tinygltf::OcclusionTextureInfo::operator==
bool operator==(const OcclusionTextureInfo &) const
tinygltf::FsCallbacks::WriteWholeFile
WriteWholeFileFunction WriteWholeFile
Definition: tiny_gltf.h:1365
tinygltf::Skin::name
std::string name
Definition: tiny_gltf.h:578
tinygltf::STRING_TYPE
@ STRING_TYPE
Definition: tiny_gltf.h:192
tinygltf::Permissive
@ Permissive
Definition: tiny_gltf.h:199
tinygltf::TinyGLTF::WriteGltfSceneToFile
bool WriteGltfSceneToFile(const Model *model, const std::string &filename, bool embedImages, bool embedBuffers, bool prettyPrint, bool writeBinary)
tinygltf::Model::defaultScene
int defaultScene
Definition: tiny_gltf.h:1233
tinygltf::Accessor::name
std::string name
Definition: tiny_gltf.h:818
tinygltf::Node::translation
std::vector< double > translation
Definition: tiny_gltf.h:1025
tinygltf::OrthographicCamera::extras
Value extras
Definition: tiny_gltf.h:939
tinygltf::AudioEmitter::distanceModel
std::string distanceModel
Definition: tiny_gltf.h:1159
dest
char * dest
Definition: lz4.h:805
tinygltf::Scene::operator==
bool operator==(const Scene &) const
tinygltf::Node::rotation
std::vector< double > rotation
Definition: tiny_gltf.h:1023
tinygltf::SectionCheck
SectionCheck
Definition: tiny_gltf.h:1247
TINYGLTF_COMPONENT_TYPE_DOUBLE
#define TINYGLTF_COMPONENT_TYPE_DOUBLE
Definition: tiny_gltf.h:97
tinygltf::Accessor::minValues
std::vector< double > minValues
Definition: tiny_gltf.h:832
tinygltf::Light
Definition: tiny_gltf.h:1105
tinygltf::AudioEmitter::AudioEmitter
AudioEmitter()
Definition: tiny_gltf.h:1174
tinygltf::Sampler::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:619
tinygltf::ReadWholeFile
bool ReadWholeFile(std::vector< unsigned char > *out, std::string *err, const std::string &filepath, void *)
tinygltf::Model::operator==
bool operator==(const Model &) const
tinygltf::OcclusionTextureInfo::OcclusionTextureInfo
OcclusionTextureInfo()=default
tinygltf::Image::name
std::string name
Definition: tiny_gltf.h:627
tinygltf::Accessor::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:825
tinygltf::Parameter::has_number_value
bool has_number_value
Definition: tiny_gltf.h:432
tinygltf::BufferView
Definition: tiny_gltf.h:792
tinygltf::PbrMetallicRoughness::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:744
tinygltf::PbrMetallicRoughness::baseColorFactor
std::vector< double > baseColorFactor
Definition: tiny_gltf.h:737
tinygltf::Asset::Asset
Asset()=default
tinygltf::Accessor::normalized
bool normalized
Definition: tiny_gltf.h:820
tinygltf::Node::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1034
tinygltf::Light::Light
Light()=default
tinygltf::OrthographicCamera::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:943
tinygltf::OrthographicCamera::ymag
double ymag
Definition: tiny_gltf.h:930
tinygltf::Value::array_value_
Array array_value_
Definition: tiny_gltf.h:392
assert
#define assert(condition)
Definition: lz4.cpp:274
tinygltf::Parameter::bool_value
bool bool_value
Definition: tiny_gltf.h:431
tinygltf::NormalTextureInfo::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:705
tinygltf::SpotLight::outerConeAngle
double outerConeAngle
Definition: tiny_gltf.h:1091
tinygltf::Light::intensity
double intensity
Definition: tiny_gltf.h:1108
tinygltf::Animation::samplers
std::vector< AnimationSampler > samplers
Definition: tiny_gltf.h:564
tinygltf::AnimationSampler::output
int output
Definition: tiny_gltf.h:546
tinygltf::AudioSource::operator==
bool operator==(const AudioSource &) const
mode
RequestMode mode
Definition: CscHttpClient.cpp:71
tinygltf::OBJECT_TYPE
@ OBJECT_TYPE
Definition: tiny_gltf.h:195
tinygltf::Model::meshes
std::vector< Mesh > meshes
Definition: tiny_gltf.h:1221
tinygltf
Definition: tiny_gltf.h:80
tinygltf::Material::emissiveTexture
TextureInfo emissiveTexture
Definition: tiny_gltf.h:772
tinygltf::Material::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:779
tinygltf::Sampler::wrapT
int wrapT
Definition: tiny_gltf.h:608
tinygltf::Value::IsString
bool IsString() const
Definition: tiny_gltf.h:306
tinygltf::Texture::name
std::string name
Definition: tiny_gltf.h:662
tinygltf::Texture::source
int source
Definition: tiny_gltf.h:665
tinygltf::Primitive::indices
int indices
Definition: tiny_gltf.h:972
tinygltf::Parameter::Factor
double Factor() const
Definition: tiny_gltf.h:493
tinygltf::TinyGLTF::WriteGltfSceneToStream
bool WriteGltfSceneToStream(const Model *model, std::ostream &stream, bool prettyPrint, bool writeBinary)
tinygltf::NormalTextureInfo::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:709
tinygltf::ParameterMap
std::map< std::string, Parameter > ParameterMap
Definition: tiny_gltf.h:519
tinygltf::Image::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:645
tinygltf::Value::Get
const Value & Get(size_t idx) const
Definition: tiny_gltf.h:340
tinygltf::Value::Value
Value(bool b)
Definition: tiny_gltf.h:267
tinygltf::GetNumComponentsInType
static int32_t GetNumComponentsInType(uint32_t ty)
Definition: tiny_gltf.h:226
tinygltf::Asset::extras
Value extras
Definition: tiny_gltf.h:1061
tinygltf::Model::audioEmitters
std::vector< AudioEmitter > audioEmitters
Definition: tiny_gltf.h:1230
tinygltf::PositionalEmitter::operator==
bool operator==(const PositionalEmitter &) const
tinygltf::Value::type_
int type_
Definition: tiny_gltf.h:386
tinygltf::Accessor::Accessor
Accessor()
Definition: tiny_gltf.h:901
tinygltf::Scene::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1077
TINYGLTF_TYPE_MAT2
#define TINYGLTF_TYPE_MAT2
Definition: tiny_gltf.h:147
tinygltf::Model::accessors
std::vector< Accessor > accessors
Definition: tiny_gltf.h:1216
tinygltf::Material::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:784
tinygltf::Node::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1029
tinygltf::Value::operator==
bool operator==(const tinygltf::Value &other) const
stb_image_write.h
tinygltf::NULL_TYPE
@ NULL_TYPE
Definition: tiny_gltf.h:188
tinygltf::Sampler::minFilter
int minFilter
Definition: tiny_gltf.h:599
tinygltf::GetFileSizeFunction
bool(* GetFileSizeFunction)(size_t *filesize_out, std::string *err, const std::string &abs_filename, void *userdata)
Definition: tiny_gltf.h:1353
tinygltf::Value::IsReal
bool IsReal() const
Definition: tiny_gltf.h:304
tinygltf::BufferView::extras
Value extras
Definition: tiny_gltf.h:801
tinygltf::Primitive::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:983
tinygltf::Buffer::Buffer
Buffer()=default
tinygltf::Animation::operator==
bool operator==(const Animation &) const
tinygltf::AnimationChannel::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:529
tinygltf::PositionalEmitter::extras
Value extras
Definition: tiny_gltf.h:1139
tinygltf::Image::pixel_type
int pixel_type
Definition: tiny_gltf.h:632
tinygltf::Accessor::Sparse::isSparse
bool isSparse
Definition: tiny_gltf.h:838
tinygltf::OrthographicCamera::zfar
double zfar
Definition: tiny_gltf.h:931
tinygltf::Accessor::Sparse::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:846
tinygltf::BufferView::byteStride
size_t byteStride
Definition: tiny_gltf.h:797
tinygltf::Value::object_value_
Object object_value_
Definition: tiny_gltf.h:393
tinygltf::TextureInfo::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:689
tinygltf::Material
Definition: tiny_gltf.h:759
tinygltf::Light::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1123
tinygltf::AudioEmitter::positional
PositionalEmitter positional
Definition: tiny_gltf.h:1171
tinygltf::Buffer::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1044
tinygltf::OcclusionTextureInfo::index
int index
Definition: tiny_gltf.h:717
tinygltf::TextureInfo::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:685
tinygltf::Value::real_value_
double real_value_
Definition: tiny_gltf.h:389
tinygltf::FsCallbacks::FileExists
FileExistsFunction FileExists
Definition: tiny_gltf.h:1362
tinygltf::Skin::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:588
tinygltf::Parameter::string_value
std::string string_value
Definition: tiny_gltf.h:433
tinygltf::Scene
Definition: tiny_gltf.h:1072
stbi_write_png_to_func
STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes)
conscience_core::bridging::commands::environment_entities::bool
const string const string EntityVideoSourcesCommandDataType CscPoint3d CscPoint3d bool
Definition: environmentEntitiesCommands.h:545
tinygltf::Sampler
Definition: tiny_gltf.h:595
tinygltf::Image::bits
int bits
Definition: tiny_gltf.h:631
tinygltf::Mesh::primitives
std::vector< Primitive > primitives
Definition: tiny_gltf.h:992
TINYGLTF_TYPE_VEC2
#define TINYGLTF_TYPE_VEC2
Definition: tiny_gltf.h:144
TINYGLTF_COMPONENT_TYPE_BYTE
#define TINYGLTF_COMPONENT_TYPE_BYTE
Definition: tiny_gltf.h:90
tinygltf::WriteImageData
bool WriteImageData(const std::string *basepath, const std::string *filename, const Image *image, bool embedImages, const URICallbacks *uri_cb, std::string *out_uri, void *)
tinygltf::Primitive::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:982
tinygltf::Scene::Scene
Scene()=default
tinygltf::Sampler::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:618
tinygltf::Value::GetNumberAsDouble
double GetNumberAsDouble() const
Definition: tiny_gltf.h:315
tinygltf::Mesh::Mesh
Mesh()=default
tinygltf::Accessor::type
int type
Definition: tiny_gltf.h:823
tinygltf::NormalTextureInfo::texCoord
int texCoord
Definition: tiny_gltf.h:698
tinygltf::Light::type
std::string type
Definition: tiny_gltf.h:1109
tinygltf::AudioEmitter::operator==
bool operator==(const AudioEmitter &) const
stbi_load_from_memory
STBIDEF stbi_uc * stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
tinygltf::Animation::extras
Value extras
Definition: tiny_gltf.h:565
tinygltf::Animation::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:570
tinygltf::Sampler::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:615
tinygltf::Skin::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:587
tinygltf::Value::IsBinary
bool IsBinary() const
Definition: tiny_gltf.h:308
tinygltf::ExtensionMap
std::map< std::string, Value > ExtensionMap
Definition: tiny_gltf.h:520
tinygltf::AudioEmitter::gain
double gain
Definition: tiny_gltf.h:1148
tinygltf::Mesh::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:999
tinygltf::PbrMetallicRoughness::extras
Value extras
Definition: tiny_gltf.h:743
tinygltf::AudioEmitter::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1179
tinygltf::Texture
Definition: tiny_gltf.h:661
tinygltf::URICallbacks
Definition: tiny_gltf.h:1285
tinygltf::AnimationSampler::AnimationSampler
AnimationSampler()
Definition: tiny_gltf.h:556
tinygltf::PositionalEmitter::coneOuterGain
double coneOuterGain
Definition: tiny_gltf.h:1129
tinygltf::ReadWholeFileFunction
bool(* ReadWholeFileFunction)(std::vector< unsigned char > *, std::string *, const std::string &, void *)
Definition: tiny_gltf.h:1339
tinygltf::AudioEmitter::loop
bool loop
Definition: tiny_gltf.h:1149
tinygltf::TextureInfo::extras
Value extras
Definition: tiny_gltf.h:684
tinygltf::REQUIRE_BUFFER_VIEWS
@ REQUIRE_BUFFER_VIEWS
Definition: tiny_gltf.h:1255
tinygltf::ARRAY_TYPE
@ ARRAY_TYPE
Definition: tiny_gltf.h:193
tinygltf::Accessor::Sparse::bufferView
int bufferView
Definition: tiny_gltf.h:841
tinygltf::Scene::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1081
tinygltf::Value::binary_value_
std::vector< unsigned char > binary_value_
Definition: tiny_gltf.h:391
tinygltf::Scene::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1082
tinygltf::Skin::joints
std::vector< int > joints
Definition: tiny_gltf.h:581
stbi_write_jpg_to_func
STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)
tinygltf::PerspectiveCamera::extras
Value extras
Definition: tiny_gltf.h:921
tinygltf::Accessor::Sparse::indices
struct tinygltf::Accessor::Sparse::@4 indices
TINYGLTF_COMPONENT_TYPE_FLOAT
#define TINYGLTF_COMPONENT_TYPE_FLOAT
Definition: tiny_gltf.h:96
tinygltf::Value::IsObject
bool IsObject() const
Definition: tiny_gltf.h:312
tinygltf::BufferView::byteOffset
size_t byteOffset
Definition: tiny_gltf.h:795
tinygltf::PositionalEmitter::PositionalEmitter
PositionalEmitter()=default
tinygltf::BufferView::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:802
tinygltf::INT_TYPE
@ INT_TYPE
Definition: tiny_gltf.h:190
tinygltf::Value::Value
Value(const unsigned char *p, size_t n)
Definition: tiny_gltf.h:279
tinygltf::Image::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:644
tinygltf::Animation::Animation
Animation()=default
tinygltf::NO_REQUIRE
@ NO_REQUIRE
Definition: tiny_gltf.h:1248
tinygltf::SpotLight::SpotLight
SpotLight()=default
tinygltf::PerspectiveCamera::znear
double znear
Definition: tiny_gltf.h:914
tinygltf::Material::doubleSided
bool doubleSided
Definition: tiny_gltf.h:765
tinygltf::AudioSource::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1205
tinygltf::TextureInfo::TextureInfo
TextureInfo()=default
tinygltf::Parameter::number_value
double number_value
Definition: tiny_gltf.h:436
tinygltf::Camera::perspective
PerspectiveCamera perspective
Definition: tiny_gltf.h:950
tinygltf::Image::height
int height
Definition: tiny_gltf.h:629
tinygltf::AnimationSampler::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:553
tinygltf::Animation::channels
std::vector< AnimationChannel > channels
Definition: tiny_gltf.h:563
tinygltf::TinyGLTF::SetURICallbacks
void SetURICallbacks(URICallbacks callbacks)
tinygltf::Value::Value
Value(const Array &a)
Definition: tiny_gltf.h:286
tinygltf::WriteWholeFile
bool WriteWholeFile(std::string *err, const std::string &filepath, const std::vector< unsigned char > &contents, void *)
tinygltf::Asset::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1065
tinygltf::AnimationSampler::interpolation
std::string interpolation
Definition: tiny_gltf.h:547
tinygltf::Image::extras
Value extras
Definition: tiny_gltf.h:640
tinygltf::Camera::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:961
tinygltf::Parameter::number_array
std::vector< double > number_array
Definition: tiny_gltf.h:434
tinygltf::AudioSource::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1202
tinygltf::AnimationChannel::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:534
tinygltf::Camera
Definition: tiny_gltf.h:946
tinygltf::AudioSource::mimeType
std::string mimeType
Definition: tiny_gltf.h:1192
tinygltf::NormalTextureInfo::extras
Value extras
Definition: tiny_gltf.h:704
tinygltf::Node::mesh
int mesh
Definition: tiny_gltf.h:1018
tinygltf::SpotLight::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1101
tinygltf::AnimationChannel::sampler
int sampler
Definition: tiny_gltf.h:523
tinygltf::Skin::operator==
bool operator==(const Skin &) const
tinygltf::TextureInfo
Definition: tiny_gltf.h:679
tinygltf::Image::bufferView
int bufferView
Definition: tiny_gltf.h:635
tinygltf::Value::Value
Value(const char *s)
Definition: tiny_gltf.h:278
tinygltf::Skin::extras
Value extras
Definition: tiny_gltf.h:583
tinygltf::AudioEmitter::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1184
tinygltf::TinyGLTF::GetSerializeDefaultValues
bool GetSerializeDefaultValues() const
Definition: tiny_gltf.h:1515
tinygltf::FsCallbacks
Definition: tiny_gltf.h:1361
tinygltf::Skin::skeleton
int skeleton
Definition: tiny_gltf.h:580
tinygltf::Accessor::byteOffset
size_t byteOffset
Definition: tiny_gltf.h:819
tinygltf::OrthographicCamera::OrthographicCamera
OrthographicCamera()=default
tinygltf::Accessor::Sparse::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:844
tinygltf::Value::Value
Value(std::string &&s)
Definition: tiny_gltf.h:276
tinygltf::Material::extras
Value extras
Definition: tiny_gltf.h:780
TINYGLTF_MODE_TRIANGLES
#define TINYGLTF_MODE_TRIANGLES
Definition: tiny_gltf.h:86
tinygltf::Node::Node
Node()=default
tinygltf::OcclusionTextureInfo::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:724
tinygltf::URICallbacks::user_data
void * user_data
Definition: tiny_gltf.h:1289
TINYGLTF_COMPONENT_TYPE_SHORT
#define TINYGLTF_COMPONENT_TYPE_SHORT
Definition: tiny_gltf.h:92
tinygltf::OcclusionTextureInfo::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:727
tinygltf::Node::name
std::string name
Definition: tiny_gltf.h:1016
tinygltf::Accessor::sparse
Sparse sparse
Definition: tiny_gltf.h:862
tinygltf::OcclusionTextureInfo::strength
double strength
Definition: tiny_gltf.h:720
tinygltf::AudioSource::bufferView
int bufferView
Definition: tiny_gltf.h:1190
tinygltf::Parameter
Definition: tiny_gltf.h:430
tinygltf::Material::additionalValues
ParameterMap additionalValues
Definition: tiny_gltf.h:777
tinygltf::Value::int_value_
int int_value_
Definition: tiny_gltf.h:388
tinygltf::ExpandFilePath
std::string ExpandFilePath(const std::string &filepath, void *userdata)
tinygltf::Node
Definition: tiny_gltf.h:1006
tinygltf::BufferView::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:805
tinygltf::Value::Value
Value(const std::string &s)
Definition: tiny_gltf.h:273
tinygltf::PositionalEmitter::maxDistance
double maxDistance
Definition: tiny_gltf.h:1130
tinygltf::Mesh::operator==
bool operator==(const Mesh &) const
tinygltf::Accessor::Sparse::extras
Value extras
Definition: tiny_gltf.h:843
tinygltf::Animation
Definition: tiny_gltf.h:561
tinygltf::Value::Value
Value(double n)
Definition: tiny_gltf.h:272
tinygltf::PositionalEmitter::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1143
tinygltf::TinyGLTF::RemoveImageLoader
void RemoveImageLoader()
tinygltf::Camera::name
std::string name
Definition: tiny_gltf.h:948
tinygltf::Animation::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:569
picojson::object
value::object object
Definition: picojson.h:210
tinygltf::Texture::Texture
Texture()=default
tinygltf::PositionalEmitter::refDistance
double refDistance
Definition: tiny_gltf.h:1131
tinygltf::Node::camera
int camera
Definition: tiny_gltf.h:1014
tinygltf::Buffer::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1048
tinygltf::Mesh::name
std::string name
Definition: tiny_gltf.h:991
tinygltf::Accessor::Sparse::values
struct tinygltf::Accessor::Sparse::@5 values
tinygltf::WriteImageDataFunction
bool(* WriteImageDataFunction)(const std::string *basepath, const std::string *filename, const Image *image, bool embedImages, const URICallbacks *uri_cb, std::string *out_uri, void *user_pointer)
Definition: tiny_gltf.h:1305
jwt::alphabet::index
uint32_t index(const std::array< char, 64 > &alphabet, char symbol)
Definition: base.h:91
tinygltf::Camera::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:962
tinygltf::Model::asset
Asset asset
Definition: tiny_gltf.h:1237
nlohmann::detail::void
j template void())
Definition: json.hpp:4189
tinygltf::Node::lods
std::vector< int > lods
Definition: tiny_gltf.h:1021
tinygltf::Texture::extras
Value extras
Definition: tiny_gltf.h:666
tinygltf::AudioSource::AudioSource
AudioSource()=default
tinygltf::OrthographicCamera::xmag
double xmag
Definition: tiny_gltf.h:929
stb_image.h
picojson::parse
std::string parse(value &out, Iter &pos, const Iter &last)
Definition: picojson.h:1098
dst
char * dst
Definition: lz4.h:832
tinygltf::Node::emitter
int emitter
Definition: tiny_gltf.h:1020
tinygltf::TinyGLTF
Definition: tiny_gltf.h:1399
tinygltf::AnimationSampler::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:550
tinygltf::Model::lights
std::vector< Light > lights
Definition: tiny_gltf.h:1229
tinygltf::TextureInfo::texCoord
int texCoord
Definition: tiny_gltf.h:681
nlohmann::detail::parse_event_t::value
@ value
the parser finished reading a JSON value
tinygltf::Asset::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1060
tinygltf::BufferView::BufferView
BufferView()=default
tinygltf::TextureInfo::operator==
bool operator==(const TextureInfo &) const
tinygltf::SpotLight::extras
Value extras
Definition: tiny_gltf.h:1098
tinygltf::Node::weights
std::vector< double > weights
Definition: tiny_gltf.h:1027
tinygltf::Buffer::data
std::vector< unsigned char > data
Definition: tiny_gltf.h:1039
tinygltf::Model::Model
Model()=default
tinygltf::PerspectiveCamera::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:925
tinygltf::Buffer::operator==
bool operator==(const Buffer &) const
tinygltf::URIEncodeFunction
bool(* URIEncodeFunction)(const std::string &in_uri, const std::string &object_type, std::string *out_uri, void *user_data)
Definition: tiny_gltf.h:1266
tinygltf::Value::Size
size_t Size() const
Definition: tiny_gltf.h:381
tinygltf::Light::spot
SpotLight spot
Definition: tiny_gltf.h:1111
tinygltf::Camera::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:957
source
const char * source
Definition: lz4.h:807
tinygltf::Buffer::uri
std::string uri
Definition: tiny_gltf.h:1041
tinygltf::PbrMetallicRoughness::operator==
bool operator==(const PbrMetallicRoughness &) const
tinygltf::PbrMetallicRoughness::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:747
jwt::json::type
type
Generic JSON types used in JWTs.
Definition: jwt.h:1794
tinygltf::Value::Value
Value(const Object &o)
Definition: tiny_gltf.h:290
tinygltf::BufferView::buffer
int buffer
Definition: tiny_gltf.h:794
tinygltf::OcclusionTextureInfo::extras
Value extras
Definition: tiny_gltf.h:723
tinygltf::SpotLight::innerConeAngle
double innerConeAngle
Definition: tiny_gltf.h:1090
tinygltf::BOOL_TYPE
@ BOOL_TYPE
Definition: tiny_gltf.h:191
TINYGLTF_TYPE_VEC3
#define TINYGLTF_TYPE_VEC3
Definition: tiny_gltf.h:145
tinygltf::TinyGLTF::SetSerializeDefaultValues
void SetSerializeDefaultValues(const bool enabled)
Definition: tiny_gltf.h:1511
tinygltf::TinyGLTF::LoadASCIIFromString
bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, const char *str, const unsigned int length, const std::string &base_dir, unsigned int check_sections=REQUIRE_VERSION)
tinygltf::Value::ArrayLen
size_t ArrayLen() const
Definition: tiny_gltf.h:356
TINYGLTF_TYPE_SCALAR
#define TINYGLTF_TYPE_SCALAR
Definition: tiny_gltf.h:150
tinygltf::TinyGLTF::SetMaxExternalFileSize
void SetMaxExternalFileSize(size_t max_bytes)
Definition: tiny_gltf.h:1543
tinygltf::Scene::nodes
std::vector< int > nodes
Definition: tiny_gltf.h:1074
tinygltf::Image::image
std::vector< unsigned char > image
Definition: tiny_gltf.h:634
tinygltf::Material::name
std::string name
Definition: tiny_gltf.h:760
tinygltf::URICallbacks::decode
URIDecodeFunction decode
Definition: tiny_gltf.h:1287
tinygltf::Light::range
double range
Definition: tiny_gltf.h:1110
tinygltf::Model::images
std::vector< Image > images
Definition: tiny_gltf.h:1224
tinygltf::Light::extras
Value extras
Definition: tiny_gltf.h:1119
tinygltf::Sampler::Sampler
Sampler()=default
tinygltf::AudioSource::extras
Value extras
Definition: tiny_gltf.h:1201
tinygltf::Camera::orthographic
OrthographicCamera orthographic
Definition: tiny_gltf.h:951
tinygltf::Model::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:1244
tinygltf::AnimationChannel::target_extensions_json_string
std::string target_extensions_json_string
Definition: tiny_gltf.h:537
TINYGLTF_TARGET_ARRAY_BUFFER
#define TINYGLTF_TARGET_ARRAY_BUFFER
Definition: tiny_gltf.h:168
tinygltf::Primitive
Definition: tiny_gltf.h:965
tinygltf::TinyGLTF::SetImageLoader
void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data)
tinygltf::AudioEmitter::extras
Value extras
Definition: tiny_gltf.h:1180
stbi_write_bmp_to_func
STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data)
tinygltf::Value::Value
Value(int i)
Definition: tiny_gltf.h:268
tinygltf::REAL_TYPE
@ REAL_TYPE
Definition: tiny_gltf.h:189
tinygltf::TinyGLTF::SetFsCallbacks
void SetFsCallbacks(FsCallbacks callbacks)
tinygltf::TinyGLTF::TinyGLTF
TinyGLTF()
tinygltf::Material::Material
Material()=default
tinygltf::NormalTextureInfo::index
int index
Definition: tiny_gltf.h:697
json.hpp
tinygltf::Strict
@ Strict
Definition: tiny_gltf.h:200
tinygltf::PerspectiveCamera::PerspectiveCamera
PerspectiveCamera()=default
tinygltf::PositionalEmitter::rolloffFactor
double rolloffFactor
Definition: tiny_gltf.h:1132
tinygltf::PositionalEmitter::coneOuterAngle
double coneOuterAngle
Definition: tiny_gltf.h:1128
tinygltf::Model::nodes
std::vector< Node > nodes
Definition: tiny_gltf.h:1222
picojson::object_type
@ object_type
Definition: picojson.h:126
tinygltf::Model::textures
std::vector< Texture > textures
Definition: tiny_gltf.h:1223
tinygltf::Sampler::wrapS
int wrapS
Definition: tiny_gltf.h:605
tinygltf::Light::operator==
bool operator==(const Light &) const
tinygltf::AnimationChannel::operator==
bool operator==(const AnimationChannel &) const
tinygltf::AudioSource::name
std::string name
Definition: tiny_gltf.h:1188
tinygltf::Primitive::mode
int mode
Definition: tiny_gltf.h:973
tinygltf::TinyGLTF::LoadBinaryFromFile
bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, const std::string &filename, unsigned int check_sections=REQUIRE_VERSION)
tinygltf::SpotLight
Definition: tiny_gltf.h:1089
tinygltf::Model::skins
std::vector< Skin > skins
Definition: tiny_gltf.h:1225
tinygltf::Material::emissiveFactor
std::vector< double > emissiveFactor
Definition: tiny_gltf.h:762
tinygltf::Image::operator==
bool operator==(const Image &) const
tinygltf::Image::Image
Image()=default
tinygltf::Mesh::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:998
tinygltf::AnimationChannel::target_extensions
ExtensionMap target_extensions
Definition: tiny_gltf.h:531
tinygltf::BufferView::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:806
tinygltf::OrthographicCamera
Definition: tiny_gltf.h:928
tinygltf::Skin
Definition: tiny_gltf.h:577
tinygltf::Primitive::extras
Value extras
Definition: tiny_gltf.h:979
tinygltf::PerspectiveCamera::aspectRatio
double aspectRatio
Definition: tiny_gltf.h:911
tinygltf::TextureInfo::index
int index
Definition: tiny_gltf.h:680
tinygltf::FsCallbacks::GetFileSizeInBytes
GetFileSizeFunction GetFileSizeInBytes
Definition: tiny_gltf.h:1366
tinygltf::BufferView::target
int target
Definition: tiny_gltf.h:799
tinygltf::PerspectiveCamera::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:920
tinygltf::REQUIRE_SCENES
@ REQUIRE_SCENES
Definition: tiny_gltf.h:1251
TINYGLTF_TYPE_VEC4
#define TINYGLTF_TYPE_VEC4
Definition: tiny_gltf.h:146
tinygltf::Node::skin
int skin
Definition: tiny_gltf.h:1017
tinygltf::Model
Definition: tiny_gltf.h:1209
tinygltf::Accessor
Definition: tiny_gltf.h:815
tinygltf::Model::audioSources
std::vector< AudioSource > audioSources
Definition: tiny_gltf.h:1231
std
Definition: json.hpp:4598
tinygltf::Buffer::extras
Value extras
Definition: tiny_gltf.h:1043
tinygltf::Primitive::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:978
tinygltf::Value::Get
const Value & Get(const std::string &key) const
Definition: tiny_gltf.h:349
TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE
Definition: tiny_gltf.h:91
tinygltf::PbrMetallicRoughness
Definition: tiny_gltf.h:736
TINYGLTF_COMPONENT_TYPE_INT
#define TINYGLTF_COMPONENT_TYPE_INT
Definition: tiny_gltf.h:94
tinygltf::Accessor::Sparse::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:845
tinygltf::Material::values
ParameterMap values
Definition: tiny_gltf.h:776
tinygltf::LoadImageDataFunction
bool(* LoadImageDataFunction)(Image *, const int, std::string *, std::string *, int, int, const unsigned char *, int, void *user_pointer)
Definition: tiny_gltf.h:1295
tinygltf::Primitive::targets
std::vector< std::map< std::string, int > > targets
Definition: tiny_gltf.h:974
tinygltf::Value::GetNumberAsInt
int GetNumberAsInt() const
Definition: tiny_gltf.h:325
tinygltf::Model::cameras
std::vector< Camera > cameras
Definition: tiny_gltf.h:1227
tinygltf::FsCallbacks::ExpandFilePath
ExpandFilePathFunction ExpandFilePath
Definition: tiny_gltf.h:1363
tinygltf::Light::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1122
tinygltf::Texture::operator==
bool operator==(const Texture &) const
picojson::copy
void copy(const std::string &s, Iter oi)
Definition: picojson.h:525
tinygltf::Skin::inverseBindMatrices
int inverseBindMatrices
Definition: tiny_gltf.h:579
tinygltf::Parameter::json_double_value
std::map< std::string, double > json_double_value
Definition: tiny_gltf.h:435
picojson::array
value::array array
Definition: picojson.h:209
tinygltf::Primitive::Primitive
Primitive()=default
tinygltf::ColorValue
std::array< double, 4 > ColorValue
Aggregate object for representing a color.
Definition: tiny_gltf.h:426
tinygltf::Value::Keys
std::vector< std::string > Keys() const
Definition: tiny_gltf.h:369
tinygltf::Texture::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:667
tinygltf::AudioEmitter
Definition: tiny_gltf.h:1146
tinygltf::Model::bufferViews
std::vector< BufferView > bufferViews
Definition: tiny_gltf.h:1219
TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
Definition: tiny_gltf.h:95
tinygltf::Image::mimeType
std::string mimeType
Definition: tiny_gltf.h:636
tinygltf::Scene::extras
Value extras
Definition: tiny_gltf.h:1078
tinygltf::Light::name
std::string name
Definition: tiny_gltf.h:1106
tinygltf::Animation::name
std::string name
Definition: tiny_gltf.h:562
tinygltf::Asset
Definition: tiny_gltf.h:1055
tinygltf::Sampler::name
std::string name
Definition: tiny_gltf.h:596
tinygltf::TinyGLTF::GetStoreOriginalJSONForExtrasAndExtensions
bool GetStoreOriginalJSONForExtrasAndExtensions() const
Definition: tiny_gltf.h:1526
tinygltf::Model::extensionsUsed
std::vector< std::string > extensionsUsed
Definition: tiny_gltf.h:1234
tinygltf::Parameter::Parameter
Parameter()=default
tinygltf::GetComponentSizeInBytes
static int32_t GetComponentSizeInBytes(uint32_t componentType)
Definition: tiny_gltf.h:203
tinygltf::ParseStrictness
ParseStrictness
Definition: tiny_gltf.h:198
tinygltf::Accessor::ByteStride
int ByteStride(const BufferView &bufferViewObject) const
Definition: tiny_gltf.h:868
tinygltf::AnimationSampler::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:554
tinygltf::Value::IsBool
bool IsBool() const
Definition: tiny_gltf.h:298
c
double c
Definition: HybridAStar.cpp:84
TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
Definition: tiny_gltf.h:169
tinygltf::FsCallbacks::ReadWholeFile
ReadWholeFileFunction ReadWholeFile
Definition: tiny_gltf.h:1364
tinygltf::Skin::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:584
tinygltf::Sampler::operator==
bool operator==(const Sampler &) const
tinygltf::Camera::Camera
Camera()=default
tinygltf::TextureInfo::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:688
tinygltf::Image::width
int width
Definition: tiny_gltf.h:628
tinygltf::Parameter::TextureStrength
double TextureStrength() const
Definition: tiny_gltf.h:481
tinygltf::Model::animations
std::vector< Animation > animations
Definition: tiny_gltf.h:1217
tinygltf::Material::lods
std::vector< int > lods
Definition: tiny_gltf.h:766
s
double s
Definition: HybridAStar.cpp:85
tinygltf::SpotLight::operator==
bool operator==(const SpotLight &) const
tinygltf::Asset::minVersion
std::string minVersion
Definition: tiny_gltf.h:1058
tinygltf::BufferView::byteLength
size_t byteLength
Definition: tiny_gltf.h:796
tinygltf::PositionalEmitter::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:1138
tinygltf::OcclusionTextureInfo::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:728
tinygltf::AnimationChannel::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:535
tinygltf::AudioEmitter::name
std::string name
Definition: tiny_gltf.h:1147
tinygltf::Accessor::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:828
tinygltf::Value::IsArray
bool IsArray() const
Definition: tiny_gltf.h:310
tinygltf::REQUIRE_BUFFERS
@ REQUIRE_BUFFERS
Definition: tiny_gltf.h:1254
tinygltf::PbrMetallicRoughness::extensions_json_string
std::string extensions_json_string
Definition: tiny_gltf.h:748
tinygltf::AnimationChannel::AnimationChannel
AnimationChannel()=default
tinygltf::Camera::extras
Value extras
Definition: tiny_gltf.h:958
tinygltf::REQUIRE_ALL
@ REQUIRE_ALL
Definition: tiny_gltf.h:1256
tinygltf::IsDataURI
bool IsDataURI(const std::string &in)
tinygltf::Accessor::Sparse::componentType
int componentType
Definition: tiny_gltf.h:842
tinygltf::Material::pbrMetallicRoughness
PbrMetallicRoughness pbrMetallicRoughness
Definition: tiny_gltf.h:768
tinygltf::Primitive::material
int material
Definition: tiny_gltf.h:970
tinygltf::GetFileSizeInBytes
bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, const std::string &filepath, void *)
f
double f
Definition: HybridAStar.cpp:190
tinygltf::NormalTextureInfo::NormalTextureInfo
NormalTextureInfo()=default
tinygltf::Asset::operator==
bool operator==(const Asset &) const
tinygltf::Node::children
std::vector< int > children
Definition: tiny_gltf.h:1022
tinygltf::OrthographicCamera::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:938
tinygltf::TinyGLTF::LoadASCIIFromFile
bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, const std::string &filename, unsigned int check_sections=REQUIRE_VERSION)
tinygltf::Image::extensions
ExtensionMap extensions
Definition: tiny_gltf.h:641
tinygltf::AudioEmitter::playing
bool playing
Definition: tiny_gltf.h:1150
src
const char * src
Definition: lz4.h:865
tinygltf::AnimationSampler::operator==
bool operator==(const AnimationSampler &) const
tinygltf::TinyGLTF::SetImageWriter
void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data)
tinygltf::AnimationChannel::extras
Value extras
Definition: tiny_gltf.h:528
tinygltf::Light::color
std::vector< double > color
Definition: tiny_gltf.h:1107
tinygltf::Accessor::Sparse::byteOffset
size_t byteOffset
Definition: tiny_gltf.h:840
tinygltf::Scene::name
std::string name
Definition: tiny_gltf.h:1073
tinygltf::Accessor::Sparse::count
int count
Definition: tiny_gltf.h:837
tinygltf::ExpandFilePathFunction
std::string(* ExpandFilePathFunction)(const std::string &, void *)
Definition: tiny_gltf.h:1334
tinygltf::TinyGLTF::SetParseStrictness
void SetParseStrictness(ParseStrictness strictness)
tinygltf::AnimationChannel::target_extras
Value target_extras
Definition: tiny_gltf.h:530
tinygltf::Material::alphaMode
std::string alphaMode
Definition: tiny_gltf.h:763
tinygltf::Asset::generator
std::string generator
Definition: tiny_gltf.h:1057
tinygltf::Value::Value
Value()=default
tinygltf::AnimationSampler
Definition: tiny_gltf.h:544
tinygltf::Asset::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1064
tinygltf::Model::extensionsRequired
std::vector< std::string > extensionsRequired
Definition: tiny_gltf.h:1235
tinygltf::NormalTextureInfo::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:708
tinygltf::REQUIRE_VERSION
@ REQUIRE_VERSION
Definition: tiny_gltf.h:1249
tinygltf::Model::scenes
std::vector< Scene > scenes
Definition: tiny_gltf.h:1228
tinygltf::Model::materials
std::vector< Material > materials
Definition: tiny_gltf.h:1220
nlohmann::detail::parse_event_t::key
@ key
the parser read a key of a value in an object
tinygltf::Accessor::extras
Value extras
Definition: tiny_gltf.h:824
tinygltf::Skin::Skin
Skin()=default
tinygltf::TinyGLTF::SetStoreOriginalJSONForExtrasAndExtensions
void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled)
Definition: tiny_gltf.h:1522
tinygltf::Value::Value
Value(Object &&o) noexcept
Definition: tiny_gltf.h:291
tinygltf::OcclusionTextureInfo::texCoord
int texCoord
Definition: tiny_gltf.h:718
tinygltf::BufferView::operator==
bool operator==(const BufferView &) const
tinygltf::Accessor::operator==
bool operator==(const tinygltf::Accessor &) const
tinygltf::Node::scale
std::vector< double > scale
Definition: tiny_gltf.h:1024
tinygltf::Camera::type
std::string type
Definition: tiny_gltf.h:947
tinygltf::Value::IsInt
bool IsInt() const
Definition: tiny_gltf.h:300
tinygltf::AudioSource::uri
std::string uri
Definition: tiny_gltf.h:1189
tinygltf::AudioSource
Definition: tiny_gltf.h:1187
tinygltf::PerspectiveCamera
Definition: tiny_gltf.h:910
TINYGLTF_TEXTURE_WRAP_REPEAT
#define TINYGLTF_TEXTURE_WRAP_REPEAT
Definition: tiny_gltf.h:110
tinygltf::Material::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:783
tinygltf::TinyGLTF::SetPreserveImageChannels
void SetPreserveImageChannels(bool onoff)
Definition: tiny_gltf.h:1534
tinygltf::PbrMetallicRoughness::PbrMetallicRoughness
PbrMetallicRoughness()=default
stbi_image_free
STBIDEF void stbi_image_free(void *retval_from_stbi_load)
tinygltf::AudioEmitter::type
std::string type
Definition: tiny_gltf.h:1152
tinygltf::PbrMetallicRoughness::roughnessFactor
double roughnessFactor
Definition: tiny_gltf.h:740
tinygltf::Material::occlusionTexture
OcclusionTextureInfo occlusionTexture
Definition: tiny_gltf.h:771
tinygltf::Parameter::TextureScale
double TextureScale() const
Definition: tiny_gltf.h:469
tinygltf::Image::uri
std::string uri
Definition: tiny_gltf.h:638
tinygltf::NormalTextureInfo
Definition: tiny_gltf.h:696
tinygltf::PositionalEmitter::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:1142
tinygltf::DecodeDataURI
bool DecodeDataURI(std::vector< unsigned char > *out, std::string &mime_type, const std::string &in, size_t reqBytes, bool checkSize)
tinygltf::Material::operator==
bool operator==(const Material &) const
tinygltf::Image::as_is
bool as_is
Definition: tiny_gltf.h:653
tinygltf::Node::light
int light
Definition: tiny_gltf.h:1019
tinygltf::Scene::audioEmitters
std::vector< int > audioEmitters
Definition: tiny_gltf.h:1075
tinygltf::Buffer
Definition: tiny_gltf.h:1037
TINYGLTF_VALUE_GET
#define TINYGLTF_VALUE_GET(ctype, var)
Definition: tiny_gltf.h:401
tinygltf::Value
Definition: tiny_gltf.h:260
j
int j
Definition: HybridAStar.cpp:192
tinygltf::Mesh::extras
Value extras
Definition: tiny_gltf.h:995
tinygltf::FsCallbacks::user_data
void * user_data
Definition: tiny_gltf.h:1369
tinygltf::BufferView::name
std::string name
Definition: tiny_gltf.h:793
tinygltf::LoadImageData
bool LoadImageData(Image *image, const int image_idx, std::string *err, std::string *warn, int req_width, int req_height, const unsigned char *bytes, int size, void *)
tinygltf::Model::buffers
std::vector< Buffer > buffers
Definition: tiny_gltf.h:1218
tinygltf::Node::operator==
bool operator==(const Node &) const
tinygltf::REQUIRE_NODES
@ REQUIRE_NODES
Definition: tiny_gltf.h:1252
TINYGLTF_TYPE_MAT4
#define TINYGLTF_TYPE_MAT4
Definition: tiny_gltf.h:149
tinygltf::AnimationChannel::target_path
std::string target_path
Definition: tiny_gltf.h:526
tinygltf::Primitive::attributes
std::map< std::string, int > attributes
Definition: tiny_gltf.h:966
tinygltf::Accessor::maxValues
std::vector< double > maxValues
Definition: tiny_gltf.h:834
tinygltf::PerspectiveCamera::extras_json_string
std::string extras_json_string
Definition: tiny_gltf.h:924
i
int i
Definition: HybridAStar.cpp:191