Coverage Report

Created: 2024-02-11 07:19

/src/tinygltf/tiny_gltf.h
Line
Count
Source (jump to first uncovered line)
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
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
31.1M
  ~x() = default;                      \
tinygltf::Value::~Value()
Line
Count
Source
74
17.2M
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::Light::~Light()
Unexecuted instantiation: tinygltf::SpotLight::~SpotLight()
Unexecuted instantiation: tinygltf::AudioEmitter::~AudioEmitter()
Unexecuted instantiation: tinygltf::PositionalEmitter::~PositionalEmitter()
tinygltf::AudioSource::~AudioSource()
Line
Count
Source
74
2.40M
  ~x() = default;                      \
tinygltf::AnimationChannel::~AnimationChannel()
Line
Count
Source
74
12.1k
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::AnimationSampler::~AnimationSampler()
tinygltf::Scene::~Scene()
Line
Count
Source
74
265k
  ~x() = default;                      \
tinygltf::Camera::~Camera()
Line
Count
Source
74
28
  ~x() = default;                      \
tinygltf::OrthographicCamera::~OrthographicCamera()
Line
Count
Source
74
28
  ~x() = default;                      \
tinygltf::PerspectiveCamera::~PerspectiveCamera()
Line
Count
Source
74
28
  ~x() = default;                      \
tinygltf::Sampler::~Sampler()
Line
Count
Source
74
1.10M
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::Skin::~Skin()
tinygltf::Image::~Image()
Line
Count
Source
74
229k
  ~x() = default;                      \
tinygltf::Texture::~Texture()
Line
Count
Source
74
534k
  ~x() = default;                      \
tinygltf::Node::~Node()
Line
Count
Source
74
284k
  ~x() = default;                      \
tinygltf::Mesh::~Mesh()
Line
Count
Source
74
1.07M
  ~x() = default;                      \
tinygltf::Primitive::~Primitive()
Line
Count
Source
74
12.0k
  ~x() = default;                      \
tinygltf::Material::~Material()
Line
Count
Source
74
1.13M
  ~x() = default;                      \
tinygltf::Parameter::~Parameter()
Line
Count
Source
74
32.4k
  ~x() = default;                      \
tinygltf::TextureInfo::~TextureInfo()
Line
Count
Source
74
3.41M
  ~x() = default;                      \
tinygltf::OcclusionTextureInfo::~OcclusionTextureInfo()
Line
Count
Source
74
1.13M
  ~x() = default;                      \
tinygltf::NormalTextureInfo::~NormalTextureInfo()
Line
Count
Source
74
1.13M
  ~x() = default;                      \
tinygltf::PbrMetallicRoughness::~PbrMetallicRoughness()
Line
Count
Source
74
1.13M
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::BufferView::~BufferView()
Unexecuted instantiation: tinygltf::Buffer::~Buffer()
tinygltf::Animation::~Animation()
Line
Count
Source
74
43.0k
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::Accessor::~Accessor()
tinygltf::Model::~Model()
Line
Count
Source
74
1.82k
  ~x() = default;                      \
tinygltf::Asset::~Asset()
Line
Count
Source
74
1.82k
  ~x() = default;                      \
75
0
  x(const x &) = default;              \
Unexecuted instantiation: tinygltf::Value::Value(tinygltf::Value const&)
Unexecuted instantiation: tinygltf::AnimationChannel::AnimationChannel(tinygltf::AnimationChannel const&)
Unexecuted instantiation: tinygltf::AnimationSampler::AnimationSampler(tinygltf::AnimationSampler const&)
76
20.9M
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Value::Value(tinygltf::Value&&)
Line
Count
Source
76
10.8M
  x(x &&) TINYGLTF_NOEXCEPT = default; \
Unexecuted instantiation: tinygltf::Buffer::Buffer(tinygltf::Buffer&&)
Unexecuted instantiation: tinygltf::BufferView::BufferView(tinygltf::BufferView&&)
Unexecuted instantiation: tinygltf::Accessor::Accessor(tinygltf::Accessor&&)
tinygltf::Primitive::Primitive(tinygltf::Primitive&&)
Line
Count
Source
76
8.00k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Mesh::Mesh(tinygltf::Mesh&&)
Line
Count
Source
76
800k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Node::Node(tinygltf::Node&&)
Line
Count
Source
76
210k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Scene::Scene(tinygltf::Scene&&)
Line
Count
Source
76
197k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Parameter::Parameter(tinygltf::Parameter&&)
Line
Count
Source
76
14.9k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Material::Material(tinygltf::Material&&)
Line
Count
Source
76
826k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::PbrMetallicRoughness::PbrMetallicRoughness(tinygltf::PbrMetallicRoughness&&)
Line
Count
Source
76
826k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::NormalTextureInfo::NormalTextureInfo(tinygltf::NormalTextureInfo&&)
Line
Count
Source
76
826k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::OcclusionTextureInfo::OcclusionTextureInfo(tinygltf::OcclusionTextureInfo&&)
Line
Count
Source
76
826k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::TextureInfo::TextureInfo(tinygltf::TextureInfo&&)
Line
Count
Source
76
2.47M
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Image::Image(tinygltf::Image&&)
Line
Count
Source
76
168k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Texture::Texture(tinygltf::Texture&&)
Line
Count
Source
76
398k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::AnimationChannel::AnimationChannel(tinygltf::AnimationChannel&&)
Line
Count
Source
76
5.06k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
Unexecuted instantiation: tinygltf::AnimationSampler::AnimationSampler(tinygltf::AnimationSampler&&)
tinygltf::Animation::Animation(tinygltf::Animation&&)
Line
Count
Source
76
31.9k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
Unexecuted instantiation: tinygltf::Skin::Skin(tinygltf::Skin&&)
tinygltf::Sampler::Sampler(tinygltf::Sampler&&)
Line
Count
Source
76
811k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
Unexecuted instantiation: tinygltf::Camera::Camera(tinygltf::Camera&&)
Unexecuted instantiation: tinygltf::PerspectiveCamera::PerspectiveCamera(tinygltf::PerspectiveCamera&&)
Unexecuted instantiation: tinygltf::OrthographicCamera::OrthographicCamera(tinygltf::OrthographicCamera&&)
Unexecuted instantiation: tinygltf::Light::Light(tinygltf::Light&&)
Unexecuted instantiation: tinygltf::SpotLight::SpotLight(tinygltf::SpotLight&&)
Unexecuted instantiation: tinygltf::AudioEmitter::AudioEmitter(tinygltf::AudioEmitter&&)
Unexecuted instantiation: tinygltf::PositionalEmitter::PositionalEmitter(tinygltf::PositionalEmitter&&)
tinygltf::AudioSource::AudioSource(tinygltf::AudioSource&&)
Line
Count
Source
76
1.63M
  x(x &&) TINYGLTF_NOEXCEPT = default; \
77
  x &operator=(const x &) = default;   \
78
1.64M
  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
4.09k
#define TINYGLTF_MODE_TRIANGLES (4)
87
#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
88
#define TINYGLTF_MODE_TRIANGLE_FAN (6)
89
90
0
#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
91
19.0k
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
92
#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
93
1.37k
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
94
#define TINYGLTF_COMPONENT_TYPE_INT (5124)
95
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
96
0
#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
97
#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
98
0
  (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
588k
#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
0
#define TINYGLTF_TYPE_VEC2 (2)
145
0
#define TINYGLTF_TYPE_VEC3 (3)
146
0
#define TINYGLTF_TYPE_VEC4 (4)
147
0
#define TINYGLTF_TYPE_MAT2 (32 + 2)
148
0
#define TINYGLTF_TYPE_MAT3 (32 + 3)
149
0
#define TINYGLTF_TYPE_MAT4 (32 + 4)
150
0
#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
0
#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
169
0
#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
0
#define TINYGLTF_DOUBLE_EPS (1.e-12)
175
0
#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 {
188
  NULL_TYPE,
189
  REAL_TYPE,
190
  INT_TYPE,
191
  BOOL_TYPE,
192
  STRING_TYPE,
193
  ARRAY_TYPE,
194
  BINARY_TYPE,
195
  OBJECT_TYPE
196
} Type;
197
198
typedef enum {
199
  Permissive,
200
  Strict
201
} ParseStrictness;
202
203
0
static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
204
0
  if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
205
0
    return 1;
206
0
  } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
207
0
    return 1;
208
0
  } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
209
0
    return 2;
210
0
  } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
211
0
    return 2;
212
0
  } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
213
0
    return 4;
214
0
  } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
215
0
    return 4;
216
0
  } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
217
0
    return 4;
218
0
  } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
219
0
    return 8;
220
0
  } else {
221
0
    // Unknown component type
222
0
    return -1;
223
0
  }
224
0
}
225
226
0
static inline int32_t GetNumComponentsInType(uint32_t ty) {
227
0
  if (ty == TINYGLTF_TYPE_SCALAR) {
228
0
    return 1;
229
0
  } else if (ty == TINYGLTF_TYPE_VEC2) {
230
0
    return 2;
231
0
  } else if (ty == TINYGLTF_TYPE_VEC3) {
232
0
    return 3;
233
0
  } else if (ty == TINYGLTF_TYPE_VEC4) {
234
0
    return 4;
235
0
  } else if (ty == TINYGLTF_TYPE_MAT2) {
236
0
    return 4;
237
0
  } else if (ty == TINYGLTF_TYPE_MAT3) {
238
0
    return 9;
239
0
  } else if (ty == TINYGLTF_TYPE_MAT4) {
240
0
    return 16;
241
0
  } else {
242
0
    // Unknown component type
243
0
    return -1;
244
0
  }
245
0
}
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
5.57M
  Value() = default;
266
267
198
  explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
268
775k
  explicit Value(int i) : type_(INT_TYPE) {
269
775k
    int_value_ = i;
270
775k
    real_value_ = i;
271
775k
  }
272
4.56k
  explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
273
0
  explicit Value(const std::string &s) : type_(STRING_TYPE) {
274
0
    string_value_ = s;
275
0
  }
276
  explicit Value(std::string &&s)
277
2.90k
      : type_(STRING_TYPE), string_value_(std::move(s)) {}
278
0
  explicit Value(const char *s) : type_(STRING_TYPE) { string_value_ = s; }
279
0
  explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
280
0
    binary_value_.resize(n);
281
0
    memcpy(binary_value_.data(), p, n);
282
0
  }
283
  explicit Value(std::vector<unsigned char> &&v) noexcept
284
      : type_(BINARY_TYPE),
285
0
        binary_value_(std::move(v)) {}
286
0
  explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
287
  explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
288
823
                                       array_value_(std::move(a)) {}
289
290
0
  explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
291
  explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
292
15.1k
                                        object_value_(std::move(o)) {}
293
294
  DEFAULT_METHODS(Value)
295
296
1.67M
  char Type() const { return static_cast<char>(type_); }
297
298
0
  bool IsBool() const { return (type_ == BOOL_TYPE); }
299
300
0
  bool IsInt() const { return (type_ == INT_TYPE); }
301
302
0
  bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
303
304
0
  bool IsReal() const { return (type_ == REAL_TYPE); }
305
306
0
  bool IsString() const { return (type_ == STRING_TYPE); }
307
308
0
  bool IsBinary() const { return (type_ == BINARY_TYPE); }
309
310
0
  bool IsArray() const { return (type_ == ARRAY_TYPE); }
311
312
3.99k
  bool IsObject() const { return (type_ == OBJECT_TYPE); }
313
314
  // Use this function if you want to have number value as double.
315
0
  double GetNumberAsDouble() const {
316
0
    if (type_ == INT_TYPE) {
317
0
      return double(int_value_);
318
0
    } else {
319
0
      return real_value_;
320
0
    }
321
0
  }
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
1.98k
  int GetNumberAsInt() const {
326
1.98k
    if (type_ == REAL_TYPE) {
327
1.06k
      return int(real_value_);
328
1.06k
    } else {
329
922
      return int_value_;
330
922
    }
331
1.98k
  }
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
0
  const Value &Get(int idx) const {
341
0
    static Value null_value;
342
0
    assert(IsArray());
343
0
    assert(idx >= 0);
344
0
    return (static_cast<size_t>(idx) < array_value_.size())
345
0
               ? array_value_[static_cast<size_t>(idx)]
346
0
               : null_value;
347
0
  }
348
349
  // Lookup value from a key-value pair
350
1.98k
  const Value &Get(const std::string &key) const {
351
1.98k
    static Value null_value;
352
1.98k
    assert(IsObject());
353
0
    Object::const_iterator it = object_value_.find(key);
354
1.98k
    return (it != object_value_.end()) ? it->second : null_value;
355
1.98k
  }
356
357
0
  size_t ArrayLen() const {
358
0
    if (!IsArray()) return 0;
359
0
    return array_value_.size();
360
0
  }
361
362
  // Valid only for object type.
363
2.00k
  bool Has(const std::string &key) const {
364
2.00k
    if (!IsObject()) return false;
365
2.00k
    Object::const_iterator it = object_value_.find(key);
366
2.00k
    return (it != object_value_.end()) ? true : false;
367
2.00k
  }
368
369
  // List keys
370
0
  std::vector<std::string> Keys() const {
371
0
    std::vector<std::string> keys;
372
0
    if (!IsObject()) return keys;  // empty
373
374
0
    for (Object::const_iterator it = object_value_.begin();
375
0
         it != object_value_.end(); ++it) {
376
0
      keys.push_back(it->first);
377
0
    }
378
379
0
    return keys;
380
0
  }
381
382
0
  size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
383
384
  bool operator==(const tinygltf::Value &other) const;
385
386
 protected:
387
  int type_ = NULL_TYPE;
388
389
  int int_value_ = 0;
390
  double real_value_ = 0.0;
391
  std::string string_value_;
392
  std::vector<unsigned char> binary_value_;
393
  Array array_value_;
394
  Object object_value_;
395
  bool boolean_value_ = false;
396
};
397
398
#ifdef __clang__
399
#pragma clang diagnostic pop
400
#endif
401
402
#define TINYGLTF_VALUE_GET(ctype, var)            \
403
  template <>                                     \
404
0
  inline const ctype &Value::Get<ctype>() const { \
405
0
    return var;                                   \
406
0
  }                                               \
Unexecuted instantiation: bool const& tinygltf::Value::Get<bool>() const
Unexecuted instantiation: double const& tinygltf::Value::Get<double>() const
Unexecuted instantiation: int const& tinygltf::Value::Get<int>() const
Unexecuted instantiation: std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, tinygltf::Value, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, tinygltf::Value> > > const& tinygltf::Value::Get<std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, tinygltf::Value, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, tinygltf::Value> > > >() const
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const& tinygltf::Value::Get<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >() const
Unexecuted instantiation: std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > const& tinygltf::Value::Get<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >() const
Unexecuted instantiation: std::__1::vector<tinygltf::Value, std::__1::allocator<tinygltf::Value> > const& tinygltf::Value::Get<std::__1::vector<tinygltf::Value, std::__1::allocator<tinygltf::Value> > >() const
407
  template <>                                     \
408
0
  inline ctype &Value::Get<ctype>() {             \
409
0
    return var;                                   \
410
0
  }
Unexecuted instantiation: bool& tinygltf::Value::Get<bool>()
Unexecuted instantiation: double& tinygltf::Value::Get<double>()
Unexecuted instantiation: int& tinygltf::Value::Get<int>()
Unexecuted instantiation: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >& tinygltf::Value::Get<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >()
Unexecuted instantiation: std::__1::vector<unsigned char, std::__1::allocator<unsigned char> >& tinygltf::Value::Get<std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >()
Unexecuted instantiation: std::__1::vector<tinygltf::Value, std::__1::allocator<tinygltf::Value> >& tinygltf::Value::Get<std::__1::vector<tinygltf::Value, std::__1::allocator<tinygltf::Value> > >()
Unexecuted instantiation: std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, tinygltf::Value, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, tinygltf::Value> > >& tinygltf::Value::Get<std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, tinygltf::Value, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, tinygltf::Value> > > >()
411
TINYGLTF_VALUE_GET(bool, boolean_value_)
412
TINYGLTF_VALUE_GET(double, real_value_)
413
TINYGLTF_VALUE_GET(int, int_value_)
414
TINYGLTF_VALUE_GET(std::string, string_value_)
415
TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
416
TINYGLTF_VALUE_GET(Value::Array, array_value_)
417
TINYGLTF_VALUE_GET(Value::Object, object_value_)
418
#undef TINYGLTF_VALUE_GET
419
420
#ifdef __clang__
421
#pragma clang diagnostic push
422
#pragma clang diagnostic ignored "-Wc++98-compat"
423
#pragma clang diagnostic ignored "-Wpadded"
424
#endif
425
426
/// Aggregate object for representing a color
427
using ColorValue = std::array<double, 4>;
428
429
// === legacy interface ====
430
// TODO(syoyo): Deprecate `Parameter` class.
431
struct Parameter {
432
  bool bool_value = false;
433
  bool has_number_value = false;
434
  std::string string_value;
435
  std::vector<double> number_array;
436
  std::map<std::string, double> json_double_value;
437
  double number_value = 0.0;
438
439
  // context sensitive methods. depending the type of the Parameter you are
440
  // accessing, these are either valid or not
441
  // If this parameter represent a texture map in a material, will return the
442
  // texture index
443
444
  /// Return the index of a texture if this Parameter is a texture map.
445
  /// Returned value is only valid if the parameter represent a texture from a
446
  /// material
447
0
  int TextureIndex() const {
448
0
    const auto it = json_double_value.find("index");
449
0
    if (it != std::end(json_double_value)) {
450
0
      return int(it->second);
451
0
    }
452
0
    return -1;
453
0
  }
454
455
  /// Return the index of a texture coordinate set if this Parameter is a
456
  /// texture map. Returned value is only valid if the parameter represent a
457
  /// texture from a material
458
0
  int TextureTexCoord() const {
459
0
    const auto it = json_double_value.find("texCoord");
460
0
    if (it != std::end(json_double_value)) {
461
0
      return int(it->second);
462
0
    }
463
0
    // As per the spec, if texCoord is omitted, this parameter is 0
464
0
    return 0;
465
0
  }
466
467
  /// Return the scale of a texture if this Parameter is a normal texture map.
468
  /// Returned value is only valid if the parameter represent a normal texture
469
  /// from a material
470
0
  double TextureScale() const {
471
0
    const auto it = json_double_value.find("scale");
472
0
    if (it != std::end(json_double_value)) {
473
0
      return it->second;
474
0
    }
475
0
    // As per the spec, if scale is omitted, this parameter is 1
476
0
    return 1;
477
0
  }
478
479
  /// Return the strength of a texture if this Parameter is a an occlusion map.
480
  /// Returned value is only valid if the parameter represent an occlusion map
481
  /// from a material
482
0
  double TextureStrength() const {
483
0
    const auto it = json_double_value.find("strength");
484
0
    if (it != std::end(json_double_value)) {
485
0
      return it->second;
486
0
    }
487
0
    // As per the spec, if strength is omitted, this parameter is 1
488
0
    return 1;
489
0
  }
490
491
  /// Material factor, like the roughness or metalness of a material
492
  /// Returned value is only valid if the parameter represent a texture from a
493
  /// material
494
0
  double Factor() const { return number_value; }
495
496
  /// Return the color of a material
497
  /// Returned value is only valid if the parameter represent a texture from a
498
  /// material
499
0
  ColorValue ColorFactor() const {
500
0
    return {
501
0
        {// this aggregate initialize the std::array object, and uses C++11 RVO.
502
0
         number_array[0], number_array[1], number_array[2],
503
0
         (number_array.size() > 3 ? number_array[3] : 1.0)}};
504
0
  }
505
506
17.5k
  Parameter() = default;
507
  DEFAULT_METHODS(Parameter)
508
  bool operator==(const Parameter &) const;
509
};
510
511
#ifdef __clang__
512
#pragma clang diagnostic pop
513
#endif
514
515
#ifdef __clang__
516
#pragma clang diagnostic push
517
#pragma clang diagnostic ignored "-Wpadded"
518
#endif
519
520
typedef std::map<std::string, Parameter> ParameterMap;
521
typedef std::map<std::string, Value> ExtensionMap;
522
523
struct AnimationChannel {
524
  int sampler{-1};          // required
525
  int target_node{-1};      // optional index of the node to target (alternative
526
                            // target should be provided by extension)
527
  std::string target_path;  // required with standard values of ["translation",
528
                            // "rotation", "scale", "weights"]
529
  Value extras;
530
  ExtensionMap extensions;
531
  Value target_extras;
532
  ExtensionMap target_extensions;
533
534
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
535
  std::string extras_json_string;
536
  std::string extensions_json_string;
537
  std::string target_extras_json_string;
538
  std::string target_extensions_json_string;
539
540
7.11k
  AnimationChannel() = default;
541
  DEFAULT_METHODS(AnimationChannel)
542
  bool operator==(const AnimationChannel &) const;
543
};
544
545
struct AnimationSampler {
546
  int input{-1};              // required
547
  int output{-1};             // required
548
  std::string interpolation;  // "LINEAR", "STEP","CUBICSPLINE" or user defined
549
                              // string. default "LINEAR"
550
  Value extras;
551
  ExtensionMap extensions;
552
553
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
554
  std::string extras_json_string;
555
  std::string extensions_json_string;
556
557
0
  AnimationSampler() : interpolation("LINEAR") {}
558
  DEFAULT_METHODS(AnimationSampler)
559
  bool operator==(const AnimationSampler &) const;
560
};
561
562
struct Animation {
563
  std::string name;
564
  std::vector<AnimationChannel> channels;
565
  std::vector<AnimationSampler> samplers;
566
  Value extras;
567
  ExtensionMap extensions;
568
569
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
570
  std::string extras_json_string;
571
  std::string extensions_json_string;
572
573
11.1k
  Animation() = default;
574
  DEFAULT_METHODS(Animation)
575
  bool operator==(const Animation &) const;
576
};
577
578
struct Skin {
579
  std::string name;
580
  int inverseBindMatrices{-1};  // required here but not in the spec
581
  int skeleton{-1};             // The index of the node used as a skeleton root
582
  std::vector<int> joints;      // Indices of skeleton nodes
583
584
  Value extras;
585
  ExtensionMap extensions;
586
587
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
588
  std::string extras_json_string;
589
  std::string extensions_json_string;
590
591
0
  Skin() = default;
592
  DEFAULT_METHODS(Skin)
593
  bool operator==(const Skin &) const;
594
};
595
596
struct Sampler {
597
  std::string name;
598
  // glTF 2.0 spec does not define default value for `minFilter` and
599
  // `magFilter`. Set -1 in TinyGLTF(issue #186)
600
  int minFilter =
601
      -1;  // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
602
           // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
603
           // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
604
  int magFilter =
605
      -1;  // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
606
  int wrapS =
607
      TINYGLTF_TEXTURE_WRAP_REPEAT;  // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
608
                                     // "REPEAT"], default "REPEAT"
609
  int wrapT =
610
      TINYGLTF_TEXTURE_WRAP_REPEAT;  // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
611
                                     // "REPEAT"], default "REPEAT"
612
  // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;  // TinyGLTF extension. currently
613
  // not used.
614
615
  Value extras;
616
  ExtensionMap extensions;
617
618
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
619
  std::string extras_json_string;
620
  std::string extensions_json_string;
621
622
294k
  Sampler() = default;
623
  DEFAULT_METHODS(Sampler)
624
  bool operator==(const Sampler &) const;
625
};
626
627
struct Image {
628
  std::string name;
629
  int width{-1};
630
  int height{-1};
631
  int component{-1};
632
  int bits{-1};        // bit depth per channel. 8(byte), 16 or 32.
633
  int pixel_type{-1};  // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
634
                       // UBYTE(bits = 8) or USHORT(bits = 16)
635
  std::vector<unsigned char> image;
636
  int bufferView{-1};    // (required if no uri)
637
  std::string mimeType;  // (required if no uri) ["image/jpeg", "image/png",
638
                         // "image/bmp", "image/gif"]
639
  std::string uri;       // (required if no mimeType) uri is not decoded(e.g.
640
                         // whitespace may be represented as %20)
641
  Value extras;
642
  ExtensionMap extensions;
643
644
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
645
  std::string extras_json_string;
646
  std::string extensions_json_string;
647
648
  // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
649
  // compressed for "image/jpeg" mime) This feature is good if you use custom
650
  // image loader function. (e.g. delayed decoding of images for faster glTF
651
  // parsing) Default parser for Image does not provide as-is loading feature at
652
  // the moment. (You can manipulate this by providing your own LoadImageData
653
  // function)
654
  bool as_is{false};
655
656
61.2k
  Image() = default;
657
  DEFAULT_METHODS(Image)
658
659
  bool operator==(const Image &) const;
660
};
661
662
struct Texture {
663
  std::string name;
664
665
  int sampler{-1};
666
  int source{-1};
667
  Value extras;
668
  ExtensionMap extensions;
669
670
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
671
  std::string extras_json_string;
672
  std::string extensions_json_string;
673
674
135k
  Texture() = default;
675
  DEFAULT_METHODS(Texture)
676
677
  bool operator==(const Texture &) const;
678
};
679
680
struct TextureInfo {
681
  int index{-1};    // required.
682
  int texCoord{0};  // The set index of texture's TEXCOORD attribute used for
683
                    // texture coordinate mapping.
684
685
  Value extras;
686
  ExtensionMap extensions;
687
688
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
689
  std::string extras_json_string;
690
  std::string extensions_json_string;
691
692
935k
  TextureInfo() = default;
693
  DEFAULT_METHODS(TextureInfo)
694
  bool operator==(const TextureInfo &) const;
695
};
696
697
struct NormalTextureInfo {
698
  int index{-1};    // required
699
  int texCoord{0};  // The set index of texture's TEXCOORD attribute used for
700
                    // texture coordinate mapping.
701
  double scale{
702
      1.0};  // scaledNormal = normalize((<sampled normal texture value>
703
             // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
704
705
  Value extras;
706
  ExtensionMap extensions;
707
708
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
709
  std::string extras_json_string;
710
  std::string extensions_json_string;
711
712
311k
  NormalTextureInfo() = default;
713
  DEFAULT_METHODS(NormalTextureInfo)
714
  bool operator==(const NormalTextureInfo &) const;
715
};
716
717
struct OcclusionTextureInfo {
718
  int index{-1};    // required
719
  int texCoord{0};  // The set index of texture's TEXCOORD attribute used for
720
                    // texture coordinate mapping.
721
  double strength{1.0};  // occludedColor = lerp(color, color * <sampled
722
                         // occlusion texture value>, <occlusion strength>)
723
724
  Value extras;
725
  ExtensionMap extensions;
726
727
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
728
  std::string extras_json_string;
729
  std::string extensions_json_string;
730
731
311k
  OcclusionTextureInfo() = default;
732
  DEFAULT_METHODS(OcclusionTextureInfo)
733
  bool operator==(const OcclusionTextureInfo &) const;
734
};
735
736
// pbrMetallicRoughness class defined in glTF 2.0 spec.
737
struct PbrMetallicRoughness {
738
  std::vector<double> baseColorFactor{1.0, 1.0, 1.0, 1.0};  // len = 4. default [1,1,1,1]
739
  TextureInfo baseColorTexture;
740
  double metallicFactor{1.0};   // default 1
741
  double roughnessFactor{1.0};  // default 1
742
  TextureInfo metallicRoughnessTexture;
743
744
  Value extras;
745
  ExtensionMap extensions;
746
747
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
748
  std::string extras_json_string;
749
  std::string extensions_json_string;
750
751
311k
  PbrMetallicRoughness() = default;
752
  DEFAULT_METHODS(PbrMetallicRoughness)
753
754
  bool operator==(const PbrMetallicRoughness &) const;
755
};
756
757
// Each extension should be stored in a ParameterMap.
758
// members not in the values could be included in the ParameterMap
759
// to keep a single material model
760
struct Material {
761
  std::string name;
762
763
  std::vector<double> emissiveFactor{0.0, 0.0, 0.0};  // length 3. default [0, 0, 0]
764
  std::string alphaMode{"OPAQUE"}; // default "OPAQUE"
765
  double alphaCutoff{0.5};        // default 0.5
766
  bool doubleSided{false};        // default false
767
  std::vector<int> lods;          // level of detail materials (MSFT_lod)
768
769
  PbrMetallicRoughness pbrMetallicRoughness;
770
771
  NormalTextureInfo normalTexture;
772
  OcclusionTextureInfo occlusionTexture;
773
  TextureInfo emissiveTexture;
774
775
  // For backward compatibility
776
  // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
777
  ParameterMap values;
778
  ParameterMap additionalValues;
779
780
  ExtensionMap extensions;
781
  Value extras;
782
783
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
784
  std::string extras_json_string;
785
  std::string extensions_json_string;
786
787
311k
  Material() = default;
788
  DEFAULT_METHODS(Material)
789
790
  bool operator==(const Material &) const;
791
};
792
793
struct BufferView {
794
  std::string name;
795
  int buffer{-1};        // Required
796
  size_t byteOffset{0};  // minimum 0, default 0
797
  size_t byteLength{0};  // required, minimum 1. 0 = invalid
798
  size_t byteStride{0};  // minimum 4, maximum 252 (multiple of 4), default 0 =
799
                         // understood to be tightly packed
800
  int target{0};  // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
801
                  // or attribs. Could be 0 for other data
802
  Value extras;
803
  ExtensionMap extensions;
804
805
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
806
  std::string extras_json_string;
807
  std::string extensions_json_string;
808
809
  bool dracoDecoded{false};  // Flag indicating this has been draco decoded
810
811
0
  BufferView() = default;
812
  DEFAULT_METHODS(BufferView)
813
  bool operator==(const BufferView &) const;
814
};
815
816
struct Accessor {
817
  int bufferView{-1};  // optional in spec but required here since sparse
818
                       // accessor are not supported
819
  std::string name;
820
  size_t byteOffset{0};
821
  bool normalized{false};  // optional.
822
  int componentType{-1};   // (required) One of TINYGLTF_COMPONENT_TYPE_***
823
  size_t count{0};         // required
824
  int type{-1};            // (required) One of TINYGLTF_TYPE_***   ..
825
  Value extras;
826
  ExtensionMap extensions;
827
828
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
829
  std::string extras_json_string;
830
  std::string extensions_json_string;
831
832
  std::vector<double>
833
      minValues;  // optional. integer value is promoted to double
834
  std::vector<double>
835
      maxValues;  // optional. integer value is promoted to double
836
837
  struct Sparse {
838
    int count;
839
    bool isSparse;
840
    struct {
841
      size_t byteOffset;
842
      int bufferView;
843
      int componentType;  // a TINYGLTF_COMPONENT_TYPE_ value
844
      Value extras;
845
      ExtensionMap extensions;
846
      std::string extras_json_string;
847
      std::string extensions_json_string;
848
    } indices;
849
    struct {
850
      int bufferView;
851
      size_t byteOffset;
852
      Value extras;
853
      ExtensionMap extensions;
854
      std::string extras_json_string;
855
      std::string extensions_json_string;
856
    } values;
857
    Value extras;
858
    ExtensionMap extensions;
859
    std::string extras_json_string;
860
    std::string extensions_json_string;
861
  };
862
863
  Sparse sparse;
864
865
  ///
866
  /// Utility function to compute byteStride for a given bufferView object.
867
  /// Returns -1 upon invalid glTF value or parameter configuration.
868
  ///
869
0
  int ByteStride(const BufferView &bufferViewObject) const {
870
0
    if (bufferViewObject.byteStride == 0) {
871
0
      // Assume data is tightly packed.
872
0
      int componentSizeInBytes =
873
0
          GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
874
0
      if (componentSizeInBytes <= 0) {
875
0
        return -1;
876
0
      }
877
0
878
0
      int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
879
0
      if (numComponents <= 0) {
880
0
        return -1;
881
0
      }
882
0
883
0
      return componentSizeInBytes * numComponents;
884
0
    } else {
885
0
      // Check if byteStride is a multiple of the size of the accessor's
886
0
      // component type.
887
0
      int componentSizeInBytes =
888
0
          GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
889
0
      if (componentSizeInBytes <= 0) {
890
0
        return -1;
891
0
      }
892
0
893
0
      if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
894
0
        return -1;
895
0
      }
896
0
      return static_cast<int>(bufferViewObject.byteStride);
897
0
    }
898
0
899
0
    // unreachable return 0;
900
0
  }
901
902
  Accessor()
903
904
0
  {
905
0
    sparse.isSparse = false;
906
0
  }
907
  DEFAULT_METHODS(Accessor)
908
  bool operator==(const tinygltf::Accessor &) const;
909
};
910
911
struct PerspectiveCamera {
912
  double aspectRatio{0.0};  // min > 0
913
  double yfov{0.0};         // required. min > 0
914
  double zfar{0.0};         // min > 0
915
  double znear{0.0};        // required. min > 0
916
917
28
  PerspectiveCamera() = default;
918
  DEFAULT_METHODS(PerspectiveCamera)
919
  bool operator==(const PerspectiveCamera &) const;
920
921
  ExtensionMap extensions;
922
  Value extras;
923
924
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
925
  std::string extras_json_string;
926
  std::string extensions_json_string;
927
};
928
929
struct OrthographicCamera {
930
  double xmag{0.0};   // required. must not be zero.
931
  double ymag{0.0};   // required. must not be zero.
932
  double zfar{0.0};   // required. `zfar` must be greater than `znear`.
933
  double znear{0.0};  // required
934
935
28
  OrthographicCamera() = default;
936
  DEFAULT_METHODS(OrthographicCamera)
937
  bool operator==(const OrthographicCamera &) const;
938
939
  ExtensionMap extensions;
940
  Value extras;
941
942
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
943
  std::string extras_json_string;
944
  std::string extensions_json_string;
945
};
946
947
struct Camera {
948
  std::string type;  // required. "perspective" or "orthographic"
949
  std::string name;
950
951
  PerspectiveCamera perspective;
952
  OrthographicCamera orthographic;
953
954
28
  Camera() = default;
955
  DEFAULT_METHODS(Camera)
956
  bool operator==(const Camera &) const;
957
958
  ExtensionMap extensions;
959
  Value extras;
960
961
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
962
  std::string extras_json_string;
963
  std::string extensions_json_string;
964
};
965
966
struct Primitive {
967
  std::map<std::string, int> attributes;  // (required) A dictionary object of
968
                                          // integer, where each integer
969
                                          // is the index of the accessor
970
                                          // containing an attribute.
971
  int material{-1};  // The index of the material to apply to this primitive
972
                     // when rendering.
973
  int indices{-1};   // The index of the accessor that contains the indices.
974
  int mode{-1};      // one of TINYGLTF_MODE_***
975
  std::vector<std::map<std::string, int> > targets;  // array of morph targets,
976
  // where each target is a dict with attributes in ["POSITION, "NORMAL",
977
  // "TANGENT"] pointing
978
  // to their corresponding accessors
979
  ExtensionMap extensions;
980
  Value extras;
981
982
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
983
  std::string extras_json_string;
984
  std::string extensions_json_string;
985
986
4.09k
  Primitive() = default;
987
  DEFAULT_METHODS(Primitive)
988
  bool operator==(const Primitive &) const;
989
};
990
991
struct Mesh {
992
  std::string name;
993
  std::vector<Primitive> primitives;
994
  std::vector<double> weights;  // weights to be applied to the Morph Targets
995
  ExtensionMap extensions;
996
  Value extras;
997
998
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
999
  std::string extras_json_string;
1000
  std::string extensions_json_string;
1001
1002
272k
  Mesh() = default;
1003
  DEFAULT_METHODS(Mesh)
1004
  bool operator==(const Mesh &) const;
1005
};
1006
1007
class Node {
1008
 public:
1009
73.4k
  Node() = default;
1010
1011
  DEFAULT_METHODS(Node)
1012
1013
  bool operator==(const Node &) const;
1014
1015
  int camera{-1};  // the index of the camera referenced by this node
1016
1017
  std::string name;
1018
  int skin{-1};
1019
  int mesh{-1};
1020
  int light{-1};    // light source index (KHR_lights_punctual)
1021
  int emitter{-1};  // audio emitter index (KHR_audio)
1022
  std::vector<int> lods; // level of detail nodes (MSFT_lod)
1023
  std::vector<int> children;
1024
  std::vector<double> rotation;     // length must be 0 or 4
1025
  std::vector<double> scale;        // length must be 0 or 3
1026
  std::vector<double> translation;  // length must be 0 or 3
1027
  std::vector<double> matrix;       // length must be 0 or 16
1028
  std::vector<double> weights;  // The weights of the instantiated Morph Target
1029
1030
  ExtensionMap extensions;
1031
  Value extras;
1032
1033
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1034
  std::string extras_json_string;
1035
  std::string extensions_json_string;
1036
};
1037
1038
struct Buffer {
1039
  std::string name;
1040
  std::vector<unsigned char> data;
1041
  std::string
1042
      uri;  // considered as required here but not in the spec (need to clarify)
1043
            // uri is not decoded(e.g. whitespace may be represented as %20)
1044
  Value extras;
1045
  ExtensionMap extensions;
1046
1047
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1048
  std::string extras_json_string;
1049
  std::string extensions_json_string;
1050
1051
0
  Buffer() = default;
1052
  DEFAULT_METHODS(Buffer)
1053
  bool operator==(const Buffer &) const;
1054
};
1055
1056
struct Asset {
1057
  std::string version = "2.0";  // required
1058
  std::string generator;
1059
  std::string minVersion;
1060
  std::string copyright;
1061
  ExtensionMap extensions;
1062
  Value extras;
1063
1064
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1065
  std::string extras_json_string;
1066
  std::string extensions_json_string;
1067
1068
1.82k
  Asset() = default;
1069
  DEFAULT_METHODS(Asset)
1070
  bool operator==(const Asset &) const;
1071
};
1072
1073
struct Scene {
1074
  std::string name;
1075
  std::vector<int> nodes;
1076
  std::vector<int> audioEmitters;  // KHR_audio global emitters
1077
1078
  ExtensionMap extensions;
1079
  Value extras;
1080
1081
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1082
  std::string extras_json_string;
1083
  std::string extensions_json_string;
1084
1085
67.7k
  Scene() = default;
1086
  DEFAULT_METHODS(Scene)
1087
  bool operator==(const Scene &) const;
1088
};
1089
1090
struct SpotLight {
1091
  double innerConeAngle{0.0};
1092
  double outerConeAngle{0.7853981634};
1093
1094
0
  SpotLight() = default;
1095
  DEFAULT_METHODS(SpotLight)
1096
  bool operator==(const SpotLight &) const;
1097
1098
  ExtensionMap extensions;
1099
  Value extras;
1100
1101
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1102
  std::string extras_json_string;
1103
  std::string extensions_json_string;
1104
};
1105
1106
struct Light {
1107
  std::string name;
1108
  std::vector<double> color;
1109
  double intensity{1.0};
1110
  std::string type;
1111
  double range{0.0};  // 0.0 = infinite
1112
  SpotLight spot;
1113
1114
0
  Light() = default;
1115
  DEFAULT_METHODS(Light)
1116
1117
  bool operator==(const Light &) const;
1118
1119
  ExtensionMap extensions;
1120
  Value extras;
1121
1122
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1123
  std::string extras_json_string;
1124
  std::string extensions_json_string;
1125
};
1126
1127
struct PositionalEmitter {
1128
  double coneInnerAngle{6.283185307179586};
1129
  double coneOuterAngle{6.283185307179586};
1130
  double coneOuterGain{0.0};
1131
  double maxDistance{100.0};
1132
  double refDistance{1.0};
1133
  double rolloffFactor{1.0};
1134
1135
0
  PositionalEmitter() = default;
1136
  DEFAULT_METHODS(PositionalEmitter)
1137
  bool operator==(const PositionalEmitter &) const;
1138
1139
  ExtensionMap extensions;
1140
  Value extras;
1141
1142
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1143
  std::string extras_json_string;
1144
  std::string extensions_json_string;
1145
};
1146
1147
struct AudioEmitter {
1148
  std::string name;
1149
  double gain{1.0};
1150
  bool loop{false};
1151
  bool playing{false};
1152
  std::string
1153
      type;  // positional - Positional audio emitters. Using sound cones, the
1154
             // orientation is +Z having the same front side for a glTF asset.
1155
             // global - Global audio emitters are not affected by the position
1156
             // of audio listeners. coneInnerAngle, coneOuterAngle,
1157
             // coneOuterGain, distanceModel, maxDistance, refDistance, and
1158
             // rolloffFactor should all be ignored when set.
1159
  std::string
1160
      distanceModel;  // linear - A linear distance model calculating the
1161
                      // gain induced by the distance according to: 1.0
1162
                      // - rolloffFactor * (distance - refDistance) /
1163
                      // (maxDistance - refDistance)
1164
                      // inverse - (default) An inverse distance model
1165
                      // calculating the gain induced by the distance according
1166
                      // to: refDistance / (refDistance + rolloffFactor *
1167
                      // (Math.max(distance, refDistance) - refDistance))
1168
                      // exponential - An exponential distance model calculating
1169
                      // the gain induced by the distance according to:
1170
                      // pow((Math.max(distance, refDistance) / refDistance,
1171
                      // -rolloffFactor))
1172
  PositionalEmitter positional;
1173
  int source{-1};
1174
1175
0
  AudioEmitter() : type("global"), distanceModel("inverse") {}
1176
  DEFAULT_METHODS(AudioEmitter)
1177
1178
  bool operator==(const AudioEmitter &) const;
1179
1180
  ExtensionMap extensions;
1181
  Value extras;
1182
1183
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1184
  std::string extras_json_string;
1185
  std::string extensions_json_string;
1186
};
1187
1188
struct AudioSource {
1189
  std::string name;
1190
  std::string uri;
1191
  int bufferView{-1};  // (required if no uri)
1192
  std::string
1193
      mimeType;  // (required if no uri) The audio's MIME type. Required if
1194
                 // bufferView is defined. Unless specified by another
1195
                 // extension, the only supported mimeType is audio/mpeg.
1196
1197
769k
  AudioSource() = default;
1198
  DEFAULT_METHODS(AudioSource)
1199
1200
  bool operator==(const AudioSource &) const;
1201
1202
  Value extras;
1203
  ExtensionMap extensions;
1204
1205
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1206
  std::string extras_json_string;
1207
  std::string extensions_json_string;
1208
};
1209
1210
class Model {
1211
 public:
1212
1.82k
  Model() = default;
1213
  DEFAULT_METHODS(Model)
1214
1215
  bool operator==(const Model &) const;
1216
1217
  std::vector<Accessor> accessors;
1218
  std::vector<Animation> animations;
1219
  std::vector<Buffer> buffers;
1220
  std::vector<BufferView> bufferViews;
1221
  std::vector<Material> materials;
1222
  std::vector<Mesh> meshes;
1223
  std::vector<Node> nodes;
1224
  std::vector<Texture> textures;
1225
  std::vector<Image> images;
1226
  std::vector<Skin> skins;
1227
  std::vector<Sampler> samplers;
1228
  std::vector<Camera> cameras;
1229
  std::vector<Scene> scenes;
1230
  std::vector<Light> lights;
1231
  std::vector<AudioEmitter> audioEmitters;
1232
  std::vector<AudioSource> audioSources;
1233
1234
  int defaultScene{-1};
1235
  std::vector<std::string> extensionsUsed;
1236
  std::vector<std::string> extensionsRequired;
1237
1238
  Asset asset;
1239
1240
  Value extras;
1241
  ExtensionMap extensions;
1242
1243
  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1244
  std::string extras_json_string;
1245
  std::string extensions_json_string;
1246
};
1247
1248
enum SectionCheck {
1249
  NO_REQUIRE = 0x00,
1250
  REQUIRE_VERSION = 0x01,
1251
  REQUIRE_SCENE = 0x02,
1252
  REQUIRE_SCENES = 0x04,
1253
  REQUIRE_NODES = 0x08,
1254
  REQUIRE_ACCESSORS = 0x10,
1255
  REQUIRE_BUFFERS = 0x20,
1256
  REQUIRE_BUFFER_VIEWS = 0x40,
1257
  REQUIRE_ALL = 0x7f
1258
};
1259
1260
///
1261
/// URIEncodeFunction type. Signature for custom URI encoding of external
1262
/// resources such as .bin and image files. Used by tinygltf to re-encode the
1263
/// final location of saved files. object_type may be used to encode buffer and
1264
/// image URIs differently, for example. See
1265
/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris
1266
///
1267
typedef bool (*URIEncodeFunction)(const std::string &in_uri,
1268
                                  const std::string &object_type,
1269
                                  std::string *out_uri, void *user_data);
1270
1271
///
1272
/// URIDecodeFunction type. Signature for custom URI decoding of external
1273
/// resources such as .bin and image files. Used by tinygltf when computing
1274
/// filenames to write resources.
1275
///
1276
typedef bool (*URIDecodeFunction)(const std::string &in_uri,
1277
                                  std::string *out_uri, void *user_data);
1278
1279
// Declaration of default uri decode function
1280
bool URIDecode(const std::string &in_uri, std::string *out_uri,
1281
               void *user_data);
1282
1283
///
1284
/// A structure containing URI callbacks and a pointer to their user data.
1285
///
1286
struct URICallbacks {
1287
  URIEncodeFunction encode;  // Optional encode method
1288
  URIDecodeFunction decode;  // Required decode method
1289
1290
  void *user_data;  // An argument that is passed to all uri callbacks
1291
};
1292
1293
///
1294
/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1295
///
1296
typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1297
                                      std::string *, int, int,
1298
                                      const unsigned char *, int,
1299
                                      void *user_pointer);
1300
1301
///
1302
/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1303
/// The out_uri parameter becomes the URI written to the gltf and may reference
1304
/// a file or contain a data URI.
1305
///
1306
typedef bool (*WriteImageDataFunction)(const std::string *basepath,
1307
                                       const std::string *filename,
1308
                                       const Image *image, bool embedImages,
1309
                                       const URICallbacks *uri_cb,
1310
                                       std::string *out_uri,
1311
                                       void *user_pointer);
1312
1313
#ifndef TINYGLTF_NO_STB_IMAGE
1314
// Declaration of default image loader callback
1315
bool LoadImageData(Image *image, const int image_idx, std::string *err,
1316
                   std::string *warn, int req_width, int req_height,
1317
                   const unsigned char *bytes, int size, void *);
1318
#endif
1319
1320
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1321
// Declaration of default image writer callback
1322
bool WriteImageData(const std::string *basepath, const std::string *filename,
1323
                    const Image *image, bool embedImages,
1324
                    const URICallbacks *uri_cb, std::string *out_uri, void *);
1325
#endif
1326
1327
///
1328
/// FilExistsFunction type. Signature for custom filesystem callbacks.
1329
///
1330
typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
1331
1332
///
1333
/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1334
///
1335
typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
1336
1337
///
1338
/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1339
///
1340
typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
1341
                                      std::string *, const std::string &,
1342
                                      void *);
1343
1344
///
1345
/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1346
///
1347
typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
1348
                                       const std::vector<unsigned char> &,
1349
                                       void *);
1350
1351
///
1352
/// GetFileSizeFunction type. Signature for custom filesystem callbacks.
1353
///
1354
typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err,
1355
                                    const std::string &abs_filename,
1356
                                    void *userdata);
1357
1358
///
1359
/// A structure containing all required filesystem callbacks and a pointer to
1360
/// their user data.
1361
///
1362
struct FsCallbacks {
1363
  FileExistsFunction FileExists;
1364
  ExpandFilePathFunction ExpandFilePath;
1365
  ReadWholeFileFunction ReadWholeFile;
1366
  WriteWholeFileFunction WriteWholeFile;
1367
  GetFileSizeFunction GetFileSizeInBytes;  // To avoid GetFileSize Win32 API,
1368
                                           // add `InBytes` suffix.
1369
1370
  void *user_data;  // An argument that is passed to all fs callbacks
1371
};
1372
1373
#ifndef TINYGLTF_NO_FS
1374
// Declaration of default filesystem callbacks
1375
1376
bool FileExists(const std::string &abs_filename, void *);
1377
1378
///
1379
/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
1380
/// `C:\\Users\\tinygltf\\AppData`)
1381
///
1382
/// @param[in] filepath File path string. Assume UTF-8
1383
/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1384
///
1385
std::string ExpandFilePath(const std::string &filepath, void *userdata);
1386
1387
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1388
                   const std::string &filepath, void *);
1389
1390
bool WriteWholeFile(std::string *err, const std::string &filepath,
1391
                    const std::vector<unsigned char> &contents, void *);
1392
1393
bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
1394
                        const std::string &filepath, void *);
1395
#endif
1396
1397
///
1398
/// glTF Parser/Serializer context.
1399
///
1400
class TinyGLTF {
1401
 public:
1402
#ifdef __clang__
1403
#pragma clang diagnostic push
1404
#pragma clang diagnostic ignored "-Wc++98-compat"
1405
#endif
1406
1407
1.82k
  TinyGLTF() = default;
1408
1409
#ifdef __clang__
1410
#pragma clang diagnostic pop
1411
#endif
1412
1413
1.82k
  ~TinyGLTF() = default;
1414
1415
  ///
1416
  /// Loads glTF ASCII asset from a file.
1417
  /// Set warning message to `warn` for example it fails to load asserts.
1418
  /// Returns false and set error string to `err` if there's an error.
1419
  ///
1420
  bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
1421
                         const std::string &filename,
1422
                         unsigned int check_sections = REQUIRE_VERSION);
1423
1424
  ///
1425
  /// Loads glTF ASCII asset from string(memory).
1426
  /// `length` = strlen(str);
1427
  /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1428
  /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1429
  /// message to `warn` for example it fails to load asserts. Returns false and
1430
  /// set error string to `err` if there's an error.
1431
  ///
1432
  bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1433
                           const char *str, const unsigned int length,
1434
                           const std::string &base_dir,
1435
                           unsigned int check_sections = REQUIRE_VERSION);
1436
1437
  ///
1438
  /// Loads glTF binary asset from a file.
1439
  /// Set warning message to `warn` for example it fails to load asserts.
1440
  /// Returns false and set error string to `err` if there's an error.
1441
  ///
1442
  bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
1443
                          const std::string &filename,
1444
                          unsigned int check_sections = REQUIRE_VERSION);
1445
1446
  ///
1447
  /// Loads glTF binary asset from memory.
1448
  /// `length` = strlen(str);
1449
  /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1450
  /// expanded path (e.g. no tilde(`~`), no environment variables).
1451
  /// Set warning message to `warn` for example it fails to load asserts.
1452
  /// Returns false and set error string to `err` if there's an error.
1453
  ///
1454
  bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
1455
                            const unsigned char *bytes,
1456
                            const unsigned int length,
1457
                            const std::string &base_dir = "",
1458
                            unsigned int check_sections = REQUIRE_VERSION);
1459
1460
  ///
1461
  /// Write glTF to stream, buffers and images will be embedded
1462
  ///
1463
  bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
1464
                              bool prettyPrint, bool writeBinary);
1465
1466
  ///
1467
  /// Write glTF to file.
1468
  ///
1469
  bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
1470
                            bool embedImages, bool embedBuffers,
1471
                            bool prettyPrint, bool writeBinary);
1472
1473
  ///
1474
  /// Sets the parsing strictness.
1475
  ///
1476
  void SetParseStrictness(ParseStrictness strictness);
1477
1478
  ///
1479
  /// Set callback to use for loading image data
1480
  ///
1481
  void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1482
1483
  ///
1484
  /// Unset(remove) callback of loading image data
1485
  ///
1486
  void RemoveImageLoader();
1487
1488
  ///
1489
  /// Set callback to use for writing image data
1490
  ///
1491
  void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1492
1493
  ///
1494
  /// Set callbacks to use for URI encoding and decoding and their user data
1495
  ///
1496
  void SetURICallbacks(URICallbacks callbacks);
1497
1498
  ///
1499
  /// Set callbacks to use for filesystem (fs) access and their user data
1500
  ///
1501
  void SetFsCallbacks(FsCallbacks callbacks);
1502
1503
  ///
1504
  /// Set serializing default values(default = false).
1505
  /// When true, default values are force serialized to .glTF.
1506
  /// This may be helpful if you want to serialize a full description of glTF
1507
  /// data.
1508
  ///
1509
  /// TODO(LTE): Supply parsing option as function arguments to
1510
  /// `LoadASCIIFromFile()` and others, not by a class method
1511
  ///
1512
0
  void SetSerializeDefaultValues(const bool enabled) {
1513
0
    serialize_default_values_ = enabled;
1514
0
  }
1515
1516
0
  bool GetSerializeDefaultValues() const { return serialize_default_values_; }
1517
1518
  ///
1519
  /// Store original JSON string for `extras` and `extensions`.
1520
  /// This feature will be useful when the user want to reconstruct custom data
1521
  /// structure from JSON string.
1522
  ///
1523
0
  void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1524
0
    store_original_json_for_extras_and_extensions_ = enabled;
1525
0
  }
1526
1527
0
  bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1528
0
    return store_original_json_for_extras_and_extensions_;
1529
0
  }
1530
1531
  ///
1532
  /// Specify whether preserve image channels when loading images or not.
1533
  /// (Not effective when the user supplies their own LoadImageData callbacks)
1534
  ///
1535
0
  void SetPreserveImageChannels(bool onoff) {
1536
0
    preserve_image_channels_ = onoff;
1537
0
  }
1538
1539
  ///
1540
  /// Set maximum allowed external file size in bytes.
1541
  /// Default: 2GB
1542
  /// Only effective for built-in ReadWholeFileFunction FS function.
1543
  ///
1544
0
  void SetMaxExternalFileSize(size_t max_bytes) {
1545
0
    max_external_file_size_ = max_bytes;
1546
0
  }
1547
1548
0
  size_t GetMaxExternalFileSize() const { return max_external_file_size_; }
1549
1550
0
  bool GetPreserveImageChannels() const { return preserve_image_channels_; }
1551
1552
 private:
1553
  ///
1554
  /// Loads glTF asset from string(memory).
1555
  /// `length` = strlen(str);
1556
  /// Set warning message to `warn` for example it fails to load asserts
1557
  /// Returns false and set error string to `err` if there's an error.
1558
  ///
1559
  bool LoadFromString(Model *model, std::string *err, std::string *warn,
1560
                      const char *str, const unsigned int length,
1561
                      const std::string &base_dir, unsigned int check_sections);
1562
1563
  const unsigned char *bin_data_ = nullptr;
1564
  size_t bin_size_ = 0;
1565
  bool is_binary_ = false;
1566
1567
  ParseStrictness strictness_ = ParseStrictness::Strict;
1568
1569
  bool serialize_default_values_ = false;  ///< Serialize default values?
1570
1571
  bool store_original_json_for_extras_and_extensions_ = false;
1572
1573
  bool preserve_image_channels_ = false;  /// Default false(expand channels to
1574
                                          /// RGBA) for backward compatibility.
1575
1576
  size_t max_external_file_size_{
1577
      size_t((std::numeric_limits<int32_t>::max)())};  // Default 2GB
1578
1579
  // Warning & error messages
1580
  std::string warn_;
1581
  std::string err_;
1582
1583
  FsCallbacks fs = {
1584
#ifndef TINYGLTF_NO_FS
1585
      &tinygltf::FileExists,
1586
      &tinygltf::ExpandFilePath,
1587
      &tinygltf::ReadWholeFile,
1588
      &tinygltf::WriteWholeFile,
1589
      &tinygltf::GetFileSizeInBytes,
1590
1591
      nullptr  // Fs callback user data
1592
#else
1593
      nullptr, nullptr, nullptr, nullptr, nullptr,
1594
1595
      nullptr  // Fs callback user data
1596
#endif
1597
  };
1598
1599
  URICallbacks uri_cb = {
1600
      // Use paths as-is by default. This will use JSON string escaping.
1601
      nullptr,
1602
      // Decode all URIs before using them as paths as the application may have
1603
      // percent encoded them.
1604
      &tinygltf::URIDecode,
1605
      // URI callback user data
1606
      nullptr};
1607
1608
  LoadImageDataFunction LoadImageData =
1609
#ifndef TINYGLTF_NO_STB_IMAGE
1610
      &tinygltf::LoadImageData;
1611
#else
1612
      nullptr;
1613
#endif
1614
  void *load_image_user_data_{nullptr};
1615
  bool user_image_loader_{false};
1616
1617
  WriteImageDataFunction WriteImageData =
1618
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1619
      &tinygltf::WriteImageData;
1620
#else
1621
      nullptr;
1622
#endif
1623
  void *write_image_user_data_{nullptr};
1624
};
1625
1626
#ifdef __clang__
1627
#pragma clang diagnostic pop  // -Wpadded
1628
#endif
1629
1630
}  // namespace tinygltf
1631
1632
#endif  // TINY_GLTF_H_
1633
1634
#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
1635
#include <algorithm>
1636
// #include <cassert>
1637
#ifndef TINYGLTF_NO_FS
1638
#include <sys/stat.h>  // for is_directory check
1639
1640
#include <cstdio>
1641
#include <fstream>
1642
#endif
1643
#include <sstream>
1644
1645
#ifdef __clang__
1646
// Disable some warnings for external files.
1647
#pragma clang diagnostic push
1648
#pragma clang diagnostic ignored "-Wfloat-equal"
1649
#pragma clang diagnostic ignored "-Wexit-time-destructors"
1650
#pragma clang diagnostic ignored "-Wconversion"
1651
#pragma clang diagnostic ignored "-Wold-style-cast"
1652
#pragma clang diagnostic ignored "-Wglobal-constructors"
1653
#if __has_warning("-Wreserved-id-macro")
1654
#pragma clang diagnostic ignored "-Wreserved-id-macro"
1655
#endif
1656
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1657
#pragma clang diagnostic ignored "-Wpadded"
1658
#pragma clang diagnostic ignored "-Wc++98-compat"
1659
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1660
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1661
#pragma clang diagnostic ignored "-Wswitch-enum"
1662
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
1663
#pragma clang diagnostic ignored "-Wweak-vtables"
1664
#pragma clang diagnostic ignored "-Wcovered-switch-default"
1665
#if __has_warning("-Wdouble-promotion")
1666
#pragma clang diagnostic ignored "-Wdouble-promotion"
1667
#endif
1668
#if __has_warning("-Wcomma")
1669
#pragma clang diagnostic ignored "-Wcomma"
1670
#endif
1671
#if __has_warning("-Wzero-as-null-pointer-constant")
1672
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1673
#endif
1674
#if __has_warning("-Wcast-qual")
1675
#pragma clang diagnostic ignored "-Wcast-qual"
1676
#endif
1677
#if __has_warning("-Wmissing-variable-declarations")
1678
#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1679
#endif
1680
#if __has_warning("-Wmissing-prototypes")
1681
#pragma clang diagnostic ignored "-Wmissing-prototypes"
1682
#endif
1683
#if __has_warning("-Wcast-align")
1684
#pragma clang diagnostic ignored "-Wcast-align"
1685
#endif
1686
#if __has_warning("-Wnewline-eof")
1687
#pragma clang diagnostic ignored "-Wnewline-eof"
1688
#endif
1689
#if __has_warning("-Wunused-parameter")
1690
#pragma clang diagnostic ignored "-Wunused-parameter"
1691
#endif
1692
#if __has_warning("-Wmismatched-tags")
1693
#pragma clang diagnostic ignored "-Wmismatched-tags"
1694
#endif
1695
#if __has_warning("-Wextra-semi-stmt")
1696
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1697
#endif
1698
#endif
1699
1700
// Disable GCC warnings
1701
#ifdef __GNUC__
1702
#pragma GCC diagnostic push
1703
#pragma GCC diagnostic ignored "-Wtype-limits"
1704
#endif  // __GNUC__
1705
1706
#ifndef TINYGLTF_NO_INCLUDE_JSON
1707
#ifndef TINYGLTF_USE_RAPIDJSON
1708
#include "json.hpp"
1709
#else
1710
#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
1711
#include "document.h"
1712
#include "prettywriter.h"
1713
#include "rapidjson.h"
1714
#include "stringbuffer.h"
1715
#include "writer.h"
1716
#endif
1717
#endif
1718
#endif
1719
1720
#ifdef TINYGLTF_ENABLE_DRACO
1721
#include "draco/compression/decode.h"
1722
#include "draco/core/decoder_buffer.h"
1723
#endif
1724
1725
#ifndef TINYGLTF_NO_STB_IMAGE
1726
#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
1727
#include "stb_image.h"
1728
#endif
1729
#endif
1730
1731
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1732
#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
1733
#include "stb_image_write.h"
1734
#endif
1735
#endif
1736
1737
#ifdef __clang__
1738
#pragma clang diagnostic pop
1739
#endif
1740
1741
#ifdef __GNUC__
1742
#pragma GCC diagnostic pop
1743
#endif
1744
1745
#ifdef _WIN32
1746
1747
// issue 143.
1748
// Define NOMINMAX to avoid min/max defines,
1749
// but undef it after included Windows.h
1750
#ifndef NOMINMAX
1751
#define TINYGLTF_INTERNAL_NOMINMAX
1752
#define NOMINMAX
1753
#endif
1754
1755
#ifndef WIN32_LEAN_AND_MEAN
1756
#define WIN32_LEAN_AND_MEAN
1757
#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1758
#endif
1759
#ifndef __MINGW32__
1760
#include <Windows.h>  // include API for expanding a file path
1761
#else
1762
#include <windows.h>
1763
#endif
1764
1765
#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1766
#undef WIN32_LEAN_AND_MEAN
1767
#endif
1768
1769
#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1770
#undef NOMINMAX
1771
#endif
1772
1773
#if defined(__GLIBCXX__)  // mingw
1774
1775
#include <fcntl.h>  // _O_RDONLY
1776
1777
#include <ext/stdio_filebuf.h>  // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
1778
1779
#endif
1780
1781
#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
1782
// #include <wordexp.h>
1783
#endif
1784
1785
#if defined(__sparcv9) || defined(__powerpc__)
1786
// Big endian
1787
#else
1788
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1789
#define TINYGLTF_LITTLE_ENDIAN 1
1790
#endif
1791
#endif
1792
1793
namespace tinygltf {
1794
namespace detail {
1795
#ifdef TINYGLTF_USE_RAPIDJSON
1796
1797
#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1798
// This uses the RapidJSON CRTAllocator.  It is thread safe and multiple
1799
// documents may be active at once.
1800
using json =
1801
    rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1802
using json_iterator = json::MemberIterator;
1803
using json_const_iterator = json::ConstMemberIterator;
1804
using json_const_array_iterator = json const *;
1805
using JsonDocument =
1806
    rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1807
rapidjson::CrtAllocator s_CrtAllocator;  // stateless and thread safe
1808
rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
1809
#else
1810
// This uses the default RapidJSON MemoryPoolAllocator.  It is very fast, but
1811
// not thread safe. Only a single JsonDocument may be active at any one time,
1812
// meaning only a single gltf load/save can be active any one time.
1813
using json = rapidjson::Value;
1814
using json_iterator = json::MemberIterator;
1815
using json_const_iterator = json::ConstMemberIterator;
1816
using json_const_array_iterator = json const *;
1817
rapidjson::Document *s_pActiveDocument = nullptr;
1818
rapidjson::Document::AllocatorType &GetAllocator() {
1819
  assert(s_pActiveDocument);  // Root json node must be JsonDocument type
1820
  return s_pActiveDocument->GetAllocator();
1821
}
1822
1823
#ifdef __clang__
1824
#pragma clang diagnostic push
1825
// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1826
#pragma clang diagnostic ignored "-Wunused-member-function"
1827
#endif
1828
1829
struct JsonDocument : public rapidjson::Document {
1830
  JsonDocument() {
1831
    assert(s_pActiveDocument ==
1832
           nullptr);  // When using default allocator, only one document can be
1833
                      // active at a time, if you need multiple active at once,
1834
                      // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1835
    s_pActiveDocument = this;
1836
  }
1837
  JsonDocument(const JsonDocument &) = delete;
1838
  JsonDocument(JsonDocument &&rhs) noexcept
1839
      : rapidjson::Document(std::move(rhs)) {
1840
    s_pActiveDocument = this;
1841
    rhs.isNil = true;
1842
  }
1843
  ~JsonDocument() {
1844
    if (!isNil) {
1845
      s_pActiveDocument = nullptr;
1846
    }
1847
  }
1848
1849
 private:
1850
  bool isNil = false;
1851
};
1852
1853
#ifdef __clang__
1854
#pragma clang diagnostic pop
1855
#endif
1856
1857
#endif  // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1858
1859
#else
1860
using nlohmann::json;
1861
using json_iterator = json::iterator;
1862
using json_const_iterator = json::const_iterator;
1863
using json_const_array_iterator = json_const_iterator;
1864
using JsonDocument = json;
1865
#endif
1866
1867
void JsonParse(JsonDocument &doc, const char *str, size_t length,
1868
1.82k
               bool throwExc = false) {
1869
#ifdef TINYGLTF_USE_RAPIDJSON
1870
  (void)throwExc;
1871
  doc.Parse(str, length);
1872
#else
1873
1.82k
  doc = detail::json::parse(str, str + length, nullptr, throwExc);
1874
1.82k
#endif
1875
1.82k
}
1876
}  // namespace detail
1877
}  // namespace tinygltf
1878
1879
#ifdef __APPLE__
1880
#include "TargetConditionals.h"
1881
#endif
1882
1883
#ifdef __clang__
1884
#pragma clang diagnostic push
1885
#pragma clang diagnostic ignored "-Wc++98-compat"
1886
#endif
1887
1888
namespace tinygltf {
1889
1890
///
1891
/// Internal LoadImageDataOption struct.
1892
/// This struct is passed through `user_pointer` in LoadImageData.
1893
/// The struct is not passed when the user supply their own LoadImageData
1894
/// callbacks.
1895
///
1896
struct LoadImageDataOption {
1897
  // true: preserve image channels(e.g. load as RGB image if the image has RGB
1898
  // channels) default `false`(channels are expanded to RGBA for backward
1899
  // compatibility).
1900
  bool preserve_channels{false};
1901
};
1902
1903
// Equals function for Value, for recursivity
1904
0
static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1905
0
  if (one.Type() != other.Type()) return false;
1906
1907
0
  switch (one.Type()) {
1908
0
    case NULL_TYPE:
1909
0
      return true;
1910
0
    case BOOL_TYPE:
1911
0
      return one.Get<bool>() == other.Get<bool>();
1912
0
    case REAL_TYPE:
1913
0
      return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1914
0
    case INT_TYPE:
1915
0
      return one.Get<int>() == other.Get<int>();
1916
0
    case OBJECT_TYPE: {
1917
0
      auto oneObj = one.Get<tinygltf::Value::Object>();
1918
0
      auto otherObj = other.Get<tinygltf::Value::Object>();
1919
0
      if (oneObj.size() != otherObj.size()) return false;
1920
0
      for (auto &it : oneObj) {
1921
0
        auto otherIt = otherObj.find(it.first);
1922
0
        if (otherIt == otherObj.end()) return false;
1923
1924
0
        if (!Equals(it.second, otherIt->second)) return false;
1925
0
      }
1926
0
      return true;
1927
0
    }
1928
0
    case ARRAY_TYPE: {
1929
0
      if (one.Size() != other.Size()) return false;
1930
0
      for (int i = 0; i < int(one.Size()); ++i)
1931
0
        if (!Equals(one.Get(i), other.Get(i))) return false;
1932
0
      return true;
1933
0
    }
1934
0
    case STRING_TYPE:
1935
0
      return one.Get<std::string>() == other.Get<std::string>();
1936
0
    case BINARY_TYPE:
1937
0
      return one.Get<std::vector<unsigned char> >() ==
1938
0
             other.Get<std::vector<unsigned char> >();
1939
0
    default: {
1940
      // unhandled type
1941
0
      return false;
1942
0
    }
1943
0
  }
1944
0
}
1945
1946
// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
1947
static bool Equals(const std::vector<double> &one,
1948
0
                   const std::vector<double> &other) {
1949
0
  if (one.size() != other.size()) return false;
1950
0
  for (int i = 0; i < int(one.size()); ++i) {
1951
0
    if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1952
0
  }
1953
0
  return true;
1954
0
}
1955
1956
0
bool Accessor::operator==(const Accessor &other) const {
1957
0
  return this->bufferView == other.bufferView &&
1958
0
         this->byteOffset == other.byteOffset &&
1959
0
         this->componentType == other.componentType &&
1960
0
         this->count == other.count && this->extensions == other.extensions &&
1961
0
         this->extras == other.extras &&
1962
0
         Equals(this->maxValues, other.maxValues) &&
1963
0
         Equals(this->minValues, other.minValues) && this->name == other.name &&
1964
0
         this->normalized == other.normalized && this->type == other.type;
1965
0
}
1966
0
bool Animation::operator==(const Animation &other) const {
1967
0
  return this->channels == other.channels &&
1968
0
         this->extensions == other.extensions && this->extras == other.extras &&
1969
0
         this->name == other.name && this->samplers == other.samplers;
1970
0
}
1971
0
bool AnimationChannel::operator==(const AnimationChannel &other) const {
1972
0
  return this->extensions == other.extensions && this->extras == other.extras &&
1973
0
         this->target_node == other.target_node &&
1974
0
         this->target_path == other.target_path &&
1975
0
         this->sampler == other.sampler;
1976
0
}
1977
0
bool AnimationSampler::operator==(const AnimationSampler &other) const {
1978
0
  return this->extras == other.extras && this->extensions == other.extensions &&
1979
0
         this->input == other.input &&
1980
0
         this->interpolation == other.interpolation &&
1981
0
         this->output == other.output;
1982
0
}
1983
0
bool Asset::operator==(const Asset &other) const {
1984
0
  return this->copyright == other.copyright &&
1985
0
         this->extensions == other.extensions && this->extras == other.extras &&
1986
0
         this->generator == other.generator &&
1987
0
         this->minVersion == other.minVersion && this->version == other.version;
1988
0
}
1989
0
bool Buffer::operator==(const Buffer &other) const {
1990
0
  return this->data == other.data && this->extensions == other.extensions &&
1991
0
         this->extras == other.extras && this->name == other.name &&
1992
0
         this->uri == other.uri;
1993
0
}
1994
0
bool BufferView::operator==(const BufferView &other) const {
1995
0
  return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1996
0
         this->byteOffset == other.byteOffset &&
1997
0
         this->byteStride == other.byteStride && this->name == other.name &&
1998
0
         this->target == other.target && this->extensions == other.extensions &&
1999
0
         this->extras == other.extras &&
2000
0
         this->dracoDecoded == other.dracoDecoded;
2001
0
}
2002
0
bool Camera::operator==(const Camera &other) const {
2003
0
  return this->name == other.name && this->extensions == other.extensions &&
2004
0
         this->extras == other.extras &&
2005
0
         this->orthographic == other.orthographic &&
2006
0
         this->perspective == other.perspective && this->type == other.type;
2007
0
}
2008
0
bool Image::operator==(const Image &other) const {
2009
0
  return this->bufferView == other.bufferView &&
2010
0
         this->component == other.component &&
2011
0
         this->extensions == other.extensions && this->extras == other.extras &&
2012
0
         this->height == other.height && this->image == other.image &&
2013
0
         this->mimeType == other.mimeType && this->name == other.name &&
2014
0
         this->uri == other.uri && this->width == other.width;
2015
0
}
2016
0
bool Light::operator==(const Light &other) const {
2017
0
  return Equals(this->color, other.color) && this->name == other.name &&
2018
0
         this->type == other.type;
2019
0
}
2020
0
bool AudioEmitter::operator==(const AudioEmitter &other) const {
2021
0
  return this->name == other.name &&
2022
0
         TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) &&
2023
0
         this->loop == other.loop && this->playing == other.playing &&
2024
0
         this->type == other.type &&
2025
0
         this->distanceModel == other.distanceModel &&
2026
0
         this->source == other.source;
2027
0
}
2028
0
bool AudioSource::operator==(const AudioSource &other) const {
2029
0
  return this->name == other.name && this->uri == other.uri;
2030
0
}
2031
0
bool Material::operator==(const Material &other) const {
2032
0
  return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
2033
0
         (this->normalTexture == other.normalTexture) &&
2034
0
         (this->occlusionTexture == other.occlusionTexture) &&
2035
0
         (this->emissiveTexture == other.emissiveTexture) &&
2036
0
         Equals(this->emissiveFactor, other.emissiveFactor) &&
2037
0
         (this->alphaMode == other.alphaMode) &&
2038
0
         TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
2039
0
         (this->doubleSided == other.doubleSided) &&
2040
0
         (this->extensions == other.extensions) &&
2041
0
         (this->extras == other.extras) && (this->values == other.values) &&
2042
0
         (this->additionalValues == other.additionalValues) &&
2043
0
         (this->name == other.name);
2044
0
}
2045
0
bool Mesh::operator==(const Mesh &other) const {
2046
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2047
0
         this->name == other.name && Equals(this->weights, other.weights) &&
2048
0
         this->primitives == other.primitives;
2049
0
}
2050
0
bool Model::operator==(const Model &other) const {
2051
0
  return this->accessors == other.accessors &&
2052
0
         this->animations == other.animations && this->asset == other.asset &&
2053
0
         this->buffers == other.buffers &&
2054
0
         this->bufferViews == other.bufferViews &&
2055
0
         this->cameras == other.cameras &&
2056
0
         this->defaultScene == other.defaultScene &&
2057
0
         this->extensions == other.extensions &&
2058
0
         this->extensionsRequired == other.extensionsRequired &&
2059
0
         this->extensionsUsed == other.extensionsUsed &&
2060
0
         this->extras == other.extras && this->images == other.images &&
2061
0
         this->lights == other.lights && this->materials == other.materials &&
2062
0
         this->meshes == other.meshes && this->nodes == other.nodes &&
2063
0
         this->samplers == other.samplers && this->scenes == other.scenes &&
2064
0
         this->skins == other.skins && this->textures == other.textures;
2065
0
}
2066
0
bool Node::operator==(const Node &other) const {
2067
0
  return this->camera == other.camera && this->children == other.children &&
2068
0
         this->extensions == other.extensions && this->extras == other.extras &&
2069
0
         Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
2070
0
         (this->light == other.light) && (this->emitter == other.emitter) &&
2071
0
         this->name == other.name && Equals(this->rotation, other.rotation) &&
2072
0
         Equals(this->scale, other.scale) && this->skin == other.skin &&
2073
0
         Equals(this->translation, other.translation) &&
2074
0
         Equals(this->weights, other.weights);
2075
0
}
2076
0
bool SpotLight::operator==(const SpotLight &other) const {
2077
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2078
0
         TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
2079
0
         TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
2080
0
}
2081
0
bool PositionalEmitter::operator==(const PositionalEmitter &other) const {
2082
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2083
0
         TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) &&
2084
0
         TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) &&
2085
0
         TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) &&
2086
0
         TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) &&
2087
0
         TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) &&
2088
0
         TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor);
2089
0
}
2090
0
bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
2091
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2092
0
         TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
2093
0
         TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
2094
0
         TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2095
0
         TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
2096
0
}
2097
0
bool Parameter::operator==(const Parameter &other) const {
2098
0
  if (this->bool_value != other.bool_value ||
2099
0
      this->has_number_value != other.has_number_value)
2100
0
    return false;
2101
2102
0
  if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
2103
0
    return false;
2104
2105
0
  if (this->json_double_value.size() != other.json_double_value.size())
2106
0
    return false;
2107
0
  for (auto &it : this->json_double_value) {
2108
0
    auto otherIt = other.json_double_value.find(it.first);
2109
0
    if (otherIt == other.json_double_value.end()) return false;
2110
2111
0
    if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
2112
0
  }
2113
2114
0
  if (!Equals(this->number_array, other.number_array)) return false;
2115
2116
0
  if (this->string_value != other.string_value) return false;
2117
2118
0
  return true;
2119
0
}
2120
0
bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
2121
0
  return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
2122
0
         this->extensions == other.extensions && this->extras == other.extras &&
2123
0
         TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
2124
0
         TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2125
0
         TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
2126
0
}
2127
0
bool Primitive::operator==(const Primitive &other) const {
2128
0
  return this->attributes == other.attributes && this->extras == other.extras &&
2129
0
         this->indices == other.indices && this->material == other.material &&
2130
0
         this->mode == other.mode && this->targets == other.targets;
2131
0
}
2132
0
bool Sampler::operator==(const Sampler &other) const {
2133
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2134
0
         this->magFilter == other.magFilter &&
2135
0
         this->minFilter == other.minFilter && this->name == other.name &&
2136
0
         this->wrapS == other.wrapS && this->wrapT == other.wrapT;
2137
2138
  // this->wrapR == other.wrapR
2139
0
}
2140
0
bool Scene::operator==(const Scene &other) const {
2141
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2142
0
         this->name == other.name && this->nodes == other.nodes;
2143
0
}
2144
0
bool Skin::operator==(const Skin &other) const {
2145
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2146
0
         this->inverseBindMatrices == other.inverseBindMatrices &&
2147
0
         this->joints == other.joints && this->name == other.name &&
2148
0
         this->skeleton == other.skeleton;
2149
0
}
2150
0
bool Texture::operator==(const Texture &other) const {
2151
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2152
0
         this->name == other.name && this->sampler == other.sampler &&
2153
0
         this->source == other.source;
2154
0
}
2155
0
bool TextureInfo::operator==(const TextureInfo &other) const {
2156
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2157
0
         this->index == other.index && this->texCoord == other.texCoord;
2158
0
}
2159
0
bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
2160
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2161
0
         this->index == other.index && this->texCoord == other.texCoord &&
2162
0
         TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
2163
0
}
2164
0
bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
2165
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2166
0
         this->index == other.index && this->texCoord == other.texCoord &&
2167
0
         TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
2168
0
}
2169
0
bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2170
0
  return this->extensions == other.extensions && this->extras == other.extras &&
2171
0
         (this->baseColorTexture == other.baseColorTexture) &&
2172
0
         (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
2173
0
         Equals(this->baseColorFactor, other.baseColorFactor) &&
2174
0
         TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2175
0
         TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2176
0
}
2177
0
bool Value::operator==(const Value &other) const {
2178
0
  return Equals(*this, other);
2179
0
}
2180
2181
0
static void swap4(unsigned int *val) {
2182
0
#ifdef TINYGLTF_LITTLE_ENDIAN
2183
0
  (void)val;
2184
#else
2185
  unsigned int tmp = *val;
2186
  unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2187
  unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2188
2189
  dst[0] = src[3];
2190
  dst[1] = src[2];
2191
  dst[2] = src[1];
2192
  dst[3] = src[0];
2193
#endif
2194
0
}
2195
2196
static std::string JoinPath(const std::string &path0,
2197
28.1k
                            const std::string &path1) {
2198
28.1k
  if (path0.empty()) {
2199
14.0k
    return path1;
2200
14.0k
  } else {
2201
    // check '/'
2202
14.0k
    char lastChar = *path0.rbegin();
2203
14.0k
    if (lastChar != '/') {
2204
14.0k
      return path0 + std::string("/") + path1;
2205
14.0k
    } else {
2206
0
      return path0 + path1;
2207
0
    }
2208
14.0k
  }
2209
28.1k
}
2210
2211
static std::string FindFile(const std::vector<std::string> &paths,
2212
42.1k
                            const std::string &filepath, FsCallbacks *fs) {
2213
42.1k
  if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2214
42.1k
      fs->FileExists == nullptr) {
2215
    // Error, fs callback[s] missing
2216
0
    return std::string();
2217
0
  }
2218
2219
  // https://github.com/syoyo/tinygltf/issues/416
2220
  // Use strlen() since std::string's size/length reports the number of elements
2221
  // in the buffer, not the length of string(null-terminated) strip
2222
  // null-character in the middle of string.
2223
42.1k
  size_t slength = strlen(filepath.c_str());
2224
42.1k
  if (slength == 0) {
2225
28.0k
    return std::string();
2226
28.0k
  }
2227
2228
14.0k
  std::string cleaned_filepath = std::string(filepath.c_str());
2229
2230
42.2k
  for (size_t i = 0; i < paths.size(); i++) {
2231
28.1k
    std::string absPath =
2232
28.1k
        fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data);
2233
28.1k
    if (fs->FileExists(absPath, fs->user_data)) {
2234
0
      return absPath;
2235
0
    }
2236
28.1k
  }
2237
2238
14.0k
  return std::string();
2239
14.0k
}
2240
2241
0
static std::string GetFilePathExtension(const std::string &FileName) {
2242
0
  if (FileName.find_last_of(".") != std::string::npos)
2243
0
    return FileName.substr(FileName.find_last_of(".") + 1);
2244
0
  return "";
2245
0
}
2246
2247
0
static std::string GetBaseDir(const std::string &filepath) {
2248
0
  if (filepath.find_last_of("/\\") != std::string::npos)
2249
0
    return filepath.substr(0, filepath.find_last_of("/\\"));
2250
0
  return "";
2251
0
}
2252
2253
0
static std::string GetBaseFilename(const std::string &filepath) {
2254
0
  auto idx = filepath.find_last_of("/\\");
2255
0
  if (idx != std::string::npos) return filepath.substr(idx + 1);
2256
0
  return filepath;
2257
0
}
2258
2259
std::string base64_encode(unsigned char const *, unsigned int len);
2260
std::string base64_decode(std::string const &s);
2261
2262
/*
2263
   base64.cpp and base64.h
2264
2265
   Copyright (C) 2004-2008 René Nyffenegger
2266
2267
   This source code is provided 'as-is', without any express or implied
2268
   warranty. In no event will the author be held liable for any damages
2269
   arising from the use of this software.
2270
2271
   Permission is granted to anyone to use this software for any purpose,
2272
   including commercial applications, and to alter it and redistribute it
2273
   freely, subject to the following restrictions:
2274
2275
   1. The origin of this source code must not be misrepresented; you must not
2276
      claim that you wrote the original source code. If you use this source code
2277
      in a product, an acknowledgment in the product documentation would be
2278
      appreciated but is not required.
2279
2280
   2. Altered source versions must be plainly marked as such, and must not be
2281
      misrepresented as being the original source code.
2282
2283
   3. This notice may not be removed or altered from any source distribution.
2284
2285
   René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2286
2287
*/
2288
2289
#ifdef __clang__
2290
#pragma clang diagnostic push
2291
#pragma clang diagnostic ignored "-Wsign-conversion"
2292
#pragma clang diagnostic ignored "-Wconversion"
2293
#endif
2294
2295
3.84M
static inline bool is_base64(unsigned char c) {
2296
3.84M
  return (isalnum(c) || (c == '+') || (c == '/'));
2297
3.84M
}
2298
2299
std::string base64_encode(unsigned char const *bytes_to_encode,
2300
0
                          unsigned int in_len) {
2301
0
  std::string ret;
2302
0
  int i = 0;
2303
0
  int j = 0;
2304
0
  unsigned char char_array_3[3];
2305
0
  unsigned char char_array_4[4];
2306
2307
0
  const char *base64_chars =
2308
0
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2309
0
      "abcdefghijklmnopqrstuvwxyz"
2310
0
      "0123456789+/";
2311
2312
0
  while (in_len--) {
2313
0
    char_array_3[i++] = *(bytes_to_encode++);
2314
0
    if (i == 3) {
2315
0
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2316
0
      char_array_4[1] =
2317
0
          ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2318
0
      char_array_4[2] =
2319
0
          ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2320
0
      char_array_4[3] = char_array_3[2] & 0x3f;
2321
2322
0
      for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
2323
0
      i = 0;
2324
0
    }
2325
0
  }
2326
2327
0
  if (i) {
2328
0
    for (j = i; j < 3; j++) char_array_3[j] = '\0';
2329
2330
0
    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2331
0
    char_array_4[1] =
2332
0
        ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2333
0
    char_array_4[2] =
2334
0
        ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2335
2336
0
    for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
2337
2338
0
    while ((i++ < 3)) ret += '=';
2339
0
  }
2340
2341
0
  return ret;
2342
0
}
2343
2344
19.0k
std::string base64_decode(std::string const &encoded_string) {
2345
19.0k
  int in_len = static_cast<int>(encoded_string.size());
2346
19.0k
  int i = 0;
2347
19.0k
  int j = 0;
2348
19.0k
  int in_ = 0;
2349
19.0k
  unsigned char char_array_4[4], char_array_3[3];
2350
19.0k
  std::string ret;
2351
2352
19.0k
  const std::string base64_chars =
2353
19.0k
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2354
19.0k
      "abcdefghijklmnopqrstuvwxyz"
2355
19.0k
      "0123456789+/";
2356
2357
3.85M
  while (in_len-- && (encoded_string[in_] != '=') &&
2358
3.85M
         is_base64(encoded_string[in_])) {
2359
3.83M
    char_array_4[i++] = encoded_string[in_];
2360
3.83M
    in_++;
2361
3.83M
    if (i == 4) {
2362
4.75M
      for (i = 0; i < 4; i++)
2363
3.80M
        char_array_4[i] =
2364
3.80M
            static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
2365
2366
951k
      char_array_3[0] =
2367
951k
          (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2368
951k
      char_array_3[1] =
2369
951k
          ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2370
951k
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2371
2372
3.80M
      for (i = 0; (i < 3); i++) ret += char_array_3[i];
2373
951k
      i = 0;
2374
951k
    }
2375
3.83M
  }
2376
2377
19.0k
  if (i) {
2378
39.0k
    for (j = i; j < 4; j++) char_array_4[j] = 0;
2379
2380
69.5k
    for (j = 0; j < 4; j++)
2381
55.6k
      char_array_4[j] =
2382
55.6k
          static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
2383
2384
13.9k
    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2385
13.9k
    char_array_3[1] =
2386
13.9k
        ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2387
13.9k
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2388
2389
30.4k
    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
2390
13.9k
  }
2391
2392
19.0k
  return ret;
2393
19.0k
}
2394
#ifdef __clang__
2395
#pragma clang diagnostic pop
2396
#endif
2397
2398
// https://github.com/syoyo/tinygltf/issues/228
2399
// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2400
// decoding?
2401
//
2402
// Uri Decoding from DLIB
2403
// http://dlib.net/dlib/server/server_http.cpp.html
2404
// --- dlib begin ------------------------------------------------------------
2405
// Copyright (C) 2003  Davis E. King (davis@dlib.net)
2406
// License: Boost Software License
2407
// Boost Software License - Version 1.0 - August 17th, 2003
2408
2409
// Permission is hereby granted, free of charge, to any person or organization
2410
// obtaining a copy of the software and accompanying documentation covered by
2411
// this license (the "Software") to use, reproduce, display, distribute,
2412
// execute, and transmit the Software, and to prepare derivative works of the
2413
// Software, and to permit third-parties to whom the Software is furnished to
2414
// do so, all subject to the following:
2415
// The copyright notices in the Software and this entire statement, including
2416
// the above license grant, this restriction and the following disclaimer,
2417
// must be included in all copies of the Software, in whole or in part, and
2418
// all derivative works of the Software, unless such copies or derivative
2419
// works are solely in the form of machine-executable object code generated by
2420
// a source language processor.
2421
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2422
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2423
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2424
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2425
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2426
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2427
// DEALINGS IN THE SOFTWARE.
2428
//
2429
namespace dlib {
2430
2431
350k
inline unsigned char from_hex(unsigned char ch) {
2432
350k
  if (ch <= '9' && ch >= '0')
2433
14.6k
    ch -= '0';
2434
335k
  else if (ch <= 'f' && ch >= 'a')
2435
16.2k
    ch -= 'a' - 10;
2436
319k
  else if (ch <= 'F' && ch >= 'A')
2437
28.5k
    ch -= 'A' - 10;
2438
290k
  else
2439
290k
    ch = 0;
2440
350k
  return ch;
2441
350k
}
2442
2443
42.1k
static const std::string urldecode(const std::string &str) {
2444
42.1k
  using namespace std;
2445
42.1k
  string result;
2446
42.1k
  string::size_type i;
2447
6.33M
  for (i = 0; i < str.size(); ++i) {
2448
6.29M
    if (str[i] == '+') {
2449
14.7k
      result += ' ';
2450
6.27M
    } else if (str[i] == '%' && str.size() > i + 2) {
2451
175k
      const unsigned char ch1 =
2452
175k
          from_hex(static_cast<unsigned char>(str[i + 1]));
2453
175k
      const unsigned char ch2 =
2454
175k
          from_hex(static_cast<unsigned char>(str[i + 2]));
2455
175k
      const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2456
175k
      result += static_cast<char>(ch);
2457
175k
      i += 2;
2458
6.10M
    } else {
2459
6.10M
      result += str[i];
2460
6.10M
    }
2461
6.29M
  }
2462
42.1k
  return result;
2463
42.1k
}
2464
2465
}  // namespace dlib
2466
// --- dlib end --------------------------------------------------------------
2467
2468
bool URIDecode(const std::string &in_uri, std::string *out_uri,
2469
42.1k
               void *user_data) {
2470
42.1k
  (void)user_data;
2471
42.1k
  *out_uri = dlib::urldecode(in_uri);
2472
42.1k
  return true;
2473
42.1k
}
2474
2475
static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
2476
                             std::string *warn, const std::string &filename,
2477
                             const std::string &basedir, bool required,
2478
                             size_t reqBytes, bool checkSize,
2479
42.1k
                             size_t maxFileSize, FsCallbacks *fs) {
2480
42.1k
  if (fs == nullptr || fs->FileExists == nullptr ||
2481
42.1k
      fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
2482
    // This is a developer error, assert() ?
2483
0
    if (err) {
2484
0
      (*err) += "FS callback[s] not set\n";
2485
0
    }
2486
0
    return false;
2487
0
  }
2488
2489
42.1k
  std::string *failMsgOut = required ? err : warn;
2490
2491
42.1k
  out->clear();
2492
2493
42.1k
  std::vector<std::string> paths;
2494
42.1k
  paths.push_back(basedir);
2495
42.1k
  paths.push_back(".");
2496
2497
42.1k
  std::string filepath = FindFile(paths, filename, fs);
2498
42.1k
  if (filepath.empty() || filename.empty()) {
2499
42.1k
    if (failMsgOut) {
2500
42.1k
      (*failMsgOut) += "File not found : " + filename + "\n";
2501
42.1k
    }
2502
42.1k
    return false;
2503
42.1k
  }
2504
2505
  // Check file size
2506
0
  if (fs->GetFileSizeInBytes) {
2507
0
    size_t file_size{0};
2508
0
    std::string _err;
2509
0
    bool ok =
2510
0
        fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data);
2511
0
    if (!ok) {
2512
0
      if (_err.size()) {
2513
0
        if (failMsgOut) {
2514
0
          (*failMsgOut) += "Getting file size failed : " + filename +
2515
0
                           ", err = " + _err + "\n";
2516
0
        }
2517
0
      }
2518
0
      return false;
2519
0
    }
2520
2521
0
    if (file_size > maxFileSize) {
2522
0
      if (failMsgOut) {
2523
0
        (*failMsgOut) += "File size " + std::to_string(file_size) +
2524
0
                         " exceeds maximum allowed file size " +
2525
0
                         std::to_string(maxFileSize) + " : " + filepath + "\n";
2526
0
      }
2527
0
      return false;
2528
0
    }
2529
0
  }
2530
2531
0
  std::vector<unsigned char> buf;
2532
0
  std::string fileReadErr;
2533
0
  bool fileRead =
2534
0
      fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
2535
0
  if (!fileRead) {
2536
0
    if (failMsgOut) {
2537
0
      (*failMsgOut) +=
2538
0
          "File read error : " + filepath + " : " + fileReadErr + "\n";
2539
0
    }
2540
0
    return false;
2541
0
  }
2542
2543
0
  size_t sz = buf.size();
2544
0
  if (sz == 0) {
2545
0
    if (failMsgOut) {
2546
0
      (*failMsgOut) += "File is empty : " + filepath + "\n";
2547
0
    }
2548
0
    return false;
2549
0
  }
2550
2551
0
  if (checkSize) {
2552
0
    if (reqBytes == sz) {
2553
0
      out->swap(buf);
2554
0
      return true;
2555
0
    } else {
2556
0
      std::stringstream ss;
2557
0
      ss << "File size mismatch : " << filepath << ", requestedBytes "
2558
0
         << reqBytes << ", but got " << sz << std::endl;
2559
0
      if (failMsgOut) {
2560
0
        (*failMsgOut) += ss.str();
2561
0
      }
2562
0
      return false;
2563
0
    }
2564
0
  }
2565
2566
0
  out->swap(buf);
2567
0
  return true;
2568
0
}
2569
2570
0
void TinyGLTF::SetParseStrictness(ParseStrictness strictness) {
2571
0
  strictness_ = strictness;
2572
0
}
2573
2574
0
void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
2575
0
  LoadImageData = func;
2576
0
  load_image_user_data_ = user_data;
2577
0
  user_image_loader_ = true;
2578
0
}
2579
2580
0
void TinyGLTF::RemoveImageLoader() {
2581
0
  LoadImageData =
2582
0
#ifndef TINYGLTF_NO_STB_IMAGE
2583
0
      &tinygltf::LoadImageData;
2584
#else
2585
      nullptr;
2586
#endif
2587
2588
0
  load_image_user_data_ = nullptr;
2589
0
  user_image_loader_ = false;
2590
0
}
2591
2592
#ifndef TINYGLTF_NO_STB_IMAGE
2593
bool LoadImageData(Image *image, const int image_idx, std::string *err,
2594
                   std::string *warn, int req_width, int req_height,
2595
19.0k
                   const unsigned char *bytes, int size, void *user_data) {
2596
19.0k
  (void)warn;
2597
2598
19.0k
  LoadImageDataOption option;
2599
19.0k
  if (user_data) {
2600
19.0k
    option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2601
19.0k
  }
2602
2603
19.0k
  int w = 0, h = 0, comp = 0, req_comp = 0;
2604
2605
19.0k
  unsigned char *data = nullptr;
2606
2607
  // preserve_channels true: Use channels stored in the image file.
2608
  // false: force 32-bit textures for common Vulkan compatibility. It appears
2609
  // that some GPU drivers do not support 24-bit images for Vulkan
2610
19.0k
  req_comp = option.preserve_channels ? 0 : 4;
2611
19.0k
  int bits = 8;
2612
19.0k
  int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
2613
2614
  // It is possible that the image we want to load is a 16bit per channel image
2615
  // We are going to attempt to load it as 16bit per channel, and if it worked,
2616
  // set the image data accordingly. We are casting the returned pointer into
2617
  // unsigned char, because we are representing "bytes". But we are updating
2618
  // the Image metadata to signal that this image uses 2 bytes (16bits) per
2619
  // channel:
2620
19.0k
  if (stbi_is_16_bit_from_memory(bytes, size)) {
2621
1.40k
    data = reinterpret_cast<unsigned char *>(
2622
1.40k
        stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
2623
1.40k
    if (data) {
2624
1.37k
      bits = 16;
2625
1.37k
      pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2626
1.37k
    }
2627
1.40k
  }
2628
2629
  // at this point, if data is still NULL, it means that the image wasn't
2630
  // 16bit per channel, we are going to load it as a normal 8bit per channel
2631
  // image as we used to do:
2632
  // if image cannot be decoded, ignore parsing and keep it by its path
2633
  // don't break in this case
2634
  // FIXME we should only enter this function if the image is embedded. If
2635
  // image->uri references
2636
  // an image file, it should be left as it is. Image loading should not be
2637
  // mandatory (to support other formats)
2638
19.0k
  if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
2639
19.0k
  if (!data) {
2640
    // NOTE: you can use `warn` instead of `err`
2641
319
    if (err) {
2642
319
      (*err) +=
2643
319
          "Unknown image format. STB cannot decode image data for image[" +
2644
319
          std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
2645
319
    }
2646
319
    return false;
2647
319
  }
2648
2649
18.6k
  if ((w < 1) || (h < 1)) {
2650
0
    stbi_image_free(data);
2651
0
    if (err) {
2652
0
      (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2653
0
                "] name = \"" + image->name + "\"\n";
2654
0
    }
2655
0
    return false;
2656
0
  }
2657
2658
18.6k
  if (req_width > 0) {
2659
0
    if (req_width != w) {
2660
0
      stbi_image_free(data);
2661
0
      if (err) {
2662
0
        (*err) += "Image width mismatch for image[" +
2663
0
                  std::to_string(image_idx) + "] name = \"" + image->name +
2664
0
                  "\"\n";
2665
0
      }
2666
0
      return false;
2667
0
    }
2668
0
  }
2669
2670
18.6k
  if (req_height > 0) {
2671
0
    if (req_height != h) {
2672
0
      stbi_image_free(data);
2673
0
      if (err) {
2674
0
        (*err) += "Image height mismatch. for image[" +
2675
0
                  std::to_string(image_idx) + "] name = \"" + image->name +
2676
0
                  "\"\n";
2677
0
      }
2678
0
      return false;
2679
0
    }
2680
0
  }
2681
2682
18.6k
  if (req_comp != 0) {
2683
    // loaded data has `req_comp` channels(components)
2684
18.6k
    comp = req_comp;
2685
18.6k
  }
2686
2687
18.6k
  image->width = w;
2688
18.6k
  image->height = h;
2689
18.6k
  image->component = comp;
2690
18.6k
  image->bits = bits;
2691
18.6k
  image->pixel_type = pixel_type;
2692
18.6k
  image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2693
18.6k
  std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
2694
18.6k
  stbi_image_free(data);
2695
2696
18.6k
  return true;
2697
18.6k
}
2698
#endif
2699
2700
0
void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2701
0
  WriteImageData = func;
2702
0
  write_image_user_data_ = user_data;
2703
0
}
2704
2705
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2706
0
static void WriteToMemory_stbi(void *context, void *data, int size) {
2707
0
  std::vector<unsigned char> *buffer =
2708
0
      reinterpret_cast<std::vector<unsigned char> *>(context);
2709
2710
0
  unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2711
2712
0
  buffer->insert(buffer->end(), pData, pData + size);
2713
0
}
2714
2715
bool WriteImageData(const std::string *basepath, const std::string *filename,
2716
                    const Image *image, bool embedImages,
2717
                    const URICallbacks *uri_cb, std::string *out_uri,
2718
0
                    void *fsPtr) {
2719
0
  const std::string ext = GetFilePathExtension(*filename);
2720
2721
  // Write image to temporary buffer
2722
0
  std::string header;
2723
0
  std::vector<unsigned char> data;
2724
2725
0
  if (ext == "png") {
2726
0
    if ((image->bits != 8) ||
2727
0
        (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
2728
      // Unsupported pixel format
2729
0
      return false;
2730
0
    }
2731
2732
0
    if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
2733
0
                                image->height, image->component,
2734
0
                                &image->image[0], 0)) {
2735
0
      return false;
2736
0
    }
2737
0
    header = "data:image/png;base64,";
2738
0
  } else if (ext == "jpg") {
2739
0
    if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
2740
0
                                image->height, image->component,
2741
0
                                &image->image[0], 100)) {
2742
0
      return false;
2743
0
    }
2744
0
    header = "data:image/jpeg;base64,";
2745
0
  } else if (ext == "bmp") {
2746
0
    if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
2747
0
                                image->height, image->component,
2748
0
                                &image->image[0])) {
2749
0
      return false;
2750
0
    }
2751
0
    header = "data:image/bmp;base64,";
2752
0
  } else if (!embedImages) {
2753
    // Error: can't output requested format to file
2754
0
    return false;
2755
0
  }
2756
2757
0
  if (embedImages) {
2758
    // Embed base64-encoded image into URI
2759
0
    if (data.size()) {
2760
0
      *out_uri = header + base64_encode(&data[0],
2761
0
                                        static_cast<unsigned int>(data.size()));
2762
0
    } else {
2763
      // Throw error?
2764
0
    }
2765
0
  } else {
2766
    // Write image to disc
2767
0
    FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
2768
0
    if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
2769
0
      const std::string imagefilepath = JoinPath(*basepath, *filename);
2770
0
      std::string writeError;
2771
0
      if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2772
0
                              fs->user_data)) {
2773
        // Could not write image file to disc; Throw error ?
2774
0
        return false;
2775
0
      }
2776
0
    } else {
2777
      // Throw error?
2778
0
    }
2779
0
    if (uri_cb->encode) {
2780
0
      if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
2781
0
        return false;
2782
0
      }
2783
0
    } else {
2784
0
      *out_uri = *filename;
2785
0
    }
2786
0
  }
2787
2788
0
  return true;
2789
0
}
2790
#endif
2791
2792
0
void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
2793
0
  assert(callbacks.decode);
2794
0
  if (callbacks.decode) {
2795
0
    uri_cb = callbacks;
2796
0
  }
2797
0
}
2798
2799
0
void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
2800
2801
#ifdef _WIN32
2802
static inline std::wstring UTF8ToWchar(const std::string &str) {
2803
  int wstr_size =
2804
      MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
2805
  std::wstring wstr((size_t)wstr_size, 0);
2806
  MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2807
                      (int)wstr.size());
2808
  return wstr;
2809
}
2810
2811
static inline std::string WcharToUTF8(const std::wstring &wstr) {
2812
  int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
2813
                                     nullptr, 0, nullptr, nullptr);
2814
  std::string str((size_t)str_size, 0);
2815
  WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
2816
                      (int)str.size(), nullptr, nullptr);
2817
  return str;
2818
}
2819
#endif
2820
2821
#ifndef TINYGLTF_NO_FS
2822
// Default implementations of filesystem functions
2823
2824
28.1k
bool FileExists(const std::string &abs_filename, void *) {
2825
28.1k
  bool ret;
2826
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2827
  if (asset_manager) {
2828
    AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2829
                                       AASSET_MODE_STREAMING);
2830
    if (!asset) {
2831
      return false;
2832
    }
2833
    AAsset_close(asset);
2834
    ret = true;
2835
  } else {
2836
    return false;
2837
  }
2838
#else
2839
#ifdef _WIN32
2840
#if defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2841
2842
  // First check if a file is a directory.
2843
  DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str());
2844
  if (result == INVALID_FILE_ATTRIBUTES) {
2845
    return false;
2846
  }
2847
  if (result & FILE_ATTRIBUTE_DIRECTORY) {
2848
    return false;
2849
  }
2850
2851
  FILE *fp = nullptr;
2852
  errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
2853
  if (err != 0) {
2854
    return false;
2855
  }
2856
#elif defined(__GLIBCXX__)
2857
  FILE *fp = fopen(abs_filename.c_str(), "rb");
2858
  if (!fp) {
2859
    return false;
2860
  }
2861
#else
2862
  // TODO: is_directory check
2863
  FILE *fp = nullptr;
2864
  errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2865
  if (err != 0) {
2866
    return false;
2867
  }
2868
#endif
2869
2870
#else
2871
28.1k
  struct stat sb;
2872
28.1k
  if (stat(abs_filename.c_str(), &sb)) {
2873
27.7k
    return false;
2874
27.7k
  }
2875
382
  if (S_ISDIR(sb.st_mode)) {
2876
382
    return false;
2877
382
  }
2878
2879
0
  FILE *fp = fopen(abs_filename.c_str(), "rb");
2880
0
#endif
2881
0
  if (fp) {
2882
0
    ret = true;
2883
0
    fclose(fp);
2884
0
  } else {
2885
0
    ret = false;
2886
0
  }
2887
0
#endif
2888
2889
0
  return ret;
2890
382
}
2891
2892
28.1k
std::string ExpandFilePath(const std::string &filepath, void *) {
2893
  // https://github.com/syoyo/tinygltf/issues/368
2894
  //
2895
  // No file path expansion in built-in FS function anymore, since glTF URI
2896
  // should not contain tilde('~') and environment variables, and for security
2897
  // reason(`wordexp`).
2898
  //
2899
  // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2900
  // `LoadBinaryFromMemory`) in expanded absolute path.
2901
2902
28.1k
  return filepath;
2903
2904
#if 0
2905
#ifdef _WIN32
2906
  // Assume input `filepath` is encoded in UTF-8
2907
  std::wstring wfilepath = UTF8ToWchar(filepath);
2908
  DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
2909
  wchar_t *wstr = new wchar_t[wlen];
2910
  ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
2911
2912
  std::wstring ws(wstr);
2913
  delete[] wstr;
2914
  return WcharToUTF8(ws);
2915
2916
#else
2917
2918
#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
2919
    defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
2920
  // no expansion
2921
  std::string s = filepath;
2922
#else
2923
  std::string s;
2924
  wordexp_t p;
2925
2926
  if (filepath.empty()) {
2927
    return "";
2928
  }
2929
2930
  // Quote the string to keep any spaces in filepath intact.
2931
  std::string quoted_path = "\"" + filepath + "\"";
2932
  // char** w;
2933
  int ret = wordexp(quoted_path.c_str(), &p, 0);
2934
  if (ret) {
2935
    // err
2936
    s = filepath;
2937
    return s;
2938
  }
2939
2940
  // Use first element only.
2941
  if (p.we_wordv) {
2942
    s = std::string(p.we_wordv[0]);
2943
    wordfree(&p);
2944
  } else {
2945
    s = filepath;
2946
  }
2947
2948
#endif
2949
2950
  return s;
2951
#endif
2952
#endif
2953
28.1k
}
2954
2955
bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
2956
0
                        const std::string &filepath, void *userdata) {
2957
0
  (void)userdata;
2958
2959
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2960
  if (asset_manager) {
2961
    AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2962
                                       AASSET_MODE_STREAMING);
2963
    if (!asset) {
2964
      if (err) {
2965
        (*err) += "File open error : " + filepath + "\n";
2966
      }
2967
      return false;
2968
    }
2969
    size_t size = AAsset_getLength(asset);
2970
2971
    if (size == 0) {
2972
      if (err) {
2973
        (*err) += "Invalid file size : " + filepath +
2974
                  " (does the path point to a directory?)";
2975
      }
2976
      return false;
2977
    }
2978
2979
    return true;
2980
  } else {
2981
    if (err) {
2982
      (*err) += "No asset manager specified : " + filepath + "\n";
2983
    }
2984
    return false;
2985
  }
2986
#else
2987
#ifdef _WIN32
2988
#if defined(__GLIBCXX__)  // mingw
2989
  int file_descriptor =
2990
      _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2991
  __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2992
  std::istream f(&wfile_buf);
2993
#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2994
  // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2995
  // `wchar_t *`
2996
  std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
2997
#else
2998
  // Unknown compiler/runtime
2999
  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3000
#endif
3001
#else
3002
0
  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3003
0
#endif
3004
0
  if (!f) {
3005
0
    if (err) {
3006
0
      (*err) += "File open error : " + filepath + "\n";
3007
0
    }
3008
0
    return false;
3009
0
  }
3010
3011
  // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
3012
0
  f.peek();
3013
0
  if (!f) {
3014
0
    if (err) {
3015
0
      (*err) +=
3016
0
          "File read error. Maybe empty file or invalid file : " + filepath +
3017
0
          "\n";
3018
0
    }
3019
0
    return false;
3020
0
  }
3021
3022
0
  f.seekg(0, f.end);
3023
0
  size_t sz = static_cast<size_t>(f.tellg());
3024
3025
  // std::cout << "sz = " << sz << "\n";
3026
0
  f.seekg(0, f.beg);
3027
3028
0
  if (int64_t(sz) < 0) {
3029
0
    if (err) {
3030
0
      (*err) += "Invalid file size : " + filepath +
3031
0
                " (does the path point to a directory?)";
3032
0
    }
3033
0
    return false;
3034
0
  } else if (sz == 0) {
3035
0
    if (err) {
3036
0
      (*err) += "File is empty : " + filepath + "\n";
3037
0
    }
3038
0
    return false;
3039
0
  } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3040
0
    if (err) {
3041
0
      (*err) += "Invalid file size : " + filepath + "\n";
3042
0
    }
3043
0
    return false;
3044
0
  }
3045
3046
0
  (*filesize_out) = sz;
3047
0
  return true;
3048
0
#endif
3049
0
}
3050
3051
bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
3052
0
                   const std::string &filepath, void *) {
3053
#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
3054
  if (asset_manager) {
3055
    AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
3056
                                       AASSET_MODE_STREAMING);
3057
    if (!asset) {
3058
      if (err) {
3059
        (*err) += "File open error : " + filepath + "\n";
3060
      }
3061
      return false;
3062
    }
3063
    size_t size = AAsset_getLength(asset);
3064
    if (size == 0) {
3065
      if (err) {
3066
        (*err) += "Invalid file size : " + filepath +
3067
                  " (does the path point to a directory?)";
3068
      }
3069
      return false;
3070
    }
3071
    out->resize(size);
3072
    AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
3073
    AAsset_close(asset);
3074
    return true;
3075
  } else {
3076
    if (err) {
3077
      (*err) += "No asset manager specified : " + filepath + "\n";
3078
    }
3079
    return false;
3080
  }
3081
#else
3082
#ifdef _WIN32
3083
#if defined(__GLIBCXX__)  // mingw
3084
  int file_descriptor =
3085
      _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
3086
  __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
3087
  std::istream f(&wfile_buf);
3088
#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
3089
  // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
3090
  // `wchar_t *`
3091
  std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
3092
#else
3093
  // Unknown compiler/runtime
3094
  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3095
#endif
3096
#else
3097
0
  std::ifstream f(filepath.c_str(), std::ifstream::binary);
3098
0
#endif
3099
0
  if (!f) {
3100
0
    if (err) {
3101
0
      (*err) += "File open error : " + filepath + "\n";
3102
0
    }
3103
0
    return false;
3104
0
  }
3105
3106
  // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
3107
0
  f.peek();
3108
0
  if (!f) {
3109
0
    if (err) {
3110
0
      (*err) +=
3111
0
          "File read error. Maybe empty file or invalid file : " + filepath +
3112
0
          "\n";
3113
0
    }
3114
0
    return false;
3115
0
  }
3116
3117
0
  f.seekg(0, f.end);
3118
0
  size_t sz = static_cast<size_t>(f.tellg());
3119
3120
  // std::cout << "sz = " << sz << "\n";
3121
0
  f.seekg(0, f.beg);
3122
3123
0
  if (int64_t(sz) < 0) {
3124
0
    if (err) {
3125
0
      (*err) += "Invalid file size : " + filepath +
3126
0
                " (does the path point to a directory?)";
3127
0
    }
3128
0
    return false;
3129
0
  } else if (sz == 0) {
3130
0
    if (err) {
3131
0
      (*err) += "File is empty : " + filepath + "\n";
3132
0
    }
3133
0
    return false;
3134
0
  } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3135
0
    if (err) {
3136
0
      (*err) += "Invalid file size : " + filepath + "\n";
3137
0
    }
3138
0
    return false;
3139
0
  }
3140
3141
0
  out->resize(sz);
3142
0
  f.read(reinterpret_cast<char *>(&out->at(0)),
3143
0
         static_cast<std::streamsize>(sz));
3144
3145
0
  return true;
3146
0
#endif
3147
0
}
3148
3149
bool WriteWholeFile(std::string *err, const std::string &filepath,
3150
0
                    const std::vector<unsigned char> &contents, void *) {
3151
#ifdef _WIN32
3152
#if defined(__GLIBCXX__)  // mingw
3153
  int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
3154
                               _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
3155
  __gnu_cxx::stdio_filebuf<char> wfile_buf(
3156
      file_descriptor, std::ios_base::out | std::ios_base::binary);
3157
  std::ostream f(&wfile_buf);
3158
#elif defined(_MSC_VER)
3159
  std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
3160
#else  // clang?
3161
  std::ofstream f(filepath.c_str(), std::ofstream::binary);
3162
#endif
3163
#else
3164
0
  std::ofstream f(filepath.c_str(), std::ofstream::binary);
3165
0
#endif
3166
0
  if (!f) {
3167
0
    if (err) {
3168
0
      (*err) += "File open error for writing : " + filepath + "\n";
3169
0
    }
3170
0
    return false;
3171
0
  }
3172
3173
0
  f.write(reinterpret_cast<const char *>(&contents.at(0)),
3174
0
          static_cast<std::streamsize>(contents.size()));
3175
0
  if (!f) {
3176
0
    if (err) {
3177
0
      (*err) += "File write error: " + filepath + "\n";
3178
0
    }
3179
0
    return false;
3180
0
  }
3181
3182
0
  return true;
3183
0
}
3184
3185
#endif  // TINYGLTF_NO_FS
3186
3187
0
static std::string MimeToExt(const std::string &mimeType) {
3188
0
  if (mimeType == "image/jpeg") {
3189
0
    return "jpg";
3190
0
  } else if (mimeType == "image/png") {
3191
0
    return "png";
3192
0
  } else if (mimeType == "image/bmp") {
3193
0
    return "bmp";
3194
0
  } else if (mimeType == "image/gif") {
3195
0
    return "gif";
3196
0
  }
3197
3198
0
  return "";
3199
0
}
3200
3201
static bool UpdateImageObject(const Image &image, std::string &baseDir,
3202
                              int index, bool embedImages,
3203
                              const URICallbacks *uri_cb,
3204
                              WriteImageDataFunction *WriteImageData,
3205
0
                              void *user_data, std::string *out_uri) {
3206
0
  std::string filename;
3207
0
  std::string ext;
3208
  // If image has uri, use it as a filename
3209
0
  if (image.uri.size()) {
3210
0
    std::string decoded_uri;
3211
0
    if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
3212
      // A decode failure results in a failure to write the gltf.
3213
0
      return false;
3214
0
    }
3215
0
    filename = GetBaseFilename(decoded_uri);
3216
0
    ext = GetFilePathExtension(filename);
3217
0
  } else if (image.bufferView != -1) {
3218
    // If there's no URI and the data exists in a buffer,
3219
    // don't change properties or write images
3220
0
  } else if (image.name.size()) {
3221
0
    ext = MimeToExt(image.mimeType);
3222
    // Otherwise use name as filename
3223
0
    filename = image.name + "." + ext;
3224
0
  } else {
3225
0
    ext = MimeToExt(image.mimeType);
3226
    // Fallback to index of image as filename
3227
0
    filename = std::to_string(index) + "." + ext;
3228
0
  }
3229
3230
  // If callback is set and image data exists, modify image data object. If
3231
  // image data does not exist, this is not considered a failure and the
3232
  // original uri should be maintained.
3233
0
  bool imageWritten = false;
3234
0
  if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
3235
0
    imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
3236
0
                                     uri_cb, out_uri, user_data);
3237
0
    if (!imageWritten) {
3238
0
      return false;
3239
0
    }
3240
0
  }
3241
3242
  // Use the original uri if the image was not written.
3243
0
  if (!imageWritten) {
3244
0
    *out_uri = image.uri;
3245
0
  }
3246
3247
0
  return true;
3248
0
}
3249
3250
61.1k
bool IsDataURI(const std::string &in) {
3251
61.1k
  std::string header = "data:application/octet-stream;base64,";
3252
61.1k
  if (in.find(header) == 0) {
3253
506
    return true;
3254
506
  }
3255
3256
60.6k
  header = "data:image/jpeg;base64,";
3257
60.6k
  if (in.find(header) == 0) {
3258
11.0k
    return true;
3259
11.0k
  }
3260
3261
49.6k
  header = "data:image/png;base64,";
3262
49.6k
  if (in.find(header) == 0) {
3263
2.09k
    return true;
3264
2.09k
  }
3265
3266
47.5k
  header = "data:image/bmp;base64,";
3267
47.5k
  if (in.find(header) == 0) {
3268
2.32k
    return true;
3269
2.32k
  }
3270
3271
45.2k
  header = "data:image/gif;base64,";
3272
45.2k
  if (in.find(header) == 0) {
3273
498
    return true;
3274
498
  }
3275
3276
44.7k
  header = "data:text/plain;base64,";
3277
44.7k
  if (in.find(header) == 0) {
3278
1.78k
    return true;
3279
1.78k
  }
3280
3281
42.9k
  header = "data:application/gltf-buffer;base64,";
3282
42.9k
  if (in.find(header) == 0) {
3283
790
    return true;
3284
790
  }
3285
3286
42.1k
  return false;
3287
42.9k
}
3288
3289
bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3290
19.0k
                   const std::string &in, size_t reqBytes, bool checkSize) {
3291
19.0k
  std::string header = "data:application/octet-stream;base64,";
3292
19.0k
  std::string data;
3293
19.0k
  if (in.find(header) == 0) {
3294
506
    data = base64_decode(in.substr(header.size()));  // cut mime string.
3295
506
  }
3296
3297
19.0k
  if (data.empty()) {
3298
18.5k
    header = "data:image/jpeg;base64,";
3299
18.5k
    if (in.find(header) == 0) {
3300
11.0k
      mime_type = "image/jpeg";
3301
11.0k
      data = base64_decode(in.substr(header.size()));  // cut mime string.
3302
11.0k
    }
3303
18.5k
  }
3304
3305
19.0k
  if (data.empty()) {
3306
7.49k
    header = "data:image/png;base64,";
3307
7.49k
    if (in.find(header) == 0) {
3308
2.09k
      mime_type = "image/png";
3309
2.09k
      data = base64_decode(in.substr(header.size()));  // cut mime string.
3310
2.09k
    }
3311
7.49k
  }
3312
3313
19.0k
  if (data.empty()) {
3314
5.41k
    header = "data:image/bmp;base64,";
3315
5.41k
    if (in.find(header) == 0) {
3316
2.32k
      mime_type = "image/bmp";
3317
2.32k
      data = base64_decode(in.substr(header.size()));  // cut mime string.
3318
2.32k
    }
3319
5.41k
  }
3320
3321
19.0k
  if (data.empty()) {
3322
3.09k
    header = "data:image/gif;base64,";
3323
3.09k
    if (in.find(header) == 0) {
3324
498
      mime_type = "image/gif";
3325
498
      data = base64_decode(in.substr(header.size()));  // cut mime string.
3326
498
    }
3327
3.09k
  }
3328
3329
19.0k
  if (data.empty()) {
3330
2.61k
    header = "data:text/plain;base64,";
3331
2.61k
    if (in.find(header) == 0) {
3332
1.78k
      mime_type = "text/plain";
3333
1.78k
      data = base64_decode(in.substr(header.size()));
3334
1.78k
    }
3335
2.61k
  }
3336
3337
19.0k
  if (data.empty()) {
3338
832
    header = "data:application/gltf-buffer;base64,";
3339
832
    if (in.find(header) == 0) {
3340
790
      data = base64_decode(in.substr(header.size()));
3341
790
    }
3342
832
  }
3343
3344
  // TODO(syoyo): Allow empty buffer? #229
3345
19.0k
  if (data.empty()) {
3346
47
    return false;
3347
47
  }
3348
3349
19.0k
  if (checkSize) {
3350
0
    if (data.size() != reqBytes) {
3351
0
      return false;
3352
0
    }
3353
0
    out->resize(reqBytes);
3354
19.0k
  } else {
3355
19.0k
    out->resize(data.size());
3356
19.0k
  }
3357
19.0k
  std::copy(data.begin(), data.end(), out->begin());
3358
19.0k
  return true;
3359
19.0k
}
3360
3361
namespace detail {
3362
589k
bool GetInt(const detail::json &o, int &val) {
3363
#ifdef TINYGLTF_USE_RAPIDJSON
3364
  if (!o.IsDouble()) {
3365
    if (o.IsInt()) {
3366
      val = o.GetInt();
3367
      return true;
3368
    } else if (o.IsUint()) {
3369
      val = static_cast<int>(o.GetUint());
3370
      return true;
3371
    } else if (o.IsInt64()) {
3372
      val = static_cast<int>(o.GetInt64());
3373
      return true;
3374
    } else if (o.IsUint64()) {
3375
      val = static_cast<int>(o.GetUint64());
3376
      return true;
3377
    }
3378
  }
3379
3380
  return false;
3381
#else
3382
589k
  auto type = o.type();
3383
3384
589k
  if ((type == detail::json::value_t::number_integer) ||
3385
589k
      (type == detail::json::value_t::number_unsigned)) {
3386
587k
    val = static_cast<int>(o.get<int64_t>());
3387
587k
    return true;
3388
587k
  }
3389
3390
2.44k
  return false;
3391
589k
#endif
3392
589k
}
3393
3394
#ifdef TINYGLTF_USE_RAPIDJSON
3395
bool GetDouble(const detail::json &o, double &val) {
3396
  if (o.IsDouble()) {
3397
    val = o.GetDouble();
3398
    return true;
3399
  }
3400
3401
  return false;
3402
}
3403
#endif
3404
3405
747k
bool GetNumber(const detail::json &o, double &val) {
3406
#ifdef TINYGLTF_USE_RAPIDJSON
3407
  if (o.IsNumber()) {
3408
    val = o.GetDouble();
3409
    return true;
3410
  }
3411
3412
  return false;
3413
#else
3414
747k
  if (o.is_number()) {
3415
739k
    val = o.get<double>();
3416
739k
    return true;
3417
739k
  }
3418
3419
7.85k
  return false;
3420
747k
#endif
3421
747k
}
3422
3423
365k
bool GetString(const detail::json &o, std::string &val) {
3424
#ifdef TINYGLTF_USE_RAPIDJSON
3425
  if (o.IsString()) {
3426
    val = o.GetString();
3427
    return true;
3428
  }
3429
3430
  return false;
3431
#else
3432
365k
  if (o.type() == detail::json::value_t::string) {
3433
70.1k
    val = o.get<std::string>();
3434
70.1k
    return true;
3435
70.1k
  }
3436
3437
295k
  return false;
3438
365k
#endif
3439
365k
}
3440
3441
29.2k
bool IsArray(const detail::json &o) {
3442
#ifdef TINYGLTF_USE_RAPIDJSON
3443
  return o.IsArray();
3444
#else
3445
29.2k
  return o.is_array();
3446
29.2k
#endif
3447
29.2k
}
3448
3449
14.9k
detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
3450
#ifdef TINYGLTF_USE_RAPIDJSON
3451
  return o.Begin();
3452
#else
3453
14.9k
  return o.begin();
3454
14.9k
#endif
3455
14.9k
}
3456
3457
14.9k
detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
3458
#ifdef TINYGLTF_USE_RAPIDJSON
3459
  return o.End();
3460
#else
3461
14.9k
  return o.end();
3462
14.9k
#endif
3463
14.9k
}
3464
3465
1.30M
bool IsObject(const detail::json &o) {
3466
#ifdef TINYGLTF_USE_RAPIDJSON
3467
  return o.IsObject();
3468
#else
3469
1.30M
  return o.is_object();
3470
1.30M
#endif
3471
1.30M
}
3472
3473
363k
detail::json_const_iterator ObjectBegin(const detail::json &o) {
3474
#ifdef TINYGLTF_USE_RAPIDJSON
3475
  return o.MemberBegin();
3476
#else
3477
363k
  return o.begin();
3478
363k
#endif
3479
363k
}
3480
3481
363k
detail::json_const_iterator ObjectEnd(const detail::json &o) {
3482
#ifdef TINYGLTF_USE_RAPIDJSON
3483
  return o.MemberEnd();
3484
#else
3485
363k
  return o.end();
3486
363k
#endif
3487
363k
}
3488
3489
// Making this a const char* results in a pointer to a temporary when
3490
// TINYGLTF_USE_RAPIDJSON is off.
3491
89.7k
std::string GetKey(detail::json_const_iterator &it) {
3492
#ifdef TINYGLTF_USE_RAPIDJSON
3493
  return it->name.GetString();
3494
#else
3495
89.7k
  return it.key().c_str();
3496
89.7k
#endif
3497
89.7k
}
3498
3499
bool FindMember(const detail::json &o, const char *member,
3500
14.1M
                detail::json_const_iterator &it) {
3501
#ifdef TINYGLTF_USE_RAPIDJSON
3502
  if (!o.IsObject()) {
3503
    return false;
3504
  }
3505
  it = o.FindMember(member);
3506
  return it != o.MemberEnd();
3507
#else
3508
14.1M
  it = o.find(member);
3509
14.1M
  return it != o.end();
3510
14.1M
#endif
3511
14.1M
}
3512
3513
bool FindMember(detail::json &o, const char *member,
3514
0
                detail::json_iterator &it) {
3515
#ifdef TINYGLTF_USE_RAPIDJSON
3516
  if (!o.IsObject()) {
3517
    return false;
3518
  }
3519
  it = o.FindMember(member);
3520
  return it != o.MemberEnd();
3521
#else
3522
0
  it = o.find(member);
3523
0
  return it != o.end();
3524
0
#endif
3525
0
}
3526
3527
0
void Erase(detail::json &o, detail::json_iterator &it) {
3528
#ifdef TINYGLTF_USE_RAPIDJSON
3529
  o.EraseMember(it);
3530
#else
3531
0
  o.erase(it);
3532
0
#endif
3533
0
}
3534
3535
0
bool IsEmpty(const detail::json &o) {
3536
#ifdef TINYGLTF_USE_RAPIDJSON
3537
  return o.ObjectEmpty();
3538
#else
3539
0
  return o.empty();
3540
0
#endif
3541
0
}
3542
3543
255k
const detail::json &GetValue(detail::json_const_iterator &it) {
3544
#ifdef TINYGLTF_USE_RAPIDJSON
3545
  return it->value;
3546
#else
3547
255k
  return it.value();
3548
255k
#endif
3549
255k
}
3550
3551
0
detail::json &GetValue(detail::json_iterator &it) {
3552
#ifdef TINYGLTF_USE_RAPIDJSON
3553
  return it->value;
3554
#else
3555
0
  return it.value();
3556
0
#endif
3557
0
}
3558
3559
0
std::string JsonToString(const detail::json &o, int spacing = -1) {
3560
#ifdef TINYGLTF_USE_RAPIDJSON
3561
  using namespace rapidjson;
3562
  StringBuffer buffer;
3563
  if (spacing == -1) {
3564
    Writer<StringBuffer> writer(buffer);
3565
    // TODO: Better error handling.
3566
    // https://github.com/syoyo/tinygltf/issues/332
3567
    if (!o.Accept(writer)) {
3568
      return "tiny_gltf::JsonToString() failed rapidjson conversion";
3569
    }
3570
  } else {
3571
    PrettyWriter<StringBuffer> writer(buffer);
3572
    writer.SetIndent(' ', uint32_t(spacing));
3573
    if (!o.Accept(writer)) {
3574
      return "tiny_gltf::JsonToString() failed rapidjson conversion";
3575
    }
3576
  }
3577
  return buffer.GetString();
3578
#else
3579
0
  return o.dump(spacing);
3580
0
#endif
3581
0
}
3582
3583
}  // namespace detail
3584
3585
842k
static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
3586
842k
  Value val{};
3587
#ifdef TINYGLTF_USE_RAPIDJSON
3588
  using rapidjson::Type;
3589
  switch (o.GetType()) {
3590
    case Type::kObjectType: {
3591
      Value::Object value_object;
3592
      for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
3593
        Value entry;
3594
        ParseJsonAsValue(&entry, it->value);
3595
        if (entry.Type() != NULL_TYPE)
3596
          value_object.emplace(detail::GetKey(it), std::move(entry));
3597
      }
3598
      if (value_object.size() > 0) val = Value(std::move(value_object));
3599
    } break;
3600
    case Type::kArrayType: {
3601
      Value::Array value_array;
3602
      value_array.reserve(o.Size());
3603
      for (auto it = o.Begin(); it != o.End(); ++it) {
3604
        Value entry;
3605
        ParseJsonAsValue(&entry, *it);
3606
        if (entry.Type() != NULL_TYPE)
3607
          value_array.emplace_back(std::move(entry));
3608
      }
3609
      if (value_array.size() > 0) val = Value(std::move(value_array));
3610
    } break;
3611
    case Type::kStringType:
3612
      val = Value(std::string(o.GetString()));
3613
      break;
3614
    case Type::kFalseType:
3615
    case Type::kTrueType:
3616
      val = Value(o.GetBool());
3617
      break;
3618
    case Type::kNumberType:
3619
      if (!o.IsDouble()) {
3620
        int i = 0;
3621
        detail::GetInt(o, i);
3622
        val = Value(i);
3623
      } else {
3624
        double d = 0.0;
3625
        detail::GetDouble(o, d);
3626
        val = Value(d);
3627
      }
3628
      break;
3629
    case Type::kNullType:
3630
      break;
3631
      // all types are covered, so no `case default`
3632
  }
3633
#else
3634
842k
  switch (o.type()) {
3635
22.4k
    case detail::json::value_t::object: {
3636
22.4k
      Value::Object value_object;
3637
40.7k
      for (auto it = o.begin(); it != o.end(); it++) {
3638
18.2k
        Value entry;
3639
18.2k
        ParseJsonAsValue(&entry, it.value());
3640
18.2k
        if (entry.Type() != NULL_TYPE)
3641
13.2k
          value_object.emplace(it.key(), std::move(entry));
3642
18.2k
      }
3643
22.4k
      if (value_object.size() > 0) val = Value(std::move(value_object));
3644
22.4k
    } break;
3645
36.8k
    case detail::json::value_t::array: {
3646
36.8k
      Value::Array value_array;
3647
36.8k
      value_array.reserve(o.size());
3648
846k
      for (auto it = o.begin(); it != o.end(); it++) {
3649
809k
        Value entry;
3650
809k
        ParseJsonAsValue(&entry, it.value());
3651
809k
        if (entry.Type() != NULL_TYPE)
3652
773k
          value_array.emplace_back(std::move(entry));
3653
809k
      }
3654
36.8k
      if (value_array.size() > 0) val = Value(std::move(value_array));
3655
36.8k
    } break;
3656
2.90k
    case detail::json::value_t::string:
3657
2.90k
      val = Value(o.get<std::string>());
3658
2.90k
      break;
3659
198
    case detail::json::value_t::boolean:
3660
198
      val = Value(o.get<bool>());
3661
198
      break;
3662
2.83k
    case detail::json::value_t::number_integer:
3663
775k
    case detail::json::value_t::number_unsigned:
3664
775k
      val = Value(static_cast<int>(o.get<int64_t>()));
3665
775k
      break;
3666
4.56k
    case detail::json::value_t::number_float:
3667
4.56k
      val = Value(o.get<double>());
3668
4.56k
      break;
3669
167
    case detail::json::value_t::null:
3670
167
    case detail::json::value_t::discarded:
3671
167
    case detail::json::value_t::binary:
3672
      // default:
3673
167
      break;
3674
842k
  }
3675
842k
#endif
3676
842k
  const bool isNotNull = val.Type() != NULL_TYPE;
3677
3678
842k
  if (ret) *ret = std::move(val);
3679
3680
842k
  return isNotNull;
3681
842k
}
3682
3683
2.00M
static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
3684
2.00M
  detail::json_const_iterator it;
3685
2.00M
  if (!detail::FindMember(o, "extras", it)) {
3686
2.00M
    return false;
3687
2.00M
  }
3688
3689
2.22k
  return ParseJsonAsValue(ret, detail::GetValue(it));
3690
2.00M
}
3691
3692
static bool ParseBooleanProperty(bool *ret, std::string *err,
3693
                                 const detail::json &o,
3694
                                 const std::string &property,
3695
                                 const bool required,
3696
313k
                                 const std::string &parent_node = "") {
3697
313k
  detail::json_const_iterator it;
3698
313k
  if (!detail::FindMember(o, property.c_str(), it)) {
3699
312k
    if (required) {
3700
0
      if (err) {
3701
0
        (*err) += "'" + property + "' property is missing";
3702
0
        if (!parent_node.empty()) {
3703
0
          (*err) += " in " + parent_node;
3704
0
        }
3705
0
        (*err) += ".\n";
3706
0
      }
3707
0
    }
3708
312k
    return false;
3709
312k
  }
3710
3711
917
  auto &value = detail::GetValue(it);
3712
3713
917
  bool isBoolean;
3714
917
  bool boolValue = false;
3715
#ifdef TINYGLTF_USE_RAPIDJSON
3716
  isBoolean = value.IsBool();
3717
  if (isBoolean) {
3718
    boolValue = value.GetBool();
3719
  }
3720
#else
3721
917
  isBoolean = value.is_boolean();
3722
917
  if (isBoolean) {
3723
287
    boolValue = value.get<bool>();
3724
287
  }
3725
917
#endif
3726
917
  if (!isBoolean) {
3727
630
    if (required) {
3728
0
      if (err) {
3729
0
        (*err) += "'" + property + "' property is not a bool type.\n";
3730
0
      }
3731
0
    }
3732
630
    return false;
3733
630
  }
3734
3735
287
  if (ret) {
3736
287
    (*ret) = boolValue;
3737
287
  }
3738
3739
287
  return true;
3740
917
}
3741
3742
static bool ParseIntegerProperty(int *ret, std::string *err,
3743
                                 const detail::json &o,
3744
                                 const std::string &property,
3745
                                 const bool required,
3746
2.46M
                                 const std::string &parent_node = "") {
3747
2.46M
  detail::json_const_iterator it;
3748
2.46M
  if (!detail::FindMember(o, property.c_str(), it)) {
3749
2.45M
    if (required) {
3750
774k
      if (err) {
3751
774k
        (*err) += "'" + property + "' property is missing";
3752
774k
        if (!parent_node.empty()) {
3753
5.68k
          (*err) += " in " + parent_node;
3754
5.68k
        }
3755
774k
        (*err) += ".\n";
3756
774k
      }
3757
774k
    }
3758
2.45M
    return false;
3759
2.45M
  }
3760
3761
3.22k
  int intValue;
3762
3.22k
  bool isInt = detail::GetInt(detail::GetValue(it), intValue);
3763
3.22k
  if (!isInt) {
3764
171
    if (required) {
3765
171
      if (err) {
3766
171
        (*err) += "'" + property + "' property is not an integer type.\n";
3767
171
      }
3768
171
    }
3769
171
    return false;
3770
171
  }
3771
3772
3.04k
  if (ret) {
3773
3.04k
    (*ret) = intValue;
3774
3.04k
  }
3775
3776
3.04k
  return true;
3777
3.22k
}
3778
3779
static bool ParseUnsignedProperty(size_t *ret, std::string *err,
3780
                                  const detail::json &o,
3781
                                  const std::string &property,
3782
                                  const bool required,
3783
0
                                  const std::string &parent_node = "") {
3784
0
  detail::json_const_iterator it;
3785
0
  if (!detail::FindMember(o, property.c_str(), it)) {
3786
0
    if (required) {
3787
0
      if (err) {
3788
0
        (*err) += "'" + property + "' property is missing";
3789
0
        if (!parent_node.empty()) {
3790
0
          (*err) += " in " + parent_node;
3791
0
        }
3792
0
        (*err) += ".\n";
3793
0
      }
3794
0
    }
3795
0
    return false;
3796
0
  }
3797
3798
0
  auto &value = detail::GetValue(it);
3799
3800
0
  size_t uValue = 0;
3801
0
  bool isUValue;
3802
#ifdef TINYGLTF_USE_RAPIDJSON
3803
  isUValue = false;
3804
  if (value.IsUint()) {
3805
    uValue = value.GetUint();
3806
    isUValue = true;
3807
  } else if (value.IsUint64()) {
3808
    uValue = value.GetUint64();
3809
    isUValue = true;
3810
  }
3811
#else
3812
0
  isUValue = value.is_number_unsigned();
3813
0
  if (isUValue) {
3814
0
    uValue = value.get<size_t>();
3815
0
  }
3816
0
#endif
3817
0
  if (!isUValue) {
3818
0
    if (required) {
3819
0
      if (err) {
3820
0
        (*err) += "'" + property + "' property is not a positive integer.\n";
3821
0
      }
3822
0
    }
3823
0
    return false;
3824
0
  }
3825
3826
0
  if (ret) {
3827
0
    (*ret) = uValue;
3828
0
  }
3829
3830
0
  return true;
3831
0
}
3832
3833
static bool ParseNumberProperty(double *ret, std::string *err,
3834
                                const detail::json &o,
3835
                                const std::string &property,
3836
                                const bool required,
3837
330k
                                const std::string &parent_node = "") {
3838
330k
  detail::json_const_iterator it;
3839
3840
330k
  if (!detail::FindMember(o, property.c_str(), it)) {
3841
316k
    if (required) {
3842
0
      if (err) {
3843
0
        (*err) += "'" + property + "' property is missing";
3844
0
        if (!parent_node.empty()) {
3845
0
          (*err) += " in " + parent_node;
3846
0
        }
3847
0
        (*err) += ".\n";
3848
0
      }
3849
0
    }
3850
316k
    return false;
3851
316k
  }
3852
3853
14.0k
  double numberValue;
3854
14.0k
  bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
3855
3856
14.0k
  if (!isNumber) {
3857
6.08k
    if (required) {
3858
0
      if (err) {
3859
0
        (*err) += "'" + property + "' property is not a number type.\n";
3860
0
      }
3861
0
    }
3862
6.08k
    return false;
3863
6.08k
  }
3864
3865
7.94k
  if (ret) {
3866
7.94k
    (*ret) = numberValue;
3867
7.94k
  }
3868
3869
7.94k
  return true;
3870
14.0k
}
3871
3872
static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
3873
                                     const detail::json &o,
3874
                                     const std::string &property, bool required,
3875
969k
                                     const std::string &parent_node = "") {
3876
969k
  detail::json_const_iterator it;
3877
969k
  if (!detail::FindMember(o, property.c_str(), it)) {
3878
952k
    if (required) {
3879
0
      if (err) {
3880
0
        (*err) += "'" + property + "' property is missing";
3881
0
        if (!parent_node.empty()) {
3882
0
          (*err) += " in " + parent_node;
3883
0
        }
3884
0
        (*err) += ".\n";
3885
0
      }
3886
0
    }
3887
952k
    return false;
3888
952k
  }
3889
3890
16.7k
  if (!detail::IsArray(detail::GetValue(it))) {
3891
13.4k
    if (required) {
3892
0
      if (err) {
3893
0
        (*err) += "'" + property + "' property is not an array";
3894
0
        if (!parent_node.empty()) {
3895
0
          (*err) += " in " + parent_node;
3896
0
        }
3897
0
        (*err) += ".\n";
3898
0
      }
3899
0
    }
3900
13.4k
    return false;
3901
13.4k
  }
3902
3903
3.39k
  ret->clear();
3904
3.39k
  auto end = detail::ArrayEnd(detail::GetValue(it));
3905
731k
  for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
3906
729k
    double numberValue;
3907
729k
    const bool isNumber = detail::GetNumber(*i, numberValue);
3908
729k
    if (!isNumber) {
3909
1.01k
      if (required) {
3910
0
        if (err) {
3911
0
          (*err) += "'" + property + "' property is not a number.\n";
3912
0
          if (!parent_node.empty()) {
3913
0
            (*err) += " in " + parent_node;
3914
0
          }
3915
0
          (*err) += ".\n";
3916
0
        }
3917
0
      }
3918
1.01k
      return false;
3919
1.01k
    }
3920
728k
    ret->push_back(numberValue);
3921
728k
  }
3922
3923
2.38k
  return true;
3924
3.39k
}
3925
3926
static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3927
                                      const detail::json &o,
3928
                                      const std::string &property,
3929
                                      bool required,
3930
141k
                                      const std::string &parent_node = "") {
3931
141k
  detail::json_const_iterator it;
3932
141k
  if (!detail::FindMember(o, property.c_str(), it)) {
3933
139k
    if (required) {
3934
0
      if (err) {
3935
0
        (*err) += "'" + property + "' property is missing";
3936
0
        if (!parent_node.empty()) {
3937
0
          (*err) += " in " + parent_node;
3938
0
        }
3939
0
        (*err) += ".\n";
3940
0
      }
3941
0
    }
3942
139k
    return false;
3943
139k
  }
3944
3945
1.85k
  if (!detail::IsArray(detail::GetValue(it))) {
3946
164
    if (required) {
3947
0
      if (err) {
3948
0
        (*err) += "'" + property + "' property is not an array";
3949
0
        if (!parent_node.empty()) {
3950
0
          (*err) += " in " + parent_node;
3951
0
        }
3952
0
        (*err) += ".\n";
3953
0
      }
3954
0
    }
3955
164
    return false;
3956
164
  }
3957
3958
1.69k
  ret->clear();
3959
1.69k
  auto end = detail::ArrayEnd(detail::GetValue(it));
3960
542k
  for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
3961
541k
    int numberValue;
3962
541k
    bool isNumber = detail::GetInt(*i, numberValue);
3963
541k
    if (!isNumber) {
3964
1.25k
      if (required) {
3965
0
        if (err) {
3966
0
          (*err) += "'" + property + "' property is not an integer type.\n";
3967
0
          if (!parent_node.empty()) {
3968
0
            (*err) += " in " + parent_node;
3969
0
          }
3970
0
          (*err) += ".\n";
3971
0
        }
3972
0
      }
3973
1.25k
      return false;
3974
1.25k
    }
3975
540k
    ret->push_back(numberValue);
3976
540k
  }
3977
3978
436
  return true;
3979
1.69k
}
3980
3981
static bool ParseStringProperty(
3982
    std::string *ret, std::string *err, const detail::json &o,
3983
    const std::string &property, bool required,
3984
4.24M
    const std::string &parent_node = std::string()) {
3985
4.24M
  detail::json_const_iterator it;
3986
4.24M
  if (!detail::FindMember(o, property.c_str(), it)) {
3987
4.15M
    if (required) {
3988
768k
      if (err) {
3989
768k
        (*err) += "'" + property + "' property is missing";
3990
768k
        if (parent_node.empty()) {
3991
768k
          (*err) += ".\n";
3992
768k
        } else {
3993
0
          (*err) += " in `" + parent_node + "'.\n";
3994
0
        }
3995
768k
      }
3996
768k
    }
3997
4.15M
    return false;
3998
4.15M
  }
3999
4000
84.0k
  std::string strValue;
4001
84.0k
  if (!detail::GetString(detail::GetValue(it), strValue)) {
4002
15.8k
    if (required) {
4003
169
      if (err) {
4004
169
        (*err) += "'" + property + "' property is not a string type.\n";
4005
169
      }
4006
169
    }
4007
15.8k
    return false;
4008
15.8k
  }
4009
4010
68.1k
  if (ret) {
4011
68.1k
    (*ret) = std::move(strValue);
4012
68.1k
  }
4013
4014
68.1k
  return true;
4015
84.0k
}
4016
4017
static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
4018
                                       std::string *err, const detail::json &o,
4019
                                       const std::string &property,
4020
                                       bool required,
4021
4.09k
                                       const std::string &parent = "") {
4022
4.09k
  detail::json_const_iterator it;
4023
4.09k
  if (!detail::FindMember(o, property.c_str(), it)) {
4024
390
    if (required) {
4025
390
      if (err) {
4026
390
        if (!parent.empty()) {
4027
390
          (*err) +=
4028
390
              "'" + property + "' property is missing in " + parent + ".\n";
4029
390
        } else {
4030
0
          (*err) += "'" + property + "' property is missing.\n";
4031
0
        }
4032
390
      }
4033
390
    }
4034
390
    return false;
4035
390
  }
4036
4037
3.70k
  const detail::json &dict = detail::GetValue(it);
4038
4039
  // Make sure we are dealing with an object / dictionary.
4040
3.70k
  if (!detail::IsObject(dict)) {
4041
134
    if (required) {
4042
134
      if (err) {
4043
134
        (*err) += "'" + property + "' property is not an object.\n";
4044
134
      }
4045
134
    }
4046
134
    return false;
4047
134
  }
4048
4049
3.56k
  ret->clear();
4050
4051
3.56k
  detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4052
3.56k
  detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
4053
4054
8.39k
  for (; dictIt != dictItEnd; ++dictIt) {
4055
5.04k
    int intVal;
4056
5.04k
    if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
4057
218
      if (required) {
4058
218
        if (err) {
4059
218
          (*err) += "'" + property + "' value is not an integer type.\n";
4060
218
        }
4061
218
      }
4062
218
      return false;
4063
218
    }
4064
4065
    // Insert into the list.
4066
4.82k
    (*ret)[detail::GetKey(dictIt)] = intVal;
4067
4.82k
  }
4068
3.34k
  return true;
4069
3.56k
}
4070
4071
static bool ParseJSONProperty(std::map<std::string, double> *ret,
4072
                              std::string *err, const detail::json &o,
4073
6.41k
                              const std::string &property, bool required) {
4074
6.41k
  detail::json_const_iterator it;
4075
6.41k
  if (!detail::FindMember(o, property.c_str(), it)) {
4076
329
    if (required) {
4077
0
      if (err) {
4078
0
        (*err) += "'" + property + "' property is missing. \n'";
4079
0
      }
4080
0
    }
4081
329
    return false;
4082
329
  }
4083
4084
6.08k
  const detail::json &obj = detail::GetValue(it);
4085
4086
6.08k
  if (!detail::IsObject(obj)) {
4087
894
    if (required) {
4088
0
      if (err) {
4089
0
        (*err) += "'" + property + "' property is not a JSON object.\n";
4090
0
      }
4091
0
    }
4092
894
    return false;
4093
894
  }
4094
4095
5.19k
  ret->clear();
4096
4097
5.19k
  detail::json_const_iterator it2(detail::ObjectBegin(obj));
4098
5.19k
  detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
4099
8.93k
  for (; it2 != itEnd; ++it2) {
4100
3.74k
    double numVal;
4101
3.74k
    if (detail::GetNumber(detail::GetValue(it2), numVal))
4102
2.99k
      ret->emplace(std::string(detail::GetKey(it2)), numVal);
4103
3.74k
  }
4104
4105
5.19k
  return true;
4106
6.08k
}
4107
4108
static bool ParseParameterProperty(Parameter *param, std::string *err,
4109
                                   const detail::json &o,
4110
17.5k
                                   const std::string &prop, bool required) {
4111
  // A parameter value can either be a string or an array of either a boolean or
4112
  // a number. Booleans of any kind aren't supported here. Granted, it
4113
  // complicates the Parameter structure and breaks it semantically in the sense
4114
  // that the client probably works off the assumption that if the string is
4115
  // empty the vector is used, etc. Would a tagged union work?
4116
17.5k
  if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
4117
    // Found string property.
4118
1.46k
    return true;
4119
16.0k
  } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
4120
16.0k
                                      false)) {
4121
    // Found a number array.
4122
1.69k
    return true;
4123
14.3k
  } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
4124
7.94k
    param->has_number_value = true;
4125
7.94k
    return true;
4126
7.94k
  } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
4127
6.41k
                               false)) {
4128
5.19k
    return true;
4129
5.19k
  } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
4130
264
    return true;
4131
959
  } else {
4132
959
    if (required) {
4133
0
      if (err) {
4134
0
        (*err) += "parameter must be a string or number / number array.\n";
4135
0
      }
4136
0
    }
4137
959
    return false;
4138
959
  }
4139
17.5k
}
4140
4141
static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
4142
2.00M
                                    const detail::json &o) {
4143
2.00M
  (void)err;
4144
4145
2.00M
  detail::json_const_iterator it;
4146
2.00M
  if (!detail::FindMember(o, "extensions", it)) {
4147
2.00M
    return false;
4148
2.00M
  }
4149
4150
5.77k
  auto &obj = detail::GetValue(it);
4151
5.77k
  if (!detail::IsObject(obj)) {
4152
264
    return false;
4153
264
  }
4154
5.50k
  ExtensionMap extensions;
4155
5.50k
  detail::json_const_iterator extIt =
4156
5.50k
      detail::ObjectBegin(obj);  // it.value().begin();
4157
5.50k
  detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
4158
19.6k
  for (; extIt != extEnd; ++extIt) {
4159
14.1k
    auto &itObj = detail::GetValue(extIt);
4160
14.1k
    if (!detail::IsObject(itObj)) continue;
4161
12.2k
    std::string key(detail::GetKey(extIt));
4162
12.2k
    if (!ParseJsonAsValue(&extensions[key], itObj)) {
4163
9.56k
      if (!key.empty()) {
4164
        // create empty object so that an extension object is still of type
4165
        // object
4166
7.69k
        extensions[key] = Value{Value::Object{}};
4167
7.69k
      }
4168
9.56k
    }
4169
12.2k
  }
4170
5.50k
  if (ret) {
4171
5.50k
    (*ret) = std::move(extensions);
4172
5.50k
  }
4173
5.50k
  return true;
4174
5.77k
}
4175
4176
template <typename GltfType>
4177
static bool ParseExtrasAndExtensions(GltfType *target, std::string *err,
4178
                                     const detail::json &o,
4179
2.00M
                                     bool store_json_strings) {
4180
2.00M
  ParseExtensionsProperty(&target->extensions, err, o);
4181
2.00M
  ParseExtrasProperty(&target->extras, o);
4182
4183
2.00M
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
2.00M
  return true;
4199
2.00M
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Asset>(tinygltf::Asset*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
1.59k
                                     bool store_json_strings) {
4180
1.59k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
1.59k
  ParseExtrasProperty(&target->extras, o);
4182
4183
1.59k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
1.59k
  return true;
4199
1.59k
}
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::SpotLight>(tinygltf::SpotLight*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Light>(tinygltf::Light*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::PositionalEmitter>(tinygltf::PositionalEmitter*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::AudioEmitter>(tinygltf::AudioEmitter*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::AudioSource>(tinygltf::AudioSource*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
769k
                                     bool store_json_strings) {
4180
769k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
769k
  ParseExtrasProperty(&target->extras, o);
4182
4183
769k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
769k
  return true;
4199
769k
}
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Buffer>(tinygltf::Buffer*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::BufferView>(tinygltf::BufferView*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Accessor>(tinygltf::Accessor*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Accessor::Sparse>(tinygltf::Accessor::Sparse*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Accessor::Sparse::{unnamed type#1}>(tinygltf::Accessor::Sparse::{unnamed type#1}*, std::__1::basic_string<char, tinygltf::Accessor::Sparse::{unnamed type#1}*::char_traits<char>, tinygltf::Accessor::Sparse::{unnamed type#1}*::allocator<char> >*, nlohmann::basic_json<tinygltf::Accessor::Sparse::{unnamed type#1}*::map, tinygltf::Accessor::Sparse::{unnamed type#1}*::vector, tinygltf::Accessor::Sparse::{unnamed type#1}*::allocator<char>, bool, long, unsigned long, double, tinygltf::Accessor::Sparse::{unnamed type#1}*::char_traits<char>, std::__1::basic_string<char, tinygltf::Accessor::Sparse::{unnamed type#1}*::char_traits<char>, tinygltf::Accessor::Sparse::{unnamed type#1}*::allocator<char> >*::adl_serializer, tinygltf::Accessor::Sparse::{unnamed type#1}*::map<unsigned char, tinygltf::Accessor::Sparse::{unnamed type#1}*::char_traits<char><unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Accessor::Sparse::{unnamed type#2}>(tinygltf::Accessor::Sparse::{unnamed type#2}*, std::__1::basic_string<char, tinygltf::Accessor::Sparse::{unnamed type#2}*::char_traits<char>, tinygltf::Accessor::Sparse::{unnamed type#2}*::allocator<char> >*, nlohmann::basic_json<tinygltf::Accessor::Sparse::{unnamed type#2}*::map, tinygltf::Accessor::Sparse::{unnamed type#2}*::vector, tinygltf::Accessor::Sparse::{unnamed type#2}*::allocator<char>, bool, long, unsigned long, double, tinygltf::Accessor::Sparse::{unnamed type#2}*::char_traits<char>, std::__1::basic_string<char, tinygltf::Accessor::Sparse::{unnamed type#2}*::char_traits<char>, tinygltf::Accessor::Sparse::{unnamed type#2}*::allocator<char> >*::adl_serializer, tinygltf::Accessor::Sparse::{unnamed type#2}*::map<unsigned char, tinygltf::Accessor::Sparse::{unnamed type#2}*::char_traits<char><unsigned char> > > const&, bool)
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Primitive>(tinygltf::Primitive*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
3.34k
                                     bool store_json_strings) {
4180
3.34k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
3.34k
  ParseExtrasProperty(&target->extras, o);
4182
4183
3.34k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
3.34k
  return true;
4199
3.34k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Mesh>(tinygltf::Mesh*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
272k
                                     bool store_json_strings) {
4180
272k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
272k
  ParseExtrasProperty(&target->extras, o);
4182
4183
272k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
272k
  return true;
4199
272k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Node>(tinygltf::Node*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
73.4k
                                     bool store_json_strings) {
4180
73.4k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
73.4k
  ParseExtrasProperty(&target->extras, o);
4182
4183
73.4k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
73.4k
  return true;
4199
73.4k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Scene>(tinygltf::Scene*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
67.7k
                                     bool store_json_strings) {
4180
67.7k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
67.7k
  ParseExtrasProperty(&target->extras, o);
4182
4183
67.7k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
67.7k
  return true;
4199
67.7k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::PbrMetallicRoughness>(tinygltf::PbrMetallicRoughness*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
1.72k
                                     bool store_json_strings) {
4180
1.72k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
1.72k
  ParseExtrasProperty(&target->extras, o);
4182
4183
1.72k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
1.72k
  return true;
4199
1.72k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::NormalTextureInfo>(tinygltf::NormalTextureInfo*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
347
                                     bool store_json_strings) {
4180
347
  ParseExtensionsProperty(&target->extensions, err, o);
4181
347
  ParseExtrasProperty(&target->extras, o);
4182
4183
347
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
347
  return true;
4199
347
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::OcclusionTextureInfo>(tinygltf::OcclusionTextureInfo*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
308
                                     bool store_json_strings) {
4180
308
  ParseExtensionsProperty(&target->extensions, err, o);
4181
308
  ParseExtrasProperty(&target->extras, o);
4182
4183
308
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
308
  return true;
4199
308
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::TextureInfo>(tinygltf::TextureInfo*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
161
                                     bool store_json_strings) {
4180
161
  ParseExtensionsProperty(&target->extensions, err, o);
4181
161
  ParseExtrasProperty(&target->extras, o);
4182
4183
161
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
161
  return true;
4199
161
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Material>(tinygltf::Material*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
311k
                                     bool store_json_strings) {
4180
311k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
311k
  ParseExtrasProperty(&target->extras, o);
4182
4183
311k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
311k
  return true;
4199
311k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Image>(tinygltf::Image*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
61.1k
                                     bool store_json_strings) {
4180
61.1k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
61.1k
  ParseExtrasProperty(&target->extras, o);
4182
4183
61.1k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
61.1k
  return true;
4199
61.1k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Texture>(tinygltf::Texture*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
135k
                                     bool store_json_strings) {
4180
135k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
135k
  ParseExtrasProperty(&target->extras, o);
4182
4183
135k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
135k
  return true;
4199
135k
}
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::AnimationChannel>(tinygltf::AnimationChannel*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
2.07k
                                     bool store_json_strings) {
4180
2.07k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
2.07k
  ParseExtrasProperty(&target->extras, o);
4182
4183
2.07k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
2.07k
  return true;
4199
2.07k
}
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::AnimationSampler>(tinygltf::AnimationSampler*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Animation>(tinygltf::Animation*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
11.1k
                                     bool store_json_strings) {
4180
11.1k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
11.1k
  ParseExtrasProperty(&target->extras, o);
4182
4183
11.1k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
11.1k
  return true;
4199
11.1k
}
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Skin>(tinygltf::Skin*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Sampler>(tinygltf::Sampler*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
294k
                                     bool store_json_strings) {
4180
294k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
294k
  ParseExtrasProperty(&target->extras, o);
4182
4183
294k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
294k
  return true;
4199
294k
}
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::OrthographicCamera>(tinygltf::OrthographicCamera*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::PerspectiveCamera>(tinygltf::PerspectiveCamera*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Camera>(tinygltf::Camera*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Model>(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4179
1.14k
                                     bool store_json_strings) {
4180
1.14k
  ParseExtensionsProperty(&target->extensions, err, o);
4181
1.14k
  ParseExtrasProperty(&target->extras, o);
4182
4183
1.14k
  if (store_json_strings) {
4184
0
    {
4185
0
      detail::json_const_iterator it;
4186
0
      if (detail::FindMember(o, "extensions", it)) {
4187
0
        target->extensions_json_string =
4188
0
            detail::JsonToString(detail::GetValue(it));
4189
0
      }
4190
0
    }
4191
0
    {
4192
0
      detail::json_const_iterator it;
4193
0
      if (detail::FindMember(o, "extras", it)) {
4194
0
        target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195
0
      }
4196
0
    }
4197
0
  }
4198
1.14k
  return true;
4199
1.14k
}
4200
4201
static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
4202
1.59k
                       bool store_original_json_for_extras_and_extensions) {
4203
1.59k
  ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
4204
1.59k
  ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
4205
1.59k
  ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
4206
1.59k
  ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
4207
4208
1.59k
  ParseExtrasAndExtensions(asset, err, o,
4209
1.59k
                           store_original_json_for_extras_and_extensions);
4210
1.59k
  return true;
4211
1.59k
}
4212
4213
static bool ParseImage(Image *image, const int image_idx, std::string *err,
4214
                       std::string *warn, const detail::json &o,
4215
                       bool store_original_json_for_extras_and_extensions,
4216
                       const std::string &basedir, const size_t max_file_size,
4217
                       FsCallbacks *fs, const URICallbacks *uri_cb,
4218
                       LoadImageDataFunction *LoadImageData = nullptr,
4219
61.2k
                       void *load_image_user_data = nullptr) {
4220
  // A glTF image must either reference a bufferView or an image uri
4221
4222
  // schema says oneOf [`bufferView`, `uri`]
4223
  // TODO(syoyo): Check the type of each parameters.
4224
61.2k
  detail::json_const_iterator it;
4225
61.2k
  bool hasBufferView = detail::FindMember(o, "bufferView", it);
4226
61.2k
  bool hasURI = detail::FindMember(o, "uri", it);
4227
4228
61.2k
  ParseStringProperty(&image->name, err, o, "name", false);
4229
4230
61.2k
  if (hasBufferView && hasURI) {
4231
    // Should not both defined.
4232
0
    if (err) {
4233
0
      (*err) +=
4234
0
          "Only one of `bufferView` or `uri` should be defined, but both are "
4235
0
          "defined for image[" +
4236
0
          std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
4237
0
    }
4238
0
    return false;
4239
0
  }
4240
4241
61.2k
  if (!hasBufferView && !hasURI) {
4242
12
    if (err) {
4243
12
      (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
4244
12
                std::to_string(image_idx) + "] name = \"" + image->name +
4245
12
                "\"\n";
4246
12
    }
4247
12
    return false;
4248
12
  }
4249
4250
61.1k
  ParseExtrasAndExtensions(image, err, o,
4251
61.1k
                           store_original_json_for_extras_and_extensions);
4252
4253
61.1k
  if (hasBufferView) {
4254
0
    int bufferView = -1;
4255
0
    if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
4256
0
      if (err) {
4257
0
        (*err) += "Failed to parse `bufferView` for image[" +
4258
0
                  std::to_string(image_idx) + "] name = \"" + image->name +
4259
0
                  "\"\n";
4260
0
      }
4261
0
      return false;
4262
0
    }
4263
4264
0
    std::string mime_type;
4265
0
    ParseStringProperty(&mime_type, err, o, "mimeType", false);
4266
4267
0
    int width = 0;
4268
0
    ParseIntegerProperty(&width, err, o, "width", false);
4269
4270
0
    int height = 0;
4271
0
    ParseIntegerProperty(&height, err, o, "height", false);
4272
4273
    // Just only save some information here. Loading actual image data from
4274
    // bufferView is done after this `ParseImage` function.
4275
0
    image->bufferView = bufferView;
4276
0
    image->mimeType = mime_type;
4277
0
    image->width = width;
4278
0
    image->height = height;
4279
4280
0
    return true;
4281
0
  }
4282
4283
  // Parse URI & Load image data.
4284
4285
61.1k
  std::string uri;
4286
61.1k
  std::string tmp_err;
4287
61.1k
  if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
4288
1
    if (err) {
4289
1
      (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
4290
1
                "] name = \"" + image->name + "\".\n";
4291
1
    }
4292
1
    return false;
4293
1
  }
4294
4295
61.1k
  std::vector<unsigned char> img;
4296
4297
61.1k
  if (IsDataURI(uri)) {
4298
19.0k
    if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
4299
47
      if (err) {
4300
47
        (*err) += "Failed to decode 'uri' for image[" +
4301
47
                  std::to_string(image_idx) + "] name = \"" + image->name +
4302
47
                  "\"\n";
4303
47
      }
4304
47
      return false;
4305
47
    }
4306
42.1k
  } else {
4307
    // Assume external file
4308
    // Keep texture path (for textures that cannot be decoded)
4309
42.1k
    image->uri = uri;
4310
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
4311
    return true;
4312
#else
4313
42.1k
    std::string decoded_uri;
4314
42.1k
    if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
4315
0
      if (warn) {
4316
0
        (*warn) += "Failed to decode 'uri' for image[" +
4317
0
                   std::to_string(image_idx) + "] name = \"" + image->name +
4318
0
                   "\"\n";
4319
0
      }
4320
4321
      // Image loading failure is not critical to overall gltf loading.
4322
0
      return true;
4323
0
    }
4324
4325
42.1k
    if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4326
42.1k
                          /* required */ false, /* required bytes */ 0,
4327
42.1k
                          /* checksize */ false,
4328
42.1k
                          /* max file size */ max_file_size, fs)) {
4329
42.1k
      if (warn) {
4330
42.1k
        (*warn) += "Failed to load external 'uri' for image[" +
4331
42.1k
                   std::to_string(image_idx) + "] name = \"" + decoded_uri +
4332
42.1k
                   "\"\n";
4333
42.1k
      }
4334
      // If the image cannot be loaded, keep uri as image->uri.
4335
42.1k
      return true;
4336
42.1k
    }
4337
4338
0
    if (img.empty()) {
4339
0
      if (warn) {
4340
0
        (*warn) += "Image data is empty for image[" +
4341
0
                   std::to_string(image_idx) + "] name = \"" + image->name +
4342
0
                   "\" \n";
4343
0
      }
4344
0
      return false;
4345
0
    }
4346
0
#endif
4347
0
  }
4348
4349
19.0k
  if (*LoadImageData == nullptr) {
4350
0
    if (err) {
4351
0
      (*err) += "No LoadImageData callback specified.\n";
4352
0
    }
4353
0
    return false;
4354
0
  }
4355
19.0k
  return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
4356
19.0k
                          static_cast<int>(img.size()), load_image_user_data);
4357
19.0k
}
4358
4359
static bool ParseTexture(Texture *texture, std::string *err,
4360
                         const detail::json &o,
4361
                         bool store_original_json_for_extras_and_extensions,
4362
135k
                         const std::string &basedir) {
4363
135k
  (void)basedir;
4364
135k
  int sampler = -1;
4365
135k
  int source = -1;
4366
135k
  ParseIntegerProperty(&sampler, err, o, "sampler", false);
4367
4368
135k
  ParseIntegerProperty(&source, err, o, "source", false);
4369
4370
135k
  texture->sampler = sampler;
4371
135k
  texture->source = source;
4372
4373
135k
  ParseExtrasAndExtensions(texture, err, o,
4374
135k
                           store_original_json_for_extras_and_extensions);
4375
4376
135k
  ParseStringProperty(&texture->name, err, o, "name", false);
4377
4378
135k
  return true;
4379
135k
}
4380
4381
static bool ParseTextureInfo(
4382
    TextureInfo *texinfo, std::string *err, const detail::json &o,
4383
733
    bool store_original_json_for_extras_and_extensions) {
4384
733
  if (texinfo == nullptr) {
4385
0
    return false;
4386
0
  }
4387
4388
733
  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4389
733
                            /* required */ true, "TextureInfo")) {
4390
572
    return false;
4391
572
  }
4392
4393
161
  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4394
4395
161
  ParseExtrasAndExtensions(texinfo, err, o,
4396
161
                           store_original_json_for_extras_and_extensions);
4397
4398
161
  return true;
4399
733
}
4400
4401
static bool ParseNormalTextureInfo(
4402
    NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
4403
555
    bool store_original_json_for_extras_and_extensions) {
4404
555
  if (texinfo == nullptr) {
4405
0
    return false;
4406
0
  }
4407
4408
555
  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4409
555
                            /* required */ true, "NormalTextureInfo")) {
4410
208
    return false;
4411
208
  }
4412
4413
347
  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4414
347
  ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4415
4416
347
  ParseExtrasAndExtensions(texinfo, err, o,
4417
347
                           store_original_json_for_extras_and_extensions);
4418
4419
347
  return true;
4420
555
}
4421
4422
static bool ParseOcclusionTextureInfo(
4423
    OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
4424
494
    bool store_original_json_for_extras_and_extensions) {
4425
494
  if (texinfo == nullptr) {
4426
0
    return false;
4427
0
  }
4428
4429
494
  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4430
494
                            /* required */ true, "NormalTextureInfo")) {
4431
186
    return false;
4432
186
  }
4433
4434
308
  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4435
308
  ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4436
4437
308
  ParseExtrasAndExtensions(texinfo, err, o,
4438
308
                           store_original_json_for_extras_and_extensions);
4439
4440
308
  return true;
4441
494
}
4442
4443
static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o,
4444
                        bool store_original_json_for_extras_and_extensions,
4445
                        FsCallbacks *fs, const URICallbacks *uri_cb,
4446
                        const std::string &basedir,
4447
                        const size_t max_buffer_size, bool is_binary = false,
4448
                        const unsigned char *bin_data = nullptr,
4449
0
                        size_t bin_size = 0) {
4450
0
  size_t byteLength;
4451
0
  if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4452
0
                             "Buffer")) {
4453
0
    return false;
4454
0
  }
4455
4456
  // In glTF 2.0, uri is not mandatory anymore
4457
0
  buffer->uri.clear();
4458
0
  ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
4459
4460
  // having an empty uri for a non embedded image should not be valid
4461
0
  if (!is_binary && buffer->uri.empty()) {
4462
0
    if (err) {
4463
0
      (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4464
0
    }
4465
0
  }
4466
4467
0
  detail::json_const_iterator type;
4468
0
  if (detail::FindMember(o, "type", type)) {
4469
0
    std::string typeStr;
4470
0
    if (detail::GetString(detail::GetValue(type), typeStr)) {
4471
0
      if (typeStr.compare("arraybuffer") == 0) {
4472
        // buffer.type = "arraybuffer";
4473
0
      }
4474
0
    }
4475
0
  }
4476
4477
0
  if (is_binary) {
4478
    // Still binary glTF accepts external dataURI.
4479
0
    if (!buffer->uri.empty()) {
4480
      // First try embedded data URI.
4481
0
      if (IsDataURI(buffer->uri)) {
4482
0
        std::string mime_type;
4483
0
        if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4484
0
                           true)) {
4485
0
          if (err) {
4486
0
            (*err) +=
4487
0
                "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4488
0
          }
4489
0
          return false;
4490
0
        }
4491
0
      } else {
4492
        // External .bin file.
4493
0
        std::string decoded_uri;
4494
0
        if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4495
0
          return false;
4496
0
        }
4497
0
        if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
4498
0
                              decoded_uri, basedir, /* required */ true,
4499
0
                              byteLength, /* checkSize */ true,
4500
0
                              /* max_file_size */ max_buffer_size, fs)) {
4501
0
          return false;
4502
0
        }
4503
0
      }
4504
0
    } else {
4505
      // load data from (embedded) binary data
4506
4507
0
      if ((bin_size == 0) || (bin_data == nullptr)) {
4508
0
        if (err) {
4509
0
          (*err) +=
4510
0
              "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
4511
0
        }
4512
0
        return false;
4513
0
      }
4514
4515
0
      if (byteLength > bin_size) {
4516
0
        if (err) {
4517
0
          std::stringstream ss;
4518
0
          ss << "Invalid `byteLength'. Must be equal or less than binary size: "
4519
0
                "`byteLength' = "
4520
0
             << byteLength << ", binary size = " << bin_size << std::endl;
4521
0
          (*err) += ss.str();
4522
0
        }
4523
0
        return false;
4524
0
      }
4525
4526
      // Read buffer data
4527
0
      buffer->data.resize(static_cast<size_t>(byteLength));
4528
0
      memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
4529
0
    }
4530
4531
0
  } else {
4532
0
    if (IsDataURI(buffer->uri)) {
4533
0
      std::string mime_type;
4534
0
      if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4535
0
                         true)) {
4536
0
        if (err) {
4537
0
          (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4538
0
        }
4539
0
        return false;
4540
0
      }
4541
0
    } else {
4542
      // Assume external .bin file.
4543
0
      std::string decoded_uri;
4544
0
      if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4545
0
        return false;
4546
0
      }
4547
0
      if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4548
0
                            basedir, /* required */ true, byteLength,
4549
0
                            /* checkSize */ true,
4550
0
                            /* max file size */ max_buffer_size, fs)) {
4551
0
        return false;
4552
0
      }
4553
0
    }
4554
0
  }
4555
4556
0
  ParseStringProperty(&buffer->name, err, o, "name", false);
4557
4558
0
  ParseExtrasAndExtensions(buffer, err, o,
4559
0
                           store_original_json_for_extras_and_extensions);
4560
4561
0
  return true;
4562
0
}
4563
4564
static bool ParseBufferView(
4565
    BufferView *bufferView, std::string *err, const detail::json &o,
4566
0
    bool store_original_json_for_extras_and_extensions) {
4567
0
  int buffer = -1;
4568
0
  if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
4569
0
    return false;
4570
0
  }
4571
4572
0
  size_t byteOffset = 0;
4573
0
  ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
4574
4575
0
  size_t byteLength = 1;
4576
0
  if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4577
0
                             "BufferView")) {
4578
0
    return false;
4579
0
  }
4580
4581
0
  size_t byteStride = 0;
4582
0
  if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
4583
    // Spec says: When byteStride of referenced bufferView is not defined, it
4584
    // means that accessor elements are tightly packed, i.e., effective stride
4585
    // equals the size of the element.
4586
    // We cannot determine the actual byteStride until Accessor are parsed, thus
4587
    // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4588
0
    byteStride = 0;
4589
0
  }
4590
4591
0
  if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4592
0
    if (err) {
4593
0
      std::stringstream ss;
4594
0
      ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4595
0
            "4 : "
4596
0
         << byteStride << std::endl;
4597
4598
0
      (*err) += ss.str();
4599
0
    }
4600
0
    return false;
4601
0
  }
4602
4603
0
  int target = 0;
4604
0
  ParseIntegerProperty(&target, err, o, "target", false);
4605
0
  if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4606
0
      (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
4607
    // OK
4608
0
  } else {
4609
0
    target = 0;
4610
0
  }
4611
0
  bufferView->target = target;
4612
4613
0
  ParseStringProperty(&bufferView->name, err, o, "name", false);
4614
4615
0
  ParseExtrasAndExtensions(bufferView, err, o,
4616
0
                           store_original_json_for_extras_and_extensions);
4617
4618
0
  bufferView->buffer = buffer;
4619
0
  bufferView->byteOffset = byteOffset;
4620
0
  bufferView->byteLength = byteLength;
4621
0
  bufferView->byteStride = byteStride;
4622
0
  return true;
4623
0
}
4624
4625
static bool ParseSparseAccessor(
4626
    Accessor::Sparse *sparse, std::string *err, const detail::json &o,
4627
0
    bool store_original_json_for_extras_and_extensions) {
4628
0
  sparse->isSparse = true;
4629
4630
0
  int count = 0;
4631
0
  if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4632
0
    return false;
4633
0
  }
4634
4635
0
  ParseExtrasAndExtensions(sparse, err, o,
4636
0
                           store_original_json_for_extras_and_extensions);
4637
4638
0
  detail::json_const_iterator indices_iterator;
4639
0
  detail::json_const_iterator values_iterator;
4640
0
  if (!detail::FindMember(o, "indices", indices_iterator)) {
4641
0
    (*err) = "the sparse object of this accessor doesn't have indices";
4642
0
    return false;
4643
0
  }
4644
4645
0
  if (!detail::FindMember(o, "values", values_iterator)) {
4646
0
    (*err) = "the sparse object of this accessor doesn't have values";
4647
0
    return false;
4648
0
  }
4649
4650
0
  const detail::json &indices_obj = detail::GetValue(indices_iterator);
4651
0
  const detail::json &values_obj = detail::GetValue(values_iterator);
4652
4653
0
  int indices_buffer_view = 0, component_type = 0;
4654
0
  size_t indices_byte_offset = 0;
4655
0
  if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4656
0
                            "bufferView", true, "SparseAccessor")) {
4657
0
    return false;
4658
0
  }
4659
0
  ParseUnsignedProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
4660
0
                       false);
4661
0
  if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
4662
0
                            true, "SparseAccessor")) {
4663
0
    return false;
4664
0
  }
4665
4666
0
  int values_buffer_view = 0;
4667
0
  size_t values_byte_offset = 0;
4668
0
  if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
4669
0
                            true, "SparseAccessor")) {
4670
0
    return false;
4671
0
  }
4672
0
  ParseUnsignedProperty(&values_byte_offset, err, values_obj, "byteOffset",
4673
0
                       false);
4674
4675
0
  sparse->count = count;
4676
0
  sparse->indices.bufferView = indices_buffer_view;
4677
0
  sparse->indices.byteOffset = indices_byte_offset;
4678
0
  sparse->indices.componentType = component_type;
4679
0
  ParseExtrasAndExtensions(&sparse->indices, err, indices_obj,
4680
0
                           store_original_json_for_extras_and_extensions);
4681
4682
0
  sparse->values.bufferView = values_buffer_view;
4683
0
  sparse->values.byteOffset = values_byte_offset;
4684
0
  ParseExtrasAndExtensions(&sparse->values, err, values_obj,
4685
0
                           store_original_json_for_extras_and_extensions);
4686
4687
0
  return true;
4688
0
}
4689
4690
static bool ParseAccessor(Accessor *accessor, std::string *err,
4691
                          const detail::json &o,
4692
0
                          bool store_original_json_for_extras_and_extensions) {
4693
0
  int bufferView = -1;
4694
0
  ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
4695
4696
0
  size_t byteOffset = 0;
4697
0
  ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
4698
4699
0
  bool normalized = false;
4700
0
  ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4701
4702
0
  size_t componentType = 0;
4703
0
  if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4704
0
                             "Accessor")) {
4705
0
    return false;
4706
0
  }
4707
4708
0
  size_t count = 0;
4709
0
  if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
4710
0
    return false;
4711
0
  }
4712
4713
0
  std::string type;
4714
0
  if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
4715
0
    return false;
4716
0
  }
4717
4718
0
  if (type.compare("SCALAR") == 0) {
4719
0
    accessor->type = TINYGLTF_TYPE_SCALAR;
4720
0
  } else if (type.compare("VEC2") == 0) {
4721
0
    accessor->type = TINYGLTF_TYPE_VEC2;
4722
0
  } else if (type.compare("VEC3") == 0) {
4723
0
    accessor->type = TINYGLTF_TYPE_VEC3;
4724
0
  } else if (type.compare("VEC4") == 0) {
4725
0
    accessor->type = TINYGLTF_TYPE_VEC4;
4726
0
  } else if (type.compare("MAT2") == 0) {
4727
0
    accessor->type = TINYGLTF_TYPE_MAT2;
4728
0
  } else if (type.compare("MAT3") == 0) {
4729
0
    accessor->type = TINYGLTF_TYPE_MAT3;
4730
0
  } else if (type.compare("MAT4") == 0) {
4731
0
    accessor->type = TINYGLTF_TYPE_MAT4;
4732
0
  } else {
4733
0
    std::stringstream ss;
4734
0
    ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
4735
0
    if (err) {
4736
0
      (*err) += ss.str();
4737
0
    }
4738
0
    return false;
4739
0
  }
4740
4741
0
  ParseStringProperty(&accessor->name, err, o, "name", false);
4742
4743
0
  accessor->minValues.clear();
4744
0
  accessor->maxValues.clear();
4745
0
  ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4746
0
                           "Accessor");
4747
4748
0
  ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4749
0
                           "Accessor");
4750
4751
0
  accessor->count = count;
4752
0
  accessor->bufferView = bufferView;
4753
0
  accessor->byteOffset = byteOffset;
4754
0
  accessor->normalized = normalized;
4755
0
  {
4756
0
    if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4757
0
        componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
4758
      // OK
4759
0
      accessor->componentType = int(componentType);
4760
0
    } else {
4761
0
      std::stringstream ss;
4762
0
      ss << "Invalid `componentType` in accessor. Got " << componentType
4763
0
         << "\n";
4764
0
      if (err) {
4765
0
        (*err) += ss.str();
4766
0
      }
4767
0
      return false;
4768
0
    }
4769
0
  }
4770
4771
0
  ParseExtrasAndExtensions(accessor, err, o,
4772
0
                           store_original_json_for_extras_and_extensions);
4773
4774
  // check if accessor has a "sparse" object:
4775
0
  detail::json_const_iterator iterator;
4776
0
  if (detail::FindMember(o, "sparse", iterator)) {
4777
    // here this accessor has a "sparse" subobject
4778
0
    return ParseSparseAccessor(&accessor->sparse, err,
4779
0
                               detail::GetValue(iterator),
4780
0
                               store_original_json_for_extras_and_extensions);
4781
0
  }
4782
4783
0
  return true;
4784
0
}
4785
4786
#ifdef TINYGLTF_ENABLE_DRACO
4787
4788
static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4789
                              std::vector<uint8_t> &outBuffer) {
4790
  if (componentSize == 4) {
4791
    assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
4792
    memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4793
           outBuffer.size());
4794
  } else {
4795
    size_t faceStride = componentSize * 3;
4796
    for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4797
      const draco::Mesh::Face &face = mesh->face(f);
4798
      if (componentSize == 2) {
4799
        uint16_t indices[3] = {(uint16_t)face[0].value(),
4800
                               (uint16_t)face[1].value(),
4801
                               (uint16_t)face[2].value()};
4802
        memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4803
               faceStride);
4804
      } else {
4805
        uint8_t indices[3] = {(uint8_t)face[0].value(),
4806
                              (uint8_t)face[1].value(),
4807
                              (uint8_t)face[2].value()};
4808
        memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4809
               faceStride);
4810
      }
4811
    }
4812
  }
4813
}
4814
4815
template <typename T>
4816
static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4817
                                     const draco::PointAttribute *pAttribute,
4818
                                     std::vector<uint8_t> &outBuffer) {
4819
  size_t byteOffset = 0;
4820
  T values[4] = {0, 0, 0, 0};
4821
  for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
4822
    const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
4823
    if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4824
                                     values))
4825
      return false;
4826
4827
    memcpy(outBuffer.data() + byteOffset, &values[0],
4828
           sizeof(T) * pAttribute->num_components());
4829
    byteOffset += sizeof(T) * pAttribute->num_components();
4830
  }
4831
4832
  return true;
4833
}
4834
4835
static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4836
                                     const draco::PointAttribute *pAttribute,
4837
                                     std::vector<uint8_t> &outBuffer) {
4838
  bool decodeResult = false;
4839
  switch (componentType) {
4840
    case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4841
      decodeResult =
4842
          GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4843
      break;
4844
    case TINYGLTF_COMPONENT_TYPE_BYTE:
4845
      decodeResult =
4846
          GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4847
      break;
4848
    case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4849
      decodeResult =
4850
          GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4851
      break;
4852
    case TINYGLTF_COMPONENT_TYPE_SHORT:
4853
      decodeResult =
4854
          GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4855
      break;
4856
    case TINYGLTF_COMPONENT_TYPE_INT:
4857
      decodeResult =
4858
          GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4859
      break;
4860
    case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4861
      decodeResult =
4862
          GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4863
      break;
4864
    case TINYGLTF_COMPONENT_TYPE_FLOAT:
4865
      decodeResult =
4866
          GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4867
      break;
4868
    case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4869
      decodeResult =
4870
          GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4871
      break;
4872
    default:
4873
      return false;
4874
  }
4875
4876
  return decodeResult;
4877
}
4878
4879
static bool ParseDracoExtension(Primitive *primitive, Model *model,
4880
                                std::string *err, std::string *warn,
4881
                                const Value &dracoExtensionValue,
4882
                                ParseStrictness strictness) {
4883
  (void)err;
4884
  auto bufferViewValue = dracoExtensionValue.Get("bufferView");
4885
  if (!bufferViewValue.IsInt()) return false;
4886
  auto attributesValue = dracoExtensionValue.Get("attributes");
4887
  if (!attributesValue.IsObject()) return false;
4888
4889
  auto attributesObject = attributesValue.Get<Value::Object>();
4890
  int bufferView = bufferViewValue.Get<int>();
4891
4892
  BufferView &view = model->bufferViews[bufferView];
4893
  Buffer &buffer = model->buffers[view.buffer];
4894
  // BufferView has already been decoded
4895
  if (view.dracoDecoded) return true;
4896
  view.dracoDecoded = true;
4897
4898
  const char *bufferViewData =
4899
      reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
4900
  size_t bufferViewSize = view.byteLength;
4901
4902
  // decode draco
4903
  draco::DecoderBuffer decoderBuffer;
4904
  decoderBuffer.Init(bufferViewData, bufferViewSize);
4905
  draco::Decoder decoder;
4906
  auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4907
  if (!decodeResult.ok()) {
4908
    return false;
4909
  }
4910
  const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
4911
4912
  // create new bufferView for indices
4913
  if (primitive->indices >= 0) {
4914
    if (strictness == ParseStrictness::Permissive) {
4915
      const draco::PointIndex::ValueType numPoint = mesh->num_points();
4916
      // handle the situation where the stored component type does not match the
4917
      // required type for the actual number of stored points
4918
      int supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4919
      if (numPoint < static_cast<draco::PointIndex::ValueType>(
4920
                         std::numeric_limits<uint8_t>::max())) {
4921
        supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4922
      } else if (
4923
          numPoint < static_cast<draco::PointIndex::ValueType>(
4924
                         std::numeric_limits<uint16_t>::max())) {
4925
        supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
4926
      } else {
4927
        supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
4928
      }
4929
4930
      if (supposedComponentType > model->accessors[primitive->indices].componentType) {
4931
        if (warn) {
4932
          (*warn) +=
4933
              "GLTF component type " + std::to_string(model->accessors[primitive->indices].componentType) +
4934
              " is not sufficient for number of stored points,"
4935
              " treating as " + std::to_string(supposedComponentType) + "\n";
4936
        }
4937
        model->accessors[primitive->indices].componentType = supposedComponentType;
4938
      }
4939
    }
4940
4941
    int32_t componentSize = GetComponentSizeInBytes(
4942
        model->accessors[primitive->indices].componentType);
4943
    Buffer decodedIndexBuffer;
4944
    decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4945
4946
    DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4947
4948
    model->buffers.emplace_back(std::move(decodedIndexBuffer));
4949
4950
    BufferView decodedIndexBufferView;
4951
    decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
4952
    decodedIndexBufferView.byteLength =
4953
        int(mesh->num_faces() * 3 * componentSize);
4954
    decodedIndexBufferView.byteOffset = 0;
4955
    decodedIndexBufferView.byteStride = 0;
4956
    decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4957
    model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4958
4959
    model->accessors[primitive->indices].bufferView =
4960
        int(model->bufferViews.size() - 1);
4961
    model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
4962
  }
4963
4964
  for (const auto &attribute : attributesObject) {
4965
    if (!attribute.second.IsInt()) return false;
4966
    auto primitiveAttribute = primitive->attributes.find(attribute.first);
4967
    if (primitiveAttribute == primitive->attributes.end()) return false;
4968
4969
    int dracoAttributeIndex = attribute.second.Get<int>();
4970
    const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
4971
    const auto componentType =
4972
        model->accessors[primitiveAttribute->second].componentType;
4973
4974
    // Create a new buffer for this decoded buffer
4975
    Buffer decodedBuffer;
4976
    size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4977
                        GetComponentSizeInBytes(componentType);
4978
    decodedBuffer.data.resize(bufferSize);
4979
4980
    if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4981
                                  decodedBuffer.data))
4982
      return false;
4983
4984
    model->buffers.emplace_back(std::move(decodedBuffer));
4985
4986
    BufferView decodedBufferView;
4987
    decodedBufferView.buffer = int(model->buffers.size() - 1);
4988
    decodedBufferView.byteLength = bufferSize;
4989
    decodedBufferView.byteOffset = pAttribute->byte_offset();
4990
    decodedBufferView.byteStride = pAttribute->byte_stride();
4991
    decodedBufferView.target = primitive->indices >= 0
4992
                                   ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4993
                                   : TINYGLTF_TARGET_ARRAY_BUFFER;
4994
    model->bufferViews.emplace_back(std::move(decodedBufferView));
4995
4996
    model->accessors[primitiveAttribute->second].bufferView =
4997
        int(model->bufferViews.size() - 1);
4998
    model->accessors[primitiveAttribute->second].count =
4999
        int(mesh->num_points());
5000
  }
5001
5002
  return true;
5003
}
5004
#endif
5005
5006
static bool ParsePrimitive(Primitive *primitive, Model *model,
5007
                           std::string *err, std::string *warn,
5008
                           const detail::json &o,
5009
                           bool store_original_json_for_extras_and_extensions,
5010
4.09k
                           ParseStrictness strictness) {
5011
4.09k
  int material = -1;
5012
4.09k
  ParseIntegerProperty(&material, err, o, "material", false);
5013
4.09k
  primitive->material = material;
5014
5015
4.09k
  int mode = TINYGLTF_MODE_TRIANGLES;
5016
4.09k
  ParseIntegerProperty(&mode, err, o, "mode", false);
5017
4.09k
  primitive->mode = mode;  // Why only triangles were supported ?
5018
5019
4.09k
  int indices = -1;
5020
4.09k
  ParseIntegerProperty(&indices, err, o, "indices", false);
5021
4.09k
  primitive->indices = indices;
5022
4.09k
  if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
5023
4.09k
                                  true, "Primitive")) {
5024
742
    return false;
5025
742
  }
5026
5027
  // Look for morph targets
5028
3.34k
  detail::json_const_iterator targetsObject;
5029
3.34k
  if (detail::FindMember(o, "targets", targetsObject) &&
5030
3.34k
      detail::IsArray(detail::GetValue(targetsObject))) {
5031
728
    auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
5032
728
    for (detail::json_const_array_iterator i =
5033
728
             detail::ArrayBegin(detail::GetValue(targetsObject));
5034
36.9k
         i != targetsObjectEnd; ++i) {
5035
36.2k
      std::map<std::string, int> targetAttribues;
5036
5037
36.2k
      const detail::json &dict = *i;
5038
36.2k
      if (detail::IsObject(dict)) {
5039
36.2k
        detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
5040
36.2k
        detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
5041
5042
76.1k
        for (; dictIt != dictItEnd; ++dictIt) {
5043
39.9k
          int iVal;
5044
39.9k
          if (detail::GetInt(detail::GetValue(dictIt), iVal))
5045
39.1k
            targetAttribues[detail::GetKey(dictIt)] = iVal;
5046
39.9k
        }
5047
36.2k
        primitive->targets.emplace_back(std::move(targetAttribues));
5048
36.2k
      }
5049
36.2k
    }
5050
728
  }
5051
5052
3.34k
  ParseExtrasAndExtensions(primitive, err, o,
5053
3.34k
                           store_original_json_for_extras_and_extensions);
5054
5055
#ifdef TINYGLTF_ENABLE_DRACO
5056
  auto dracoExtension =
5057
      primitive->extensions.find("KHR_draco_mesh_compression");
5058
  if (dracoExtension != primitive->extensions.end()) {
5059
    ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness);
5060
  }
5061
#else
5062
3.34k
  (void)model;
5063
3.34k
  (void)warn;
5064
3.34k
  (void)strictness;
5065
3.34k
#endif
5066
5067
3.34k
  return true;
5068
4.09k
}
5069
5070
static bool ParseMesh(Mesh *mesh, Model *model,
5071
                      std::string *err, std::string *warn,
5072
                      const detail::json &o,
5073
                      bool store_original_json_for_extras_and_extensions,
5074
272k
                      ParseStrictness strictness) {
5075
272k
  ParseStringProperty(&mesh->name, err, o, "name", false);
5076
5077
272k
  mesh->primitives.clear();
5078
272k
  detail::json_const_iterator primObject;
5079
272k
  if (detail::FindMember(o, "primitives", primObject) &&
5080
272k
      detail::IsArray(detail::GetValue(primObject))) {
5081
612
    detail::json_const_array_iterator primEnd =
5082
612
        detail::ArrayEnd(detail::GetValue(primObject));
5083
612
    for (detail::json_const_array_iterator i =
5084
612
             detail::ArrayBegin(detail::GetValue(primObject));
5085
4.70k
         i != primEnd; ++i) {
5086
4.09k
      Primitive primitive;
5087
4.09k
      if (ParsePrimitive(&primitive, model, err, warn, *i,
5088
4.09k
                         store_original_json_for_extras_and_extensions,
5089
4.09k
                         strictness)) {
5090
        // Only add the primitive if the parsing succeeds.
5091
3.34k
        mesh->primitives.emplace_back(std::move(primitive));
5092
3.34k
      }
5093
4.09k
    }
5094
612
  }
5095
5096
  // Should probably check if has targets and if dimensions fit
5097
272k
  ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
5098
5099
272k
  ParseExtrasAndExtensions(mesh, err, o,
5100
272k
                           store_original_json_for_extras_and_extensions);
5101
5102
272k
  return true;
5103
272k
}
5104
5105
static bool ParseNode(Node *node, std::string *err, const detail::json &o,
5106
73.4k
                      bool store_original_json_for_extras_and_extensions) {
5107
73.4k
  ParseStringProperty(&node->name, err, o, "name", false);
5108
5109
73.4k
  int skin = -1;
5110
73.4k
  ParseIntegerProperty(&skin, err, o, "skin", false);
5111
73.4k
  node->skin = skin;
5112
5113
  // Matrix and T/R/S are exclusive
5114
73.4k
  if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
5115
73.4k
    ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
5116
73.4k
    ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
5117
73.4k
    ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
5118
73.4k
  }
5119
5120
73.4k
  int camera = -1;
5121
73.4k
  ParseIntegerProperty(&camera, err, o, "camera", false);
5122
73.4k
  node->camera = camera;
5123
5124
73.4k
  int mesh = -1;
5125
73.4k
  ParseIntegerProperty(&mesh, err, o, "mesh", false);
5126
73.4k
  node->mesh = mesh;
5127
5128
73.4k
  node->children.clear();
5129
73.4k
  ParseIntegerArrayProperty(&node->children, err, o, "children", false);
5130
5131
73.4k
  ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
5132
5133
73.4k
  ParseExtrasAndExtensions(node, err, o,
5134
73.4k
                           store_original_json_for_extras_and_extensions);
5135
5136
  // KHR_lights_punctual: parse light source reference
5137
73.4k
  int light = -1;
5138
73.4k
  if (node->extensions.count("KHR_lights_punctual") != 0) {
5139
1.19k
    auto const &light_ext = node->extensions["KHR_lights_punctual"];
5140
1.19k
    if (light_ext.Has("light")) {
5141
1.19k
      light = light_ext.Get("light").GetNumberAsInt();
5142
1.19k
    } else {
5143
2
      if (err) {
5144
2
        *err +=
5145
2
            "Node has extension KHR_lights_punctual, but does not reference "
5146
2
            "a light source.\n";
5147
2
      }
5148
2
      return false;
5149
2
    }
5150
1.19k
  }
5151
73.4k
  node->light = light;
5152
5153
  // KHR_audio: parse audio source reference
5154
73.4k
  int emitter = -1;
5155
73.4k
  if (node->extensions.count("KHR_audio") != 0) {
5156
810
    auto const &audio_ext = node->extensions["KHR_audio"];
5157
810
    if (audio_ext.Has("emitter")) {
5158
785
      emitter = audio_ext.Get("emitter").GetNumberAsInt();
5159
785
    } else {
5160
25
      if (err) {
5161
25
        *err +=
5162
25
            "Node has extension KHR_audio, but does not reference "
5163
25
            "a audio emitter.\n";
5164
25
      }
5165
25
      return false;
5166
25
    }
5167
810
  }
5168
73.4k
  node->emitter = emitter;
5169
5170
73.4k
  node->lods.clear();
5171
73.4k
  if (node->extensions.count("MSFT_lod") != 0) {
5172
0
    auto const &msft_lod_ext = node->extensions["MSFT_lod"];
5173
0
    if (msft_lod_ext.Has("ids")) {
5174
0
      auto idsArr = msft_lod_ext.Get("ids");
5175
0
      for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5176
0
        node->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5177
0
      }
5178
0
    } else {
5179
0
      if (err) {
5180
0
        *err +=
5181
0
            "Node has extension MSFT_lod, but does not reference "
5182
0
            "other nodes via their ids.\n";
5183
0
      }
5184
0
      return false;
5185
0
    }
5186
0
  }
5187
5188
73.4k
  return true;
5189
73.4k
}
5190
5191
static bool ParseScene(Scene *scene, std::string *err, const detail::json &o,
5192
67.7k
                       bool store_original_json_for_extras_and_extensions) {
5193
67.7k
  ParseStringProperty(&scene->name, err, o, "name", false);
5194
67.7k
  ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false);
5195
5196
67.7k
  ParseExtrasAndExtensions(scene, err, o,
5197
67.7k
                           store_original_json_for_extras_and_extensions);
5198
5199
  // Parse KHR_audio global emitters
5200
67.7k
  if (scene->extensions.count("KHR_audio") != 0) {
5201
0
    auto const &audio_ext = scene->extensions["KHR_audio"];
5202
0
    if (audio_ext.Has("emitters")) {
5203
0
      auto emittersArr = audio_ext.Get("emitters");
5204
0
      for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) {
5205
0
        scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt());
5206
0
      }
5207
0
    } else {
5208
0
      if (err) {
5209
0
        *err +=
5210
0
            "Node has extension KHR_audio, but does not reference "
5211
0
            "a audio emitter.\n";
5212
0
      }
5213
0
      return false;
5214
0
    }
5215
0
  }
5216
5217
67.7k
  return true;
5218
67.7k
}
5219
5220
static bool ParsePbrMetallicRoughness(
5221
    PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
5222
1.72k
    bool store_original_json_for_extras_and_extensions) {
5223
1.72k
  if (pbr == nullptr) {
5224
0
    return false;
5225
0
  }
5226
5227
1.72k
  std::vector<double> baseColorFactor;
5228
1.72k
  if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
5229
1.72k
                               /* required */ false)) {
5230
0
    if (baseColorFactor.size() != 4) {
5231
0
      if (err) {
5232
0
        (*err) +=
5233
0
            "Array length of `baseColorFactor` parameter in "
5234
0
            "pbrMetallicRoughness must be 4, but got " +
5235
0
            std::to_string(baseColorFactor.size()) + "\n";
5236
0
      }
5237
0
      return false;
5238
0
    }
5239
0
    pbr->baseColorFactor = baseColorFactor;
5240
0
  }
5241
5242
1.72k
  {
5243
1.72k
    detail::json_const_iterator it;
5244
1.72k
    if (detail::FindMember(o, "baseColorTexture", it)) {
5245
221
      ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
5246
221
                       store_original_json_for_extras_and_extensions);
5247
221
    }
5248
1.72k
  }
5249
5250
1.72k
  {
5251
1.72k
    detail::json_const_iterator it;
5252
1.72k
    if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
5253
187
      ParseTextureInfo(&pbr->metallicRoughnessTexture, err,
5254
187
                       detail::GetValue(it),
5255
187
                       store_original_json_for_extras_and_extensions);
5256
187
    }
5257
1.72k
  }
5258
5259
1.72k
  ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
5260
1.72k
  ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
5261
5262
1.72k
  ParseExtrasAndExtensions(pbr, err, o,
5263
1.72k
                           store_original_json_for_extras_and_extensions);
5264
5265
1.72k
  return true;
5266
1.72k
}
5267
5268
static bool ParseMaterial(Material *material, std::string *err, std::string *warn,
5269
                          const detail::json &o,
5270
                          bool store_original_json_for_extras_and_extensions,
5271
311k
                          ParseStrictness strictness) {
5272
311k
  ParseStringProperty(&material->name, err, o, "name", /* required */ false);
5273
5274
311k
  if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5275
311k
                               "emissiveFactor",
5276
311k
                               /* required */ false)) {
5277
186
    if (strictness==ParseStrictness::Permissive && material->emissiveFactor.size() == 4) {
5278
0
      if (warn) {
5279
0
        (*warn) +=
5280
0
            "Array length of `emissiveFactor` parameter in "
5281
0
            "material must be 3, but got 4\n";
5282
0
      }
5283
0
      material->emissiveFactor.resize(3);
5284
0
    }
5285
186
    else if (material->emissiveFactor.size() != 3) {
5286
7
      if (err) {
5287
7
        (*err) +=
5288
7
            "Array length of `emissiveFactor` parameter in "
5289
7
            "material must be 3, but got " +
5290
7
            std::to_string(material->emissiveFactor.size()) + "\n";
5291
7
      }
5292
7
      return false;
5293
7
    }
5294
311k
  } else {
5295
    // fill with default values
5296
311k
    material->emissiveFactor = {0.0, 0.0, 0.0};
5297
311k
  }
5298
5299
311k
  ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
5300
311k
                      /* required */ false);
5301
311k
  ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
5302
311k
                      /* required */ false);
5303
311k
  ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
5304
311k
                       /* required */ false);
5305
5306
311k
  {
5307
311k
    detail::json_const_iterator it;
5308
311k
    if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
5309
1.72k
      ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
5310
1.72k
                                detail::GetValue(it),
5311
1.72k
                                store_original_json_for_extras_and_extensions);
5312
1.72k
    }
5313
311k
  }
5314
5315
311k
  {
5316
311k
    detail::json_const_iterator it;
5317
311k
    if (detail::FindMember(o, "normalTexture", it)) {
5318
555
      ParseNormalTextureInfo(&material->normalTexture, err,
5319
555
                             detail::GetValue(it),
5320
555
                             store_original_json_for_extras_and_extensions);
5321
555
    }
5322
311k
  }
5323
5324
311k
  {
5325
311k
    detail::json_const_iterator it;
5326
311k
    if (detail::FindMember(o, "occlusionTexture", it)) {
5327
494
      ParseOcclusionTextureInfo(&material->occlusionTexture, err,
5328
494
                                detail::GetValue(it),
5329
494
                                store_original_json_for_extras_and_extensions);
5330
494
    }
5331
311k
  }
5332
5333
311k
  {
5334
311k
    detail::json_const_iterator it;
5335
311k
    if (detail::FindMember(o, "emissiveTexture", it)) {
5336
325
      ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
5337
325
                       store_original_json_for_extras_and_extensions);
5338
325
    }
5339
311k
  }
5340
5341
  // Old code path. For backward compatibility, we still store material values
5342
  // as Parameter. This will create duplicated information for
5343
  // example(pbrMetallicRoughness), but should be negligible in terms of memory
5344
  // consumption.
5345
  // TODO(syoyo): Remove in the next major release.
5346
311k
  material->values.clear();
5347
311k
  material->additionalValues.clear();
5348
5349
311k
  detail::json_const_iterator it(detail::ObjectBegin(o));
5350
311k
  detail::json_const_iterator itEnd(detail::ObjectEnd(o));
5351
5352
324k
  for (; it != itEnd; ++it) {
5353
12.8k
    std::string key(detail::GetKey(it));
5354
12.8k
    if (key == "pbrMetallicRoughness") {
5355
1.92k
      if (detail::IsObject(detail::GetValue(it))) {
5356
1.46k
        const detail::json &values_object = detail::GetValue(it);
5357
5358
1.46k
        detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5359
1.46k
        detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
5360
5361
8.88k
        for (; itVal != itValEnd; ++itVal) {
5362
7.41k
          Parameter param;
5363
7.41k
          if (ParseParameterProperty(&param, err, values_object,
5364
7.41k
                                     detail::GetKey(itVal), false)) {
5365
7.24k
            material->values.emplace(detail::GetKey(itVal), std::move(param));
5366
7.24k
          }
5367
7.41k
        }
5368
1.46k
      }
5369
10.9k
    } else if (key == "extensions" || key == "extras") {
5370
      // done later, skip, otherwise poorly parsed contents will be saved in the
5371
      // parametermap and serialized again later
5372
10.1k
    } else {
5373
10.1k
      Parameter param;
5374
10.1k
      if (ParseParameterProperty(&param, err, o, key, false)) {
5375
        // names of materials have already been parsed. Putting it in this map
5376
        // doesn't correctly reflect the glTF specification
5377
9.31k
        if (key != "name")
5378
8.50k
          material->additionalValues.emplace(std::move(key), std::move(param));
5379
9.31k
      }
5380
10.1k
    }
5381
12.8k
  }
5382
5383
311k
  material->extensions.clear();  // Note(agnat): Why?
5384
311k
  ParseExtrasAndExtensions(material, err, o,
5385
311k
                           store_original_json_for_extras_and_extensions);
5386
5387
311k
  material->lods.clear();
5388
311k
  if (material->extensions.count("MSFT_lod") != 0) {
5389
0
    auto const &msft_lod_ext = material->extensions["MSFT_lod"];
5390
0
    if (msft_lod_ext.Has("ids")) {
5391
0
      auto idsArr = msft_lod_ext.Get("ids");
5392
0
      for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5393
0
        material->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5394
0
      }
5395
0
    } else {
5396
0
      if (err) {
5397
0
        *err +=
5398
0
            "Material has extension MSFT_lod, but does not reference "
5399
0
            "other materials via their ids.\n";
5400
0
      }
5401
0
      return false;
5402
0
    }
5403
0
  }
5404
5405
311k
  return true;
5406
311k
}
5407
5408
static bool ParseAnimationChannel(
5409
    AnimationChannel *channel, std::string *err, const detail::json &o,
5410
7.11k
    bool store_original_json_for_extras_and_extensions) {
5411
7.11k
  int samplerIndex = -1;
5412
7.11k
  int targetIndex = -1;
5413
7.11k
  if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
5414
7.11k
                            "AnimationChannel")) {
5415
4.88k
    if (err) {
5416
4.88k
      (*err) += "`sampler` field is missing in animation channels\n";
5417
4.88k
    }
5418
4.88k
    return false;
5419
4.88k
  }
5420
5421
2.23k
  detail::json_const_iterator targetIt;
5422
2.23k
  if (detail::FindMember(o, "target", targetIt) &&
5423
2.23k
      detail::IsObject(detail::GetValue(targetIt))) {
5424
160
    const detail::json &target_object = detail::GetValue(targetIt);
5425
5426
160
    ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
5427
5428
160
    if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5429
160
                             true)) {
5430
160
      if (err) {
5431
160
        (*err) += "`path` field is missing in animation.channels.target\n";
5432
160
      }
5433
160
      return false;
5434
160
    }
5435
0
    ParseExtensionsProperty(&channel->target_extensions, err, target_object);
5436
0
    ParseExtrasProperty(&channel->target_extras, target_object);
5437
0
    if (store_original_json_for_extras_and_extensions) {
5438
0
      {
5439
0
        detail::json_const_iterator it;
5440
0
        if (detail::FindMember(target_object, "extensions", it)) {
5441
0
          channel->target_extensions_json_string =
5442
0
              detail::JsonToString(detail::GetValue(it));
5443
0
        }
5444
0
      }
5445
0
      {
5446
0
        detail::json_const_iterator it;
5447
0
        if (detail::FindMember(target_object, "extras", it)) {
5448
0
          channel->target_extras_json_string =
5449
0
              detail::JsonToString(detail::GetValue(it));
5450
0
        }
5451
0
      }
5452
0
    }
5453
0
  }
5454
5455
2.07k
  channel->sampler = samplerIndex;
5456
2.07k
  channel->target_node = targetIndex;
5457
5458
2.07k
  ParseExtrasAndExtensions(channel, err, o,
5459
2.07k
                           store_original_json_for_extras_and_extensions);
5460
5461
2.07k
  return true;
5462
2.23k
}
5463
5464
static bool ParseAnimation(Animation *animation, std::string *err,
5465
                           const detail::json &o,
5466
11.1k
                           bool store_original_json_for_extras_and_extensions) {
5467
11.1k
  {
5468
11.1k
    detail::json_const_iterator channelsIt;
5469
11.1k
    if (detail::FindMember(o, "channels", channelsIt) &&
5470
11.1k
        detail::IsArray(detail::GetValue(channelsIt))) {
5471
5.08k
      detail::json_const_array_iterator channelEnd =
5472
5.08k
          detail::ArrayEnd(detail::GetValue(channelsIt));
5473
5.08k
      for (detail::json_const_array_iterator i =
5474
5.08k
               detail::ArrayBegin(detail::GetValue(channelsIt));
5475
12.2k
           i != channelEnd; ++i) {
5476
7.11k
        AnimationChannel channel;
5477
7.11k
        if (ParseAnimationChannel(
5478
7.11k
                &channel, err, *i,
5479
7.11k
                store_original_json_for_extras_and_extensions)) {
5480
          // Only add the channel if the parsing succeeds.
5481
2.07k
          animation->channels.emplace_back(std::move(channel));
5482
2.07k
        }
5483
7.11k
      }
5484
5.08k
    }
5485
11.1k
  }
5486
5487
11.1k
  {
5488
11.1k
    detail::json_const_iterator samplerIt;
5489
11.1k
    if (detail::FindMember(o, "samplers", samplerIt) &&
5490
11.1k
        detail::IsArray(detail::GetValue(samplerIt))) {
5491
2.00k
      const detail::json &sampler_array = detail::GetValue(samplerIt);
5492
5493
2.00k
      detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5494
2.00k
      detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
5495
5496
2.00k
      for (; it != itEnd; ++it) {
5497
0
        const detail::json &s = *it;
5498
5499
0
        AnimationSampler sampler;
5500
0
        int inputIndex = -1;
5501
0
        int outputIndex = -1;
5502
0
        if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
5503
0
          if (err) {
5504
0
            (*err) += "`input` field is missing in animation.sampler\n";
5505
0
          }
5506
0
          return false;
5507
0
        }
5508
0
        ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5509
0
                            false);
5510
0
        if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
5511
0
          if (err) {
5512
0
            (*err) += "`output` field is missing in animation.sampler\n";
5513
0
          }
5514
0
          return false;
5515
0
        }
5516
0
        sampler.input = inputIndex;
5517
0
        sampler.output = outputIndex;
5518
0
        ParseExtrasAndExtensions(&sampler, err, o,
5519
0
                                 store_original_json_for_extras_and_extensions);
5520
5521
0
        animation->samplers.emplace_back(std::move(sampler));
5522
0
      }
5523
2.00k
    }
5524
11.1k
  }
5525
5526
11.1k
  ParseStringProperty(&animation->name, err, o, "name", false);
5527
5528
11.1k
  ParseExtrasAndExtensions(animation, err, o,
5529
11.1k
                           store_original_json_for_extras_and_extensions);
5530
5531
11.1k
  return true;
5532
11.1k
}
5533
5534
static bool ParseSampler(Sampler *sampler, std::string *err,
5535
                         const detail::json &o,
5536
294k
                         bool store_original_json_for_extras_and_extensions) {
5537
294k
  ParseStringProperty(&sampler->name, err, o, "name", false);
5538
5539
294k
  int minFilter = -1;
5540
294k
  int magFilter = -1;
5541
294k
  int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5542
294k
  int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
5543
  // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
5544
294k
  ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5545
294k
  ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5546
294k
  ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5547
294k
  ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
5548
  // ParseIntegerProperty(&wrapR, err, o, "wrapR", false);  // tinygltf
5549
  // extension
5550
5551
  // TODO(syoyo): Check the value is allowed one.
5552
  // (e.g. we allow 9728(NEAREST), but don't allow 9727)
5553
5554
294k
  sampler->minFilter = minFilter;
5555
294k
  sampler->magFilter = magFilter;
5556
294k
  sampler->wrapS = wrapS;
5557
294k
  sampler->wrapT = wrapT;
5558
  // sampler->wrapR = wrapR;
5559
5560
294k
  ParseExtrasAndExtensions(sampler, err, o,
5561
294k
                           store_original_json_for_extras_and_extensions);
5562
5563
294k
  return true;
5564
294k
}
5565
5566
static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o,
5567
0
                      bool store_original_json_for_extras_and_extensions) {
5568
0
  ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
5569
5570
0
  std::vector<int> joints;
5571
0
  if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
5572
0
    return false;
5573
0
  }
5574
0
  skin->joints = std::move(joints);
5575
5576
0
  int skeleton = -1;
5577
0
  ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5578
0
  skin->skeleton = skeleton;
5579
5580
0
  int invBind = -1;
5581
0
  ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5582
0
  skin->inverseBindMatrices = invBind;
5583
5584
0
  ParseExtrasAndExtensions(skin, err, o,
5585
0
                           store_original_json_for_extras_and_extensions);
5586
5587
0
  return true;
5588
0
}
5589
5590
static bool ParsePerspectiveCamera(
5591
    PerspectiveCamera *camera, std::string *err, const detail::json &o,
5592
0
    bool store_original_json_for_extras_and_extensions) {
5593
0
  double yfov = 0.0;
5594
0
  if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5595
0
    return false;
5596
0
  }
5597
5598
0
  double znear = 0.0;
5599
0
  if (!ParseNumberProperty(&znear, err, o, "znear", true,
5600
0
                           "PerspectiveCamera")) {
5601
0
    return false;
5602
0
  }
5603
5604
0
  double aspectRatio = 0.0;  // = invalid
5605
0
  ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5606
0
                      "PerspectiveCamera");
5607
5608
0
  double zfar = 0.0;  // = invalid
5609
0
  ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5610
5611
0
  camera->aspectRatio = aspectRatio;
5612
0
  camera->zfar = zfar;
5613
0
  camera->yfov = yfov;
5614
0
  camera->znear = znear;
5615
5616
0
  ParseExtrasAndExtensions(camera, err, o,
5617
0
                           store_original_json_for_extras_and_extensions);
5618
5619
  // TODO(syoyo): Validate parameter values.
5620
5621
0
  return true;
5622
0
}
5623
5624
static bool ParseSpotLight(SpotLight *light, std::string *err,
5625
                           const detail::json &o,
5626
0
                           bool store_original_json_for_extras_and_extensions) {
5627
0
  ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5628
0
  ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
5629
5630
0
  ParseExtrasAndExtensions(light, err, o,
5631
0
                           store_original_json_for_extras_and_extensions);
5632
5633
  // TODO(syoyo): Validate parameter values.
5634
5635
0
  return true;
5636
0
}
5637
5638
static bool ParseOrthographicCamera(
5639
    OrthographicCamera *camera, std::string *err, const detail::json &o,
5640
0
    bool store_original_json_for_extras_and_extensions) {
5641
0
  double xmag = 0.0;
5642
0
  if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5643
0
    return false;
5644
0
  }
5645
5646
0
  double ymag = 0.0;
5647
0
  if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5648
0
    return false;
5649
0
  }
5650
5651
0
  double zfar = 0.0;
5652
0
  if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5653
0
    return false;
5654
0
  }
5655
5656
0
  double znear = 0.0;
5657
0
  if (!ParseNumberProperty(&znear, err, o, "znear", true,
5658
0
                           "OrthographicCamera")) {
5659
0
    return false;
5660
0
  }
5661
5662
0
  ParseExtrasAndExtensions(camera, err, o,
5663
0
                           store_original_json_for_extras_and_extensions);
5664
5665
0
  camera->xmag = xmag;
5666
0
  camera->ymag = ymag;
5667
0
  camera->zfar = zfar;
5668
0
  camera->znear = znear;
5669
5670
  // TODO(syoyo): Validate parameter values.
5671
5672
0
  return true;
5673
0
}
5674
5675
static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o,
5676
28
                        bool store_original_json_for_extras_and_extensions) {
5677
28
  if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5678
0
    return false;
5679
0
  }
5680
5681
28
  if (camera->type.compare("orthographic") == 0) {
5682
0
    detail::json_const_iterator orthoIt;
5683
0
    if (!detail::FindMember(o, "orthographic", orthoIt)) {
5684
0
      if (err) {
5685
0
        std::stringstream ss;
5686
0
        ss << "Orthographic camera description not found." << std::endl;
5687
0
        (*err) += ss.str();
5688
0
      }
5689
0
      return false;
5690
0
    }
5691
5692
0
    const detail::json &v = detail::GetValue(orthoIt);
5693
0
    if (!detail::IsObject(v)) {
5694
0
      if (err) {
5695
0
        std::stringstream ss;
5696
0
        ss << "\"orthographic\" is not a JSON object." << std::endl;
5697
0
        (*err) += ss.str();
5698
0
      }
5699
0
      return false;
5700
0
    }
5701
5702
0
    if (!ParseOrthographicCamera(
5703
0
            &camera->orthographic, err, v,
5704
0
            store_original_json_for_extras_and_extensions)) {
5705
0
      return false;
5706
0
    }
5707
28
  } else if (camera->type.compare("perspective") == 0) {
5708
0
    detail::json_const_iterator perspIt;
5709
0
    if (!detail::FindMember(o, "perspective", perspIt)) {
5710
0
      if (err) {
5711
0
        std::stringstream ss;
5712
0
        ss << "Perspective camera description not found." << std::endl;
5713
0
        (*err) += ss.str();
5714
0
      }
5715
0
      return false;
5716
0
    }
5717
5718
0
    const detail::json &v = detail::GetValue(perspIt);
5719
0
    if (!detail::IsObject(v)) {
5720
0
      if (err) {
5721
0
        std::stringstream ss;
5722
0
        ss << "\"perspective\" is not a JSON object." << std::endl;
5723
0
        (*err) += ss.str();
5724
0
      }
5725
0
      return false;
5726
0
    }
5727
5728
0
    if (!ParsePerspectiveCamera(
5729
0
            &camera->perspective, err, v,
5730
0
            store_original_json_for_extras_and_extensions)) {
5731
0
      return false;
5732
0
    }
5733
28
  } else {
5734
28
    if (err) {
5735
28
      std::stringstream ss;
5736
28
      ss << "Invalid camera type: \"" << camera->type
5737
28
         << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5738
28
      (*err) += ss.str();
5739
28
    }
5740
28
    return false;
5741
28
  }
5742
5743
0
  ParseStringProperty(&camera->name, err, o, "name", false);
5744
5745
0
  ParseExtrasAndExtensions(camera, err, o,
5746
0
                           store_original_json_for_extras_and_extensions);
5747
5748
0
  return true;
5749
28
}
5750
5751
static bool ParseLight(Light *light, std::string *err, const detail::json &o,
5752
0
                       bool store_original_json_for_extras_and_extensions) {
5753
0
  if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5754
0
    return false;
5755
0
  }
5756
5757
0
  if (light->type == "spot") {
5758
0
    detail::json_const_iterator spotIt;
5759
0
    if (!detail::FindMember(o, "spot", spotIt)) {
5760
0
      if (err) {
5761
0
        std::stringstream ss;
5762
0
        ss << "Spot light description not found." << std::endl;
5763
0
        (*err) += ss.str();
5764
0
      }
5765
0
      return false;
5766
0
    }
5767
5768
0
    const detail::json &v = detail::GetValue(spotIt);
5769
0
    if (!detail::IsObject(v)) {
5770
0
      if (err) {
5771
0
        std::stringstream ss;
5772
0
        ss << "\"spot\" is not a JSON object." << std::endl;
5773
0
        (*err) += ss.str();
5774
0
      }
5775
0
      return false;
5776
0
    }
5777
5778
0
    if (!ParseSpotLight(&light->spot, err, v,
5779
0
                        store_original_json_for_extras_and_extensions)) {
5780
0
      return false;
5781
0
    }
5782
0
  }
5783
5784
0
  ParseStringProperty(&light->name, err, o, "name", false);
5785
0
  ParseNumberArrayProperty(&light->color, err, o, "color", false);
5786
0
  ParseNumberProperty(&light->range, err, o, "range", false);
5787
0
  ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5788
5789
0
  ParseExtrasAndExtensions(light, err, o,
5790
0
                           store_original_json_for_extras_and_extensions);
5791
5792
0
  return true;
5793
0
}
5794
5795
static bool ParsePositionalEmitter(
5796
    PositionalEmitter *positional, std::string *err, const detail::json &o,
5797
0
    bool store_original_json_for_extras_and_extensions) {
5798
0
  ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle",
5799
0
                      false);
5800
0
  ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle",
5801
0
                      false);
5802
0
  ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain",
5803
0
                      false);
5804
0
  ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false);
5805
0
  ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false);
5806
0
  ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor",
5807
0
                      false);
5808
5809
0
  ParseExtrasAndExtensions(positional, err, o,
5810
0
                           store_original_json_for_extras_and_extensions);
5811
5812
0
  return true;
5813
0
}
5814
5815
static bool ParseAudioEmitter(
5816
    AudioEmitter *emitter, std::string *err, const detail::json &o,
5817
0
    bool store_original_json_for_extras_and_extensions) {
5818
0
  if (!ParseStringProperty(&emitter->type, err, o, "type", true)) {
5819
0
    return false;
5820
0
  }
5821
5822
0
  if (emitter->type == "positional") {
5823
0
    detail::json_const_iterator positionalIt;
5824
0
    if (!detail::FindMember(o, "positional", positionalIt)) {
5825
0
      if (err) {
5826
0
        std::stringstream ss;
5827
0
        ss << "Positional emitter description not found." << std::endl;
5828
0
        (*err) += ss.str();
5829
0
      }
5830
0
      return false;
5831
0
    }
5832
5833
0
    const detail::json &v = detail::GetValue(positionalIt);
5834
0
    if (!detail::IsObject(v)) {
5835
0
      if (err) {
5836
0
        std::stringstream ss;
5837
0
        ss << "\"positional\" is not a JSON object." << std::endl;
5838
0
        (*err) += ss.str();
5839
0
      }
5840
0
      return false;
5841
0
    }
5842
5843
0
    if (!ParsePositionalEmitter(
5844
0
            &emitter->positional, err, v,
5845
0
            store_original_json_for_extras_and_extensions)) {
5846
0
      return false;
5847
0
    }
5848
0
  }
5849
5850
0
  ParseStringProperty(&emitter->name, err, o, "name", false);
5851
0
  ParseNumberProperty(&emitter->gain, err, o, "gain", false);
5852
0
  ParseBooleanProperty(&emitter->loop, err, o, "loop", false);
5853
0
  ParseBooleanProperty(&emitter->playing, err, o, "playing", false);
5854
0
  ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false);
5855
0
  ParseIntegerProperty(&emitter->source, err, o, "source", true);
5856
5857
0
  ParseExtrasAndExtensions(emitter, err, o,
5858
0
                           store_original_json_for_extras_and_extensions);
5859
5860
0
  return true;
5861
0
}
5862
5863
static bool ParseAudioSource(
5864
    AudioSource *source, std::string *err, const detail::json &o,
5865
769k
    bool store_original_json_for_extras_and_extensions) {
5866
769k
  ParseStringProperty(&source->name, err, o, "name", false);
5867
769k
  ParseStringProperty(&source->uri, err, o, "uri", false);
5868
5869
769k
  if (source->uri.empty()) {
5870
768k
    ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true);
5871
768k
    ParseStringProperty(&source->mimeType, err, o, "mimeType", true);
5872
768k
  }
5873
5874
769k
  ParseExtrasAndExtensions(source, err, o,
5875
769k
                           store_original_json_for_extras_and_extensions);
5876
5877
769k
  return true;
5878
769k
}
5879
5880
namespace detail {
5881
5882
template <typename Callback>
5883
21.7k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
21.7k
  detail::json_const_iterator itm;
5885
21.7k
  if (detail::FindMember(_v, member, itm) &&
5886
21.7k
      detail::IsArray(detail::GetValue(itm))) {
5887
1.39k
    const detail::json &root = detail::GetValue(itm);
5888
1.39k
    auto it = detail::ArrayBegin(root);
5889
1.39k
    auto end = detail::ArrayEnd(root);
5890
1.50M
    for (; it != end; ++it) {
5891
1.50M
      if (!cb(*it)) return false;
5892
1.50M
    }
5893
1.39k
  }
5894
21.3k
  return true;
5895
21.7k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_1>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_1&&)
Line
Count
Source
5883
1.59k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.59k
  detail::json_const_iterator itm;
5885
1.59k
  if (detail::FindMember(_v, member, itm) &&
5886
1.59k
      detail::IsArray(detail::GetValue(itm))) {
5887
10
    const detail::json &root = detail::GetValue(itm);
5888
10
    auto it = detail::ArrayBegin(root);
5889
10
    auto end = detail::ArrayEnd(root);
5890
261k
    for (; it != end; ++it) {
5891
261k
      if (!cb(*it)) return false;
5892
261k
    }
5893
10
  }
5894
1.59k
  return true;
5895
1.59k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_2>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_2&&)
Line
Count
Source
5883
1.59k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.59k
  detail::json_const_iterator itm;
5885
1.59k
  if (detail::FindMember(_v, member, itm) &&
5886
1.59k
      detail::IsArray(detail::GetValue(itm))) {
5887
3
    const detail::json &root = detail::GetValue(itm);
5888
3
    auto it = detail::ArrayBegin(root);
5889
3
    auto end = detail::ArrayEnd(root);
5890
18.1k
    for (; it != end; ++it) {
5891
18.1k
      if (!cb(*it)) return false;
5892
18.1k
    }
5893
3
  }
5894
1.59k
  return true;
5895
1.59k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_3>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_3&&)
Line
Count
Source
5883
1.59k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.59k
  detail::json_const_iterator itm;
5885
1.59k
  if (detail::FindMember(_v, member, itm) &&
5886
1.59k
      detail::IsArray(detail::GetValue(itm))) {
5887
0
    const detail::json &root = detail::GetValue(itm);
5888
0
    auto it = detail::ArrayBegin(root);
5889
0
    auto end = detail::ArrayEnd(root);
5890
0
    for (; it != end; ++it) {
5891
0
      if (!cb(*it)) return false;
5892
0
    }
5893
0
  }
5894
1.59k
  return true;
5895
1.59k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_4>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_4&&)
Line
Count
Source
5883
1.59k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.59k
  detail::json_const_iterator itm;
5885
1.59k
  if (detail::FindMember(_v, member, itm) &&
5886
1.59k
      detail::IsArray(detail::GetValue(itm))) {
5887
0
    const detail::json &root = detail::GetValue(itm);
5888
0
    auto it = detail::ArrayBegin(root);
5889
0
    auto end = detail::ArrayEnd(root);
5890
0
    for (; it != end; ++it) {
5891
0
      if (!cb(*it)) return false;
5892
0
    }
5893
0
  }
5894
1.59k
  return true;
5895
1.59k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_5>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_5&&)
Line
Count
Source
5883
1.59k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.59k
  detail::json_const_iterator itm;
5885
1.59k
  if (detail::FindMember(_v, member, itm) &&
5886
1.59k
      detail::IsArray(detail::GetValue(itm))) {
5887
0
    const detail::json &root = detail::GetValue(itm);
5888
0
    auto it = detail::ArrayBegin(root);
5889
0
    auto end = detail::ArrayEnd(root);
5890
0
    for (; it != end; ++it) {
5891
0
      if (!cb(*it)) return false;
5892
0
    }
5893
0
  }
5894
1.59k
  return true;
5895
1.59k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_6>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_6&&)
Line
Count
Source
5883
1.59k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.59k
  detail::json_const_iterator itm;
5885
1.59k
  if (detail::FindMember(_v, member, itm) &&
5886
1.59k
      detail::IsArray(detail::GetValue(itm))) {
5887
182
    const detail::json &root = detail::GetValue(itm);
5888
182
    auto it = detail::ArrayBegin(root);
5889
182
    auto end = detail::ArrayEnd(root);
5890
272k
    for (; it != end; ++it) {
5891
272k
      if (!cb(*it)) return false;
5892
272k
    }
5893
182
  }
5894
1.59k
  return true;
5895
1.59k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_7>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_7&&)
Line
Count
Source
5883
1.59k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.59k
  detail::json_const_iterator itm;
5885
1.59k
  if (detail::FindMember(_v, member, itm) &&
5886
1.59k
      detail::IsArray(detail::GetValue(itm))) {
5887
123
    const detail::json &root = detail::GetValue(itm);
5888
123
    auto it = detail::ArrayBegin(root);
5889
123
    auto end = detail::ArrayEnd(root);
5890
73.5k
    for (; it != end; ++it) {
5891
73.4k
      if (!cb(*it)) return false;
5892
73.4k
    }
5893
123
  }
5894
1.57k
  return true;
5895
1.59k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_8>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_8&&)
Line
Count
Source
5883
1.57k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.57k
  detail::json_const_iterator itm;
5885
1.57k
  if (detail::FindMember(_v, member, itm) &&
5886
1.57k
      detail::IsArray(detail::GetValue(itm))) {
5887
34
    const detail::json &root = detail::GetValue(itm);
5888
34
    auto it = detail::ArrayBegin(root);
5889
34
    auto end = detail::ArrayEnd(root);
5890
67.8k
    for (; it != end; ++it) {
5891
67.7k
      if (!cb(*it)) return false;
5892
67.7k
    }
5893
34
  }
5894
1.57k
  return true;
5895
1.57k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_9>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_9&&)
Line
Count
Source
5883
1.57k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.57k
  detail::json_const_iterator itm;
5885
1.57k
  if (detail::FindMember(_v, member, itm) &&
5886
1.57k
      detail::IsArray(detail::GetValue(itm))) {
5887
284
    const detail::json &root = detail::GetValue(itm);
5888
284
    auto it = detail::ArrayBegin(root);
5889
284
    auto end = detail::ArrayEnd(root);
5890
312k
    for (; it != end; ++it) {
5891
311k
      if (!cb(*it)) return false;
5892
311k
    }
5893
284
  }
5894
1.56k
  return true;
5895
1.57k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_10>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_10&&)
Line
Count
Source
5883
1.56k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.56k
  detail::json_const_iterator itm;
5885
1.56k
  if (detail::FindMember(_v, member, itm) &&
5886
1.56k
      detail::IsArray(detail::GetValue(itm))) {
5887
650
    const detail::json &root = detail::GetValue(itm);
5888
650
    auto it = detail::ArrayBegin(root);
5889
650
    auto end = detail::ArrayEnd(root);
5890
61.4k
    for (; it != end; ++it) {
5891
61.2k
      if (!cb(*it)) return false;
5892
61.2k
    }
5893
650
  }
5894
1.17k
  return true;
5895
1.56k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_11>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_11&&)
Line
Count
Source
5883
1.17k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.17k
  detail::json_const_iterator itm;
5885
1.17k
  if (detail::FindMember(_v, member, itm) &&
5886
1.17k
      detail::IsArray(detail::GetValue(itm))) {
5887
17
    const detail::json &root = detail::GetValue(itm);
5888
17
    auto it = detail::ArrayBegin(root);
5889
17
    auto end = detail::ArrayEnd(root);
5890
135k
    for (; it != end; ++it) {
5891
135k
      if (!cb(*it)) return false;
5892
135k
    }
5893
17
  }
5894
1.17k
  return true;
5895
1.17k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_12>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_12&&)
Line
Count
Source
5883
1.17k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.17k
  detail::json_const_iterator itm;
5885
1.17k
  if (detail::FindMember(_v, member, itm) &&
5886
1.17k
      detail::IsArray(detail::GetValue(itm))) {
5887
45
    const detail::json &root = detail::GetValue(itm);
5888
45
    auto it = detail::ArrayBegin(root);
5889
45
    auto end = detail::ArrayEnd(root);
5890
11.1k
    for (; it != end; ++it) {
5891
11.1k
      if (!cb(*it)) return false;
5892
11.1k
    }
5893
45
  }
5894
1.17k
  return true;
5895
1.17k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_13>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_13&&)
Line
Count
Source
5883
1.17k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.17k
  detail::json_const_iterator itm;
5885
1.17k
  if (detail::FindMember(_v, member, itm) &&
5886
1.17k
      detail::IsArray(detail::GetValue(itm))) {
5887
0
    const detail::json &root = detail::GetValue(itm);
5888
0
    auto it = detail::ArrayBegin(root);
5889
0
    auto end = detail::ArrayEnd(root);
5890
0
    for (; it != end; ++it) {
5891
0
      if (!cb(*it)) return false;
5892
0
    }
5893
0
  }
5894
1.17k
  return true;
5895
1.17k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_14>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_14&&)
Line
Count
Source
5883
1.17k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.17k
  detail::json_const_iterator itm;
5885
1.17k
  if (detail::FindMember(_v, member, itm) &&
5886
1.17k
      detail::IsArray(detail::GetValue(itm))) {
5887
18
    const detail::json &root = detail::GetValue(itm);
5888
18
    auto it = detail::ArrayBegin(root);
5889
18
    auto end = detail::ArrayEnd(root);
5890
294k
    for (; it != end; ++it) {
5891
294k
      if (!cb(*it)) return false;
5892
294k
    }
5893
18
  }
5894
1.17k
  return true;
5895
1.17k
};
fuzz_gltf.cc:bool tinygltf::detail::ForEachInArray<tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_15>(nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, char const*, tinygltf::TinyGLTF::LoadFromString(tinygltf::Model*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned int)::$_15&&)
Line
Count
Source
5883
1.17k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5884
1.17k
  detail::json_const_iterator itm;
5885
1.17k
  if (detail::FindMember(_v, member, itm) &&
5886
1.17k
      detail::IsArray(detail::GetValue(itm))) {
5887
28
    const detail::json &root = detail::GetValue(itm);
5888
28
    auto it = detail::ArrayBegin(root);
5889
28
    auto end = detail::ArrayEnd(root);
5890
28
    for (; it != end; ++it) {
5891
28
      if (!cb(*it)) return false;
5892
28
    }
5893
28
  }
5894
1.14k
  return true;
5895
1.17k
};
5896
5897
}  // end of namespace detail
5898
5899
bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5900
                              const char *json_str,
5901
                              unsigned int json_str_length,
5902
                              const std::string &base_dir,
5903
1.82k
                              unsigned int check_sections) {
5904
1.82k
  if (json_str_length < 4) {
5905
0
    if (err) {
5906
0
      (*err) = "JSON string too short.\n";
5907
0
    }
5908
0
    return false;
5909
0
  }
5910
5911
1.82k
  detail::JsonDocument v;
5912
5913
1.82k
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5914
1.82k
     defined(_CPPUNWIND)) &&                               \
5915
1.82k
    !defined(TINYGLTF_NOEXCEPTION)
5916
1.82k
  try {
5917
1.82k
    detail::JsonParse(v, json_str, json_str_length, true);
5918
5919
1.82k
  } catch (const std::exception &e) {
5920
214
    if (err) {
5921
214
      (*err) = e.what();
5922
214
    }
5923
214
    return false;
5924
214
  }
5925
#else
5926
  {
5927
    detail::JsonParse(v, json_str, json_str_length);
5928
5929
    if (!detail::IsObject(v)) {
5930
      // Assume parsing was failed.
5931
      if (err) {
5932
        (*err) = "Failed to parse JSON object\n";
5933
      }
5934
      return false;
5935
    }
5936
  }
5937
#endif
5938
5939
1.60k
  if (!detail::IsObject(v)) {
5940
    // root is not an object.
5941
0
    if (err) {
5942
0
      (*err) = "Root element is not a JSON object\n";
5943
0
    }
5944
0
    return false;
5945
0
  }
5946
5947
1.60k
  {
5948
1.60k
    bool version_found = false;
5949
1.60k
    detail::json_const_iterator it;
5950
1.60k
    if (detail::FindMember(v, "asset", it) &&
5951
1.60k
        detail::IsObject(detail::GetValue(it))) {
5952
1.60k
      auto &itObj = detail::GetValue(it);
5953
1.60k
      detail::json_const_iterator version_it;
5954
1.60k
      std::string versionStr;
5955
1.60k
      if (detail::FindMember(itObj, "version", version_it) &&
5956
1.60k
          detail::GetString(detail::GetValue(version_it), versionStr)) {
5957
1.59k
        version_found = true;
5958
1.59k
      }
5959
1.60k
    }
5960
1.60k
    if (version_found) {
5961
      // OK
5962
1.59k
    } else if (check_sections & REQUIRE_VERSION) {
5963
7
      if (err) {
5964
7
        (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5965
7
      }
5966
7
      return false;
5967
7
    }
5968
1.60k
  }
5969
5970
  // scene is not mandatory.
5971
  // FIXME Maybe a better way to handle it than removing the code
5972
5973
1.59k
  auto IsArrayMemberPresent = [](const detail::json &_v,
5974
1.59k
                                 const char *name) -> bool {
5975
0
    detail::json_const_iterator it;
5976
0
    return detail::FindMember(_v, name, it) &&
5977
0
           detail::IsArray(detail::GetValue(it));
5978
0
  };
5979
5980
1.59k
  {
5981
1.59k
    if ((check_sections & REQUIRE_SCENES) &&
5982
1.59k
        !IsArrayMemberPresent(v, "scenes")) {
5983
0
      if (err) {
5984
0
        (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5985
0
      }
5986
0
      return false;
5987
0
    }
5988
1.59k
  }
5989
5990
1.59k
  {
5991
1.59k
    if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
5992
0
      if (err) {
5993
0
        (*err) += "\"nodes\" object not found in .gltf\n";
5994
0
      }
5995
0
      return false;
5996
0
    }
5997
1.59k
  }
5998
5999
1.59k
  {
6000
1.59k
    if ((check_sections & REQUIRE_ACCESSORS) &&
6001
1.59k
        !IsArrayMemberPresent(v, "accessors")) {
6002
0
      if (err) {
6003
0
        (*err) += "\"accessors\" object not found in .gltf\n";
6004
0
      }
6005
0
      return false;
6006
0
    }
6007
1.59k
  }
6008
6009
1.59k
  {
6010
1.59k
    if ((check_sections & REQUIRE_BUFFERS) &&
6011
1.59k
        !IsArrayMemberPresent(v, "buffers")) {
6012
0
      if (err) {
6013
0
        (*err) += "\"buffers\" object not found in .gltf\n";
6014
0
      }
6015
0
      return false;
6016
0
    }
6017
1.59k
  }
6018
6019
1.59k
  {
6020
1.59k
    if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
6021
1.59k
        !IsArrayMemberPresent(v, "bufferViews")) {
6022
0
      if (err) {
6023
0
        (*err) += "\"bufferViews\" object not found in .gltf\n";
6024
0
      }
6025
0
      return false;
6026
0
    }
6027
1.59k
  }
6028
6029
1.59k
  model->buffers.clear();
6030
1.59k
  model->bufferViews.clear();
6031
1.59k
  model->accessors.clear();
6032
1.59k
  model->meshes.clear();
6033
1.59k
  model->cameras.clear();
6034
1.59k
  model->nodes.clear();
6035
1.59k
  model->extensionsUsed.clear();
6036
1.59k
  model->extensionsRequired.clear();
6037
1.59k
  model->extensions.clear();
6038
1.59k
  model->defaultScene = -1;
6039
6040
  // 1. Parse Asset
6041
1.59k
  {
6042
1.59k
    detail::json_const_iterator it;
6043
1.59k
    if (detail::FindMember(v, "asset", it) &&
6044
1.59k
        detail::IsObject(detail::GetValue(it))) {
6045
1.59k
      const detail::json &root = detail::GetValue(it);
6046
6047
1.59k
      ParseAsset(&model->asset, err, root,
6048
1.59k
                 store_original_json_for_extras_and_extensions_);
6049
1.59k
    }
6050
1.59k
  }
6051
6052
1.59k
  using detail::ForEachInArray;
6053
6054
  // 2. Parse extensionUsed
6055
1.59k
  {
6056
261k
    ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
6057
261k
      std::string str;
6058
261k
      detail::GetString(o, str);
6059
261k
      model->extensionsUsed.emplace_back(std::move(str));
6060
261k
      return true;
6061
261k
    });
6062
1.59k
  }
6063
6064
1.59k
  {
6065
18.1k
    ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
6066
18.1k
      std::string str;
6067
18.1k
      detail::GetString(o, str);
6068
18.1k
      model->extensionsRequired.emplace_back(std::move(str));
6069
18.1k
      return true;
6070
18.1k
    });
6071
1.59k
  }
6072
6073
  // 3. Parse Buffer
6074
1.59k
  {
6075
1.59k
    bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) {
6076
0
      if (!detail::IsObject(o)) {
6077
0
        if (err) {
6078
0
          (*err) += "`buffers' does not contain an JSON object.";
6079
0
        }
6080
0
        return false;
6081
0
      }
6082
0
      Buffer buffer;
6083
0
      if (!ParseBuffer(&buffer, err, o,
6084
0
                       store_original_json_for_extras_and_extensions_, &fs,
6085
0
                       &uri_cb, base_dir, max_external_file_size_, is_binary_,
6086
0
                       bin_data_, bin_size_)) {
6087
0
        return false;
6088
0
      }
6089
6090
0
      model->buffers.emplace_back(std::move(buffer));
6091
0
      return true;
6092
0
    });
6093
6094
1.59k
    if (!success) {
6095
0
      return false;
6096
0
    }
6097
1.59k
  }
6098
  // 4. Parse BufferView
6099
1.59k
  {
6100
1.59k
    bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) {
6101
0
      if (!detail::IsObject(o)) {
6102
0
        if (err) {
6103
0
          (*err) += "`bufferViews' does not contain an JSON object.";
6104
0
        }
6105
0
        return false;
6106
0
      }
6107
0
      BufferView bufferView;
6108
0
      if (!ParseBufferView(&bufferView, err, o,
6109
0
                           store_original_json_for_extras_and_extensions_)) {
6110
0
        return false;
6111
0
      }
6112
6113
0
      model->bufferViews.emplace_back(std::move(bufferView));
6114
0
      return true;
6115
0
    });
6116
6117
1.59k
    if (!success) {
6118
0
      return false;
6119
0
    }
6120
1.59k
  }
6121
6122
  // 5. Parse Accessor
6123
1.59k
  {
6124
1.59k
    bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) {
6125
0
      if (!detail::IsObject(o)) {
6126
0
        if (err) {
6127
0
          (*err) += "`accessors' does not contain an JSON object.";
6128
0
        }
6129
0
        return false;
6130
0
      }
6131
0
      Accessor accessor;
6132
0
      if (!ParseAccessor(&accessor, err, o,
6133
0
                         store_original_json_for_extras_and_extensions_)) {
6134
0
        return false;
6135
0
      }
6136
6137
0
      model->accessors.emplace_back(std::move(accessor));
6138
0
      return true;
6139
0
    });
6140
6141
1.59k
    if (!success) {
6142
0
      return false;
6143
0
    }
6144
1.59k
  }
6145
6146
  // 6. Parse Mesh
6147
1.59k
  {
6148
272k
    bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
6149
272k
      if (!detail::IsObject(o)) {
6150
0
        if (err) {
6151
0
          (*err) += "`meshes' does not contain an JSON object.";
6152
0
        }
6153
0
        return false;
6154
0
      }
6155
272k
      Mesh mesh;
6156
272k
      if (!ParseMesh(&mesh, model, err, warn, o,
6157
272k
                     store_original_json_for_extras_and_extensions_,
6158
272k
                     strictness_)) {
6159
0
        return false;
6160
0
      }
6161
6162
272k
      model->meshes.emplace_back(std::move(mesh));
6163
272k
      return true;
6164
272k
    });
6165
6166
1.59k
    if (!success) {
6167
0
      return false;
6168
0
    }
6169
1.59k
  }
6170
6171
  // Assign missing bufferView target types
6172
  // - Look for missing Mesh indices
6173
  // - Look for missing Mesh attributes
6174
272k
  for (auto &mesh : model->meshes) {
6175
272k
    for (auto &primitive : mesh.primitives) {
6176
3.34k
      if (primitive.indices >
6177
3.34k
          -1)  // has indices from parsing step, must be Element Array Buffer
6178
0
      {
6179
0
        if (size_t(primitive.indices) >= model->accessors.size()) {
6180
0
          if (err) {
6181
0
            (*err) += "primitive indices accessor out of bounds";
6182
0
          }
6183
0
          return false;
6184
0
        }
6185
6186
0
        const auto bufferView =
6187
0
            model->accessors[size_t(primitive.indices)].bufferView;
6188
0
        if (bufferView < 0) {
6189
          // skip, bufferView could be null(-1) for certain extensions
6190
0
        } else if (size_t(bufferView) >= model->bufferViews.size()) {
6191
0
          if (err) {
6192
0
            (*err) += "accessor[" + std::to_string(primitive.indices) +
6193
0
                      "] invalid bufferView";
6194
0
          }
6195
0
          return false;
6196
0
        } else {
6197
0
          model->bufferViews[size_t(bufferView)].target =
6198
0
              TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
6199
          // we could optionally check if accessors' bufferView type is Scalar, as
6200
          // it should be
6201
0
        }
6202
0
      }
6203
6204
3.96k
      for (auto &attribute : primitive.attributes) {
6205
3.96k
        const auto accessorsIndex = size_t(attribute.second);
6206
3.96k
        if (accessorsIndex < model->accessors.size()) {
6207
0
          const auto bufferView = model->accessors[accessorsIndex].bufferView;
6208
          // bufferView could be null(-1) for sparse morph target
6209
0
          if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
6210
0
            model->bufferViews[size_t(bufferView)].target =
6211
0
                TINYGLTF_TARGET_ARRAY_BUFFER;
6212
0
          }
6213
0
        }
6214
3.96k
      }
6215
6216
36.2k
      for (auto &target : primitive.targets) {
6217
38.6k
        for (auto &attribute : target) {
6218
38.6k
          const auto accessorsIndex = size_t(attribute.second);
6219
38.6k
          if (accessorsIndex < model->accessors.size()) {
6220
0
            const auto bufferView = model->accessors[accessorsIndex].bufferView;
6221
            // bufferView could be null(-1) for sparse morph target
6222
0
            if (bufferView >= 0 &&
6223
0
                bufferView < (int)model->bufferViews.size()) {
6224
0
              model->bufferViews[size_t(bufferView)].target =
6225
0
                  TINYGLTF_TARGET_ARRAY_BUFFER;
6226
0
            }
6227
0
          }
6228
38.6k
        }
6229
36.2k
      }
6230
3.34k
    }
6231
272k
  }
6232
6233
  // 7. Parse Node
6234
1.59k
  {
6235
73.4k
    bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
6236
73.4k
      if (!detail::IsObject(o)) {
6237
0
        if (err) {
6238
0
          (*err) += "`nodes' does not contain an JSON object.";
6239
0
        }
6240
0
        return false;
6241
0
      }
6242
73.4k
      Node node;
6243
73.4k
      if (!ParseNode(&node, err, o,
6244
73.4k
                     store_original_json_for_extras_and_extensions_)) {
6245
27
        return false;
6246
27
      }
6247
6248
73.4k
      model->nodes.emplace_back(std::move(node));
6249
73.4k
      return true;
6250
73.4k
    });
6251
6252
1.59k
    if (!success) {
6253
27
      return false;
6254
27
    }
6255
1.59k
  }
6256
6257
  // 8. Parse scenes.
6258
1.57k
  {
6259
67.7k
    bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
6260
67.7k
      if (!detail::IsObject(o)) {
6261
0
        if (err) {
6262
0
          (*err) += "`scenes' does not contain an JSON object.";
6263
0
        }
6264
0
        return false;
6265
0
      }
6266
6267
67.7k
      Scene scene;
6268
67.7k
      if (!ParseScene(&scene, err, o,
6269
67.7k
                      store_original_json_for_extras_and_extensions_)) {
6270
0
        return false;
6271
0
      }
6272
6273
67.7k
      model->scenes.emplace_back(std::move(scene));
6274
67.7k
      return true;
6275
67.7k
    });
6276
6277
1.57k
    if (!success) {
6278
0
      return false;
6279
0
    }
6280
1.57k
  }
6281
6282
  // 9. Parse default scenes.
6283
1.57k
  {
6284
1.57k
    detail::json_const_iterator rootIt;
6285
1.57k
    int iVal;
6286
1.57k
    if (detail::FindMember(v, "scene", rootIt) &&
6287
1.57k
        detail::GetInt(detail::GetValue(rootIt), iVal)) {
6288
0
      model->defaultScene = iVal;
6289
0
    }
6290
1.57k
  }
6291
6292
  // 10. Parse Material
6293
1.57k
  {
6294
311k
    bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
6295
311k
      if (!detail::IsObject(o)) {
6296
1
        if (err) {
6297
1
          (*err) += "`materials' does not contain an JSON object.";
6298
1
        }
6299
1
        return false;
6300
1
      }
6301
311k
      Material material;
6302
311k
      ParseStringProperty(&material.name, err, o, "name", false);
6303
6304
311k
      if (!ParseMaterial(&material, err, warn, o,
6305
311k
                         store_original_json_for_extras_and_extensions_,
6306
311k
                         strictness_)) {
6307
7
        return false;
6308
7
      }
6309
6310
311k
      model->materials.emplace_back(std::move(material));
6311
311k
      return true;
6312
311k
    });
6313
6314
1.57k
    if (!success) {
6315
8
      return false;
6316
8
    }
6317
1.57k
  }
6318
6319
  // 11. Parse Image
6320
1.56k
  void *load_image_user_data{nullptr};
6321
6322
1.56k
  LoadImageDataOption load_image_option;
6323
6324
1.56k
  if (user_image_loader_) {
6325
    // Use user supplied pointer
6326
0
    load_image_user_data = load_image_user_data_;
6327
1.56k
  } else {
6328
1.56k
    load_image_option.preserve_channels = preserve_image_channels_;
6329
1.56k
    load_image_user_data = reinterpret_cast<void *>(&load_image_option);
6330
1.56k
  }
6331
6332
1.56k
  {
6333
1.56k
    int idx = 0;
6334
61.2k
    bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
6335
61.2k
      if (!detail::IsObject(o)) {
6336
10
        if (err) {
6337
10
          (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
6338
10
        }
6339
10
        return false;
6340
10
      }
6341
61.2k
      Image image;
6342
61.2k
      if (!ParseImage(&image, idx, err, warn, o,
6343
61.2k
                      store_original_json_for_extras_and_extensions_, base_dir,
6344
61.2k
                      max_external_file_size_, &fs, &uri_cb,
6345
61.2k
                      &this->LoadImageData, load_image_user_data)) {
6346
379
        return false;
6347
379
      }
6348
6349
60.8k
      if (image.bufferView != -1) {
6350
        // Load image from the buffer view.
6351
0
        if (size_t(image.bufferView) >= model->bufferViews.size()) {
6352
0
          if (err) {
6353
0
            std::stringstream ss;
6354
0
            ss << "image[" << idx << "] bufferView \"" << image.bufferView
6355
0
               << "\" not found in the scene." << std::endl;
6356
0
            (*err) += ss.str();
6357
0
          }
6358
0
          return false;
6359
0
        }
6360
6361
0
        const BufferView &bufferView =
6362
0
            model->bufferViews[size_t(image.bufferView)];
6363
0
        if (size_t(bufferView.buffer) >= model->buffers.size()) {
6364
0
          if (err) {
6365
0
            std::stringstream ss;
6366
0
            ss << "image[" << idx << "] buffer \"" << bufferView.buffer
6367
0
               << "\" not found in the scene." << std::endl;
6368
0
            (*err) += ss.str();
6369
0
          }
6370
0
          return false;
6371
0
        }
6372
0
        const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6373
6374
0
        if (*LoadImageData == nullptr) {
6375
0
          if (err) {
6376
0
            (*err) += "No LoadImageData callback specified.\n";
6377
0
          }
6378
0
          return false;
6379
0
        }
6380
0
        bool ret = LoadImageData(
6381
0
            &image, idx, err, warn, image.width, image.height,
6382
0
            &buffer.data[bufferView.byteOffset],
6383
0
            static_cast<int>(bufferView.byteLength), load_image_user_data);
6384
0
        if (!ret) {
6385
0
          return false;
6386
0
        }
6387
0
      }
6388
6389
60.8k
      model->images.emplace_back(std::move(image));
6390
60.8k
      ++idx;
6391
60.8k
      return true;
6392
60.8k
    });
6393
6394
1.56k
    if (!success) {
6395
389
      return false;
6396
389
    }
6397
1.56k
  }
6398
6399
  // 12. Parse Texture
6400
1.17k
  {
6401
135k
    bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
6402
135k
      if (!detail::IsObject(o)) {
6403
0
        if (err) {
6404
0
          (*err) += "`textures' does not contain an JSON object.";
6405
0
        }
6406
0
        return false;
6407
0
      }
6408
135k
      Texture texture;
6409
135k
      if (!ParseTexture(&texture, err, o,
6410
135k
                        store_original_json_for_extras_and_extensions_,
6411
135k
                        base_dir)) {
6412
0
        return false;
6413
0
      }
6414
6415
135k
      model->textures.emplace_back(std::move(texture));
6416
135k
      return true;
6417
135k
    });
6418
6419
1.17k
    if (!success) {
6420
0
      return false;
6421
0
    }
6422
1.17k
  }
6423
6424
  // 13. Parse Animation
6425
1.17k
  {
6426
11.1k
    bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
6427
11.1k
      if (!detail::IsObject(o)) {
6428
1
        if (err) {
6429
1
          (*err) += "`animations' does not contain an JSON object.";
6430
1
        }
6431
1
        return false;
6432
1
      }
6433
11.1k
      Animation animation;
6434
11.1k
      if (!ParseAnimation(&animation, err, o,
6435
11.1k
                          store_original_json_for_extras_and_extensions_)) {
6436
0
        return false;
6437
0
      }
6438
6439
11.1k
      model->animations.emplace_back(std::move(animation));
6440
11.1k
      return true;
6441
11.1k
    });
6442
6443
1.17k
    if (!success) {
6444
1
      return false;
6445
1
    }
6446
1.17k
  }
6447
6448
  // 14. Parse Skin
6449
1.17k
  {
6450
1.17k
    bool success = ForEachInArray(v, "skins", [&](const detail::json &o) {
6451
0
      if (!detail::IsObject(o)) {
6452
0
        if (err) {
6453
0
          (*err) += "`skins' does not contain an JSON object.";
6454
0
        }
6455
0
        return false;
6456
0
      }
6457
0
      Skin skin;
6458
0
      if (!ParseSkin(&skin, err, o,
6459
0
                     store_original_json_for_extras_and_extensions_)) {
6460
0
        return false;
6461
0
      }
6462
6463
0
      model->skins.emplace_back(std::move(skin));
6464
0
      return true;
6465
0
    });
6466
6467
1.17k
    if (!success) {
6468
0
      return false;
6469
0
    }
6470
1.17k
  }
6471
6472
  // 15. Parse Sampler
6473
1.17k
  {
6474
294k
    bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
6475
294k
      if (!detail::IsObject(o)) {
6476
0
        if (err) {
6477
0
          (*err) += "`samplers' does not contain an JSON object.";
6478
0
        }
6479
0
        return false;
6480
0
      }
6481
294k
      Sampler sampler;
6482
294k
      if (!ParseSampler(&sampler, err, o,
6483
294k
                        store_original_json_for_extras_and_extensions_)) {
6484
0
        return false;
6485
0
      }
6486
6487
294k
      model->samplers.emplace_back(std::move(sampler));
6488
294k
      return true;
6489
294k
    });
6490
6491
1.17k
    if (!success) {
6492
0
      return false;
6493
0
    }
6494
1.17k
  }
6495
6496
  // 16. Parse Camera
6497
1.17k
  {
6498
1.17k
    bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
6499
28
      if (!detail::IsObject(o)) {
6500
0
        if (err) {
6501
0
          (*err) += "`cameras' does not contain an JSON object.";
6502
0
        }
6503
0
        return false;
6504
0
      }
6505
28
      Camera camera;
6506
28
      if (!ParseCamera(&camera, err, o,
6507
28
                       store_original_json_for_extras_and_extensions_)) {
6508
28
        return false;
6509
28
      }
6510
6511
0
      model->cameras.emplace_back(std::move(camera));
6512
0
      return true;
6513
28
    });
6514
6515
1.17k
    if (!success) {
6516
28
      return false;
6517
28
    }
6518
1.17k
  }
6519
6520
  // 17. Parse Extras & Extensions
6521
1.14k
  ParseExtrasAndExtensions(model, err, v,
6522
1.14k
                           store_original_json_for_extras_and_extensions_);
6523
6524
  // 18. Specific extension implementations
6525
1.14k
  {
6526
1.14k
    detail::json_const_iterator rootIt;
6527
1.14k
    if (detail::FindMember(v, "extensions", rootIt) &&
6528
1.14k
        detail::IsObject(detail::GetValue(rootIt))) {
6529
133
      const detail::json &root = detail::GetValue(rootIt);
6530
6531
133
      detail::json_const_iterator it(detail::ObjectBegin(root));
6532
133
      detail::json_const_iterator itEnd(detail::ObjectEnd(root));
6533
3.13k
      for (; it != itEnd; ++it) {
6534
        // parse KHR_lights_punctual extension
6535
3.00k
        std::string key(detail::GetKey(it));
6536
3.00k
        if ((key == "KHR_lights_punctual") &&
6537
3.00k
            detail::IsObject(detail::GetValue(it))) {
6538
141
          const detail::json &object = detail::GetValue(it);
6539
141
          detail::json_const_iterator itLight;
6540
141
          if (detail::FindMember(object, "lights", itLight)) {
6541
78
            const detail::json &lights = detail::GetValue(itLight);
6542
78
            if (!detail::IsArray(lights)) {
6543
78
              continue;
6544
78
            }
6545
6546
0
            auto arrayIt(detail::ArrayBegin(lights));
6547
0
            auto arrayItEnd(detail::ArrayEnd(lights));
6548
0
            for (; arrayIt != arrayItEnd; ++arrayIt) {
6549
0
              Light light;
6550
0
              if (!ParseLight(&light, err, *arrayIt,
6551
0
                              store_original_json_for_extras_and_extensions_)) {
6552
0
                return false;
6553
0
              }
6554
0
              model->lights.emplace_back(std::move(light));
6555
0
            }
6556
0
          }
6557
141
        }
6558
        // parse KHR_audio extension
6559
2.92k
        if ((key == "KHR_audio") && detail::IsObject(detail::GetValue(it))) {
6560
429
          const detail::json &object = detail::GetValue(it);
6561
429
          detail::json_const_iterator itKhrAudio;
6562
429
          if (detail::FindMember(object, "emitters", itKhrAudio)) {
6563
56
            const detail::json &emitters = detail::GetValue(itKhrAudio);
6564
56
            if (!detail::IsArray(emitters)) {
6565
56
              continue;
6566
56
            }
6567
6568
0
            auto arrayIt(detail::ArrayBegin(emitters));
6569
0
            auto arrayItEnd(detail::ArrayEnd(emitters));
6570
0
            for (; arrayIt != arrayItEnd; ++arrayIt) {
6571
0
              AudioEmitter emitter;
6572
0
              if (!ParseAudioEmitter(
6573
0
                      &emitter, err, *arrayIt,
6574
0
                      store_original_json_for_extras_and_extensions_)) {
6575
0
                return false;
6576
0
              }
6577
0
              model->audioEmitters.emplace_back(std::move(emitter));
6578
0
            }
6579
0
          }
6580
6581
373
          if (detail::FindMember(object, "sources", itKhrAudio)) {
6582
193
            const detail::json &sources = detail::GetValue(itKhrAudio);
6583
193
            if (!detail::IsArray(sources)) {
6584
149
              continue;
6585
149
            }
6586
6587
44
            auto arrayIt(detail::ArrayBegin(sources));
6588
44
            auto arrayItEnd(detail::ArrayEnd(sources));
6589
769k
            for (; arrayIt != arrayItEnd; ++arrayIt) {
6590
769k
              AudioSource source;
6591
769k
              if (!ParseAudioSource(
6592
769k
                      &source, err, *arrayIt,
6593
769k
                      store_original_json_for_extras_and_extensions_)) {
6594
0
                return false;
6595
0
              }
6596
769k
              model->audioSources.emplace_back(std::move(source));
6597
769k
            }
6598
44
          }
6599
373
        }
6600
2.92k
      }
6601
133
    }
6602
1.14k
  }
6603
6604
1.14k
  return true;
6605
1.14k
}
6606
6607
bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
6608
                                   std::string *warn, const char *str,
6609
                                   unsigned int length,
6610
                                   const std::string &base_dir,
6611
1.82k
                                   unsigned int check_sections) {
6612
1.82k
  is_binary_ = false;
6613
1.82k
  bin_data_ = nullptr;
6614
1.82k
  bin_size_ = 0;
6615
6616
1.82k
  return LoadFromString(model, err, warn, str, length, base_dir,
6617
1.82k
                        check_sections);
6618
1.82k
}
6619
6620
bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
6621
                                 std::string *warn, const std::string &filename,
6622
0
                                 unsigned int check_sections) {
6623
0
  std::stringstream ss;
6624
6625
0
  if (fs.ReadWholeFile == nullptr) {
6626
    // Programmer error, assert() ?
6627
0
    ss << "Failed to read file: " << filename
6628
0
       << ": one or more FS callback not set" << std::endl;
6629
0
    if (err) {
6630
0
      (*err) = ss.str();
6631
0
    }
6632
0
    return false;
6633
0
  }
6634
6635
0
  std::vector<unsigned char> data;
6636
0
  std::string fileerr;
6637
0
  bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
6638
0
  if (!fileread) {
6639
0
    ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6640
0
    if (err) {
6641
0
      (*err) = ss.str();
6642
0
    }
6643
0
    return false;
6644
0
  }
6645
6646
0
  size_t sz = data.size();
6647
0
  if (sz == 0) {
6648
0
    if (err) {
6649
0
      (*err) = "Empty file.";
6650
0
    }
6651
0
    return false;
6652
0
  }
6653
6654
0
  std::string basedir = GetBaseDir(filename);
6655
6656
0
  bool ret = LoadASCIIFromString(
6657
0
      model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6658
0
      static_cast<unsigned int>(data.size()), basedir, check_sections);
6659
6660
0
  return ret;
6661
0
}
6662
6663
bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
6664
                                    std::string *warn,
6665
                                    const unsigned char *bytes,
6666
                                    unsigned int size,
6667
                                    const std::string &base_dir,
6668
0
                                    unsigned int check_sections) {
6669
0
  if (size < 20) {
6670
0
    if (err) {
6671
0
      (*err) = "Too short data size for glTF Binary.";
6672
0
    }
6673
0
    return false;
6674
0
  }
6675
6676
0
  if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6677
0
      bytes[3] == 'F') {
6678
    // ok
6679
0
  } else {
6680
0
    if (err) {
6681
0
      (*err) = "Invalid magic.";
6682
0
    }
6683
0
    return false;
6684
0
  }
6685
6686
0
  unsigned int version;        // 4 bytes
6687
0
  unsigned int length;         // 4 bytes
6688
0
  unsigned int chunk0_length;  // 4 bytes
6689
0
  unsigned int chunk0_format;  // 4 bytes;
6690
6691
0
  memcpy(&version, bytes + 4, 4);
6692
0
  swap4(&version);
6693
0
  memcpy(&length, bytes + 8, 4); // Total glb size, including header and all chunks.
6694
0
  swap4(&length);
6695
0
  memcpy(&chunk0_length, bytes + 12, 4);  // JSON data length
6696
0
  swap4(&chunk0_length);
6697
0
  memcpy(&chunk0_format, bytes + 16, 4);
6698
0
  swap4(&chunk0_format);
6699
6700
  // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6701
  //
6702
  // In case the Bin buffer is not present, the size is exactly 20 + size of
6703
  // JSON contents,
6704
  // so use "greater than" operator.
6705
  //
6706
  // https://github.com/syoyo/tinygltf/issues/372
6707
  // Use 64bit uint to avoid integer overflow.
6708
0
  uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
6709
6710
0
  if (header_and_json_size > (std::numeric_limits<uint32_t>::max)()) {
6711
    // Do not allow 4GB or more GLB data.
6712
0
    if (err) {
6713
0
      (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6714
0
    }
6715
0
    return false;
6716
0
  }
6717
6718
0
  if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) ||
6719
0
      (length > size) || (header_and_json_size > uint64_t(length)) ||
6720
0
      (chunk0_format != 0x4E4F534A)) {  // 0x4E4F534A = JSON format.
6721
0
    if (err) {
6722
0
      (*err) = "Invalid glTF binary.";
6723
0
    }
6724
0
    return false;
6725
0
  }
6726
6727
  // Padding check
6728
  // The start and the end of each chunk must be aligned to a 4-byte boundary.
6729
  // No padding check for chunk0 start since its 4byte-boundary is ensured.
6730
0
  if ((header_and_json_size % 4) != 0) {
6731
0
    if (err) {
6732
0
      (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6733
0
    }
6734
0
    return false;
6735
0
  }
6736
6737
  // std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6738
  // std::cout << "length = " << length << "\n";
6739
6740
  // Chunk1(BIN) data
6741
  // The spec says: When the binary buffer is empty or when it is stored by
6742
  // other means, this chunk SHOULD be omitted. So when header + JSON data ==
6743
  // binary size, Chunk1 is omitted.
6744
0
  if (header_and_json_size == uint64_t(length)) {
6745
0
    bin_data_ = nullptr;
6746
0
    bin_size_ = 0;
6747
0
  } else {
6748
    // Read Chunk1 info(BIN data)
6749
    //
6750
    // issue-440:
6751
    // 'SHOULD' in glTF spec means 'RECOMMENDED',
6752
    // So there is a situation that Chunk1(BIN) is composed of zero-sized BIN data
6753
    // (chunksize(0) + binformat(BIN) = 8bytes).
6754
    // 
6755
0
    if ((header_and_json_size + 8ull) > uint64_t(length)) {
6756
0
      if (err) {
6757
0
        (*err) =
6758
0
            "Insufficient storage space for Chunk1(BIN data). At least Chunk1 "
6759
0
            "Must have 8 or more bytes, but got " +
6760
0
            std::to_string((header_and_json_size + 8ull) - uint64_t(length)) +
6761
0
            ".\n";
6762
0
      }
6763
0
      return false;
6764
0
    }
6765
6766
0
    unsigned int chunk1_length{0};  // 4 bytes
6767
0
    unsigned int chunk1_format{0};  // 4 bytes;
6768
0
    memcpy(&chunk1_length, bytes + header_and_json_size,
6769
0
           4);  // Bin data length
6770
0
    swap4(&chunk1_length);
6771
0
    memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6772
0
    swap4(&chunk1_format);
6773
6774
0
    if (chunk1_format != 0x004e4942) {
6775
0
      if (err) {
6776
0
        (*err) = "Invalid chunkType for Chunk1.";
6777
0
      }
6778
0
      return false;
6779
0
    }
6780
6781
0
    if (chunk1_length == 0) {
6782
6783
0
      if (header_and_json_size + 8 > uint64_t(length)) {
6784
0
        if (err) {
6785
0
          (*err) = "BIN Chunk header location exceeds the GLB size.";
6786
0
        }
6787
0
        return false;
6788
0
      }
6789
6790
0
      bin_data_ = nullptr;
6791
6792
0
    } else {
6793
6794
      // When BIN chunk size is not zero, at least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin
6795
      // payload could be 1~3 bytes, but need to be aligned to 4 bytes)
6796
6797
0
      if (chunk1_length < 4) {
6798
0
        if (err) {
6799
0
          (*err) = "Insufficient Chunk1(BIN) data size.";
6800
0
        }
6801
0
        return false;
6802
0
      }
6803
6804
0
      if ((chunk1_length % 4) != 0) {
6805
0
        if (strictness_==ParseStrictness::Permissive) {
6806
0
          if (warn) {
6807
0
            (*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n";
6808
0
          }
6809
0
        }
6810
0
        else {
6811
0
          if (err) {
6812
0
            (*err) = "BIN Chunk end is not aligned to a 4-byte boundary.";
6813
0
          }
6814
0
          return false;
6815
0
        }
6816
0
      }
6817
6818
      // +8 chunk1 header size.
6819
0
      if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) {
6820
0
        if (err) {
6821
0
          (*err) = "BIN Chunk data length exceeds the GLB size.";
6822
0
        }
6823
0
        return false;
6824
0
      }
6825
6826
0
      bin_data_ = bytes + header_and_json_size +
6827
0
                  8;  // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6828
0
    }
6829
6830
0
    bin_size_ = size_t(chunk1_length);
6831
0
  }
6832
6833
0
  is_binary_ = true;
6834
6835
0
  bool ret = LoadFromString(model, err, warn,
6836
0
                            reinterpret_cast<const char *>(&bytes[20]),
6837
0
                            chunk0_length, base_dir, check_sections);
6838
0
  if (!ret) {
6839
0
    return ret;
6840
0
  }
6841
6842
0
  return true;
6843
0
}
6844
6845
bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
6846
                                  std::string *warn,
6847
                                  const std::string &filename,
6848
0
                                  unsigned int check_sections) {
6849
0
  std::stringstream ss;
6850
6851
0
  if (fs.ReadWholeFile == nullptr) {
6852
    // Programmer error, assert() ?
6853
0
    ss << "Failed to read file: " << filename
6854
0
       << ": one or more FS callback not set" << std::endl;
6855
0
    if (err) {
6856
0
      (*err) = ss.str();
6857
0
    }
6858
0
    return false;
6859
0
  }
6860
6861
0
  std::vector<unsigned char> data;
6862
0
  std::string fileerr;
6863
0
  bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
6864
0
  if (!fileread) {
6865
0
    ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6866
0
    if (err) {
6867
0
      (*err) = ss.str();
6868
0
    }
6869
0
    return false;
6870
0
  }
6871
6872
0
  std::string basedir = GetBaseDir(filename);
6873
6874
0
  bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6875
0
                                  static_cast<unsigned int>(data.size()),
6876
0
                                  basedir, check_sections);
6877
6878
0
  return ret;
6879
0
}
6880
6881
///////////////////////
6882
// GLTF Serialization
6883
///////////////////////
6884
namespace detail {
6885
0
detail::json JsonFromString(const char *s) {
6886
#ifdef TINYGLTF_USE_RAPIDJSON
6887
  return detail::json(s, detail::GetAllocator());
6888
#else
6889
0
  return detail::json(s);
6890
0
#endif
6891
0
}
6892
6893
0
void JsonAssign(detail::json &dest, const detail::json &src) {
6894
#ifdef TINYGLTF_USE_RAPIDJSON
6895
  dest.CopyFrom(src, detail::GetAllocator());
6896
#else
6897
0
  dest = src;
6898
0
#endif
6899
0
}
6900
6901
0
void JsonAddMember(detail::json &o, const char *key, detail::json &&value) {
6902
#ifdef TINYGLTF_USE_RAPIDJSON
6903
  if (!o.IsObject()) {
6904
    o.SetObject();
6905
  }
6906
6907
  // Issue 420.
6908
  // AddMember may create duplicated key, so use [] API when a key already
6909
  // exists.
6910
  // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863
6911
  detail::json_const_iterator it;
6912
  if (detail::FindMember(o, key, it)) {
6913
    o[key] = std::move(value);  // replace
6914
  } else {
6915
    o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value),
6916
                detail::GetAllocator());
6917
  }
6918
#else
6919
0
  o[key] = std::move(value);
6920
0
#endif
6921
0
}
6922
6923
0
void JsonPushBack(detail::json &o, detail::json &&value) {
6924
#ifdef TINYGLTF_USE_RAPIDJSON
6925
  o.PushBack(std::move(value), detail::GetAllocator());
6926
#else
6927
0
  o.push_back(std::move(value));
6928
0
#endif
6929
0
}
6930
6931
0
bool JsonIsNull(const detail::json &o) {
6932
#ifdef TINYGLTF_USE_RAPIDJSON
6933
  return o.IsNull();
6934
#else
6935
0
  return o.is_null();
6936
0
#endif
6937
0
}
6938
6939
0
void JsonSetObject(detail::json &o) {
6940
#ifdef TINYGLTF_USE_RAPIDJSON
6941
  o.SetObject();
6942
#else
6943
0
  o = o.object({});
6944
0
#endif
6945
0
}
6946
6947
0
void JsonReserveArray(detail::json &o, size_t s) {
6948
#ifdef TINYGLTF_USE_RAPIDJSON
6949
  o.SetArray();
6950
  o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator());
6951
#endif
6952
0
  (void)(o);
6953
0
  (void)(s);
6954
0
}
6955
}  // namespace detail
6956
6957
// typedef std::pair<std::string, detail::json> json_object_pair;
6958
6959
template <typename T>
6960
static void SerializeNumberProperty(const std::string &key, T number,
6961
0
                                    detail::json &obj) {
6962
  // obj.insert(
6963
  //    json_object_pair(key, detail::json(static_cast<double>(number))));
6964
  // obj[key] = static_cast<double>(number);
6965
0
  detail::JsonAddMember(obj, key.c_str(), detail::json(number));
6966
0
}
Unexecuted instantiation: fuzz_gltf.cc:void tinygltf::SerializeNumberProperty<unsigned long>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: fuzz_gltf.cc:void tinygltf::SerializeNumberProperty<double>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, double, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: fuzz_gltf.cc:void tinygltf::SerializeNumberProperty<int>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, int, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: fuzz_gltf.cc:void tinygltf::SerializeNumberProperty<bool>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, bool, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
6967
6968
#ifdef TINYGLTF_USE_RAPIDJSON
6969
template <>
6970
void SerializeNumberProperty(const std::string &key, size_t number,
6971
                             detail::json &obj) {
6972
  detail::JsonAddMember(obj, key.c_str(),
6973
                        detail::json(static_cast<uint64_t>(number)));
6974
}
6975
#endif
6976
6977
template <typename T>
6978
static void SerializeNumberArrayProperty(const std::string &key,
6979
                                         const std::vector<T> &value,
6980
0
                                         detail::json &obj) {
6981
0
  if (value.empty()) return;
6982
6983
0
  detail::json ary;
6984
0
  detail::JsonReserveArray(ary, value.size());
6985
0
  for (const auto &s : value) {
6986
0
    detail::JsonPushBack(ary, detail::json(s));
6987
0
  }
6988
0
  detail::JsonAddMember(obj, key.c_str(), std::move(ary));
6989
0
}
Unexecuted instantiation: fuzz_gltf.cc:void tinygltf::SerializeNumberArrayProperty<double>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<double, std::__1::allocator<double> > const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: fuzz_gltf.cc:void tinygltf::SerializeNumberArrayProperty<int>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<int, std::__1::allocator<int> > const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
6990
6991
static void SerializeStringProperty(const std::string &key,
6992
                                    const std::string &value,
6993
0
                                    detail::json &obj) {
6994
0
  detail::JsonAddMember(obj, key.c_str(),
6995
0
                        detail::JsonFromString(value.c_str()));
6996
0
}
6997
6998
static void SerializeStringArrayProperty(const std::string &key,
6999
                                         const std::vector<std::string> &value,
7000
0
                                         detail::json &obj) {
7001
0
  detail::json ary;
7002
0
  detail::JsonReserveArray(ary, value.size());
7003
0
  for (auto &s : value) {
7004
0
    detail::JsonPushBack(ary, detail::JsonFromString(s.c_str()));
7005
0
  }
7006
0
  detail::JsonAddMember(obj, key.c_str(), std::move(ary));
7007
0
}
7008
7009
0
static bool ValueToJson(const Value &value, detail::json *ret) {
7010
0
  detail::json obj;
7011
#ifdef TINYGLTF_USE_RAPIDJSON
7012
  switch (value.Type()) {
7013
    case REAL_TYPE:
7014
      obj.SetDouble(value.Get<double>());
7015
      break;
7016
    case INT_TYPE:
7017
      obj.SetInt(value.Get<int>());
7018
      break;
7019
    case BOOL_TYPE:
7020
      obj.SetBool(value.Get<bool>());
7021
      break;
7022
    case STRING_TYPE:
7023
      obj.SetString(value.Get<std::string>().c_str(), detail::GetAllocator());
7024
      break;
7025
    case ARRAY_TYPE: {
7026
      obj.SetArray();
7027
      obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
7028
                  detail::GetAllocator());
7029
      for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
7030
        Value elementValue = value.Get(int(i));
7031
        detail::json elementJson;
7032
        if (ValueToJson(value.Get(int(i)), &elementJson))
7033
          obj.PushBack(std::move(elementJson), detail::GetAllocator());
7034
      }
7035
      break;
7036
    }
7037
    case BINARY_TYPE:
7038
      // TODO
7039
      // obj = detail::json(value.Get<std::vector<unsigned char>>());
7040
      return false;
7041
      break;
7042
    case OBJECT_TYPE: {
7043
      obj.SetObject();
7044
      Value::Object objMap = value.Get<Value::Object>();
7045
      for (auto &it : objMap) {
7046
        detail::json elementJson;
7047
        if (ValueToJson(it.second, &elementJson)) {
7048
          obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()),
7049
                        std::move(elementJson), detail::GetAllocator());
7050
        }
7051
      }
7052
      break;
7053
    }
7054
    case NULL_TYPE:
7055
    default:
7056
      return false;
7057
  }
7058
#else
7059
0
  switch (value.Type()) {
7060
0
    case REAL_TYPE:
7061
0
      obj = detail::json(value.Get<double>());
7062
0
      break;
7063
0
    case INT_TYPE:
7064
0
      obj = detail::json(value.Get<int>());
7065
0
      break;
7066
0
    case BOOL_TYPE:
7067
0
      obj = detail::json(value.Get<bool>());
7068
0
      break;
7069
0
    case STRING_TYPE:
7070
0
      obj = detail::json(value.Get<std::string>());
7071
0
      break;
7072
0
    case ARRAY_TYPE: {
7073
0
      for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
7074
0
        Value elementValue = value.Get(int(i));
7075
0
        detail::json elementJson;
7076
0
        if (ValueToJson(value.Get(int(i)), &elementJson))
7077
0
          obj.push_back(elementJson);
7078
0
      }
7079
0
      break;
7080
0
    }
7081
0
    case BINARY_TYPE:
7082
      // TODO
7083
      // obj = json(value.Get<std::vector<unsigned char>>());
7084
0
      return false;
7085
0
      break;
7086
0
    case OBJECT_TYPE: {
7087
0
      Value::Object objMap = value.Get<Value::Object>();
7088
0
      for (auto &it : objMap) {
7089
0
        detail::json elementJson;
7090
0
        if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
7091
0
      }
7092
0
      break;
7093
0
    }
7094
0
    case NULL_TYPE:
7095
0
    default:
7096
0
      return false;
7097
0
  }
7098
0
#endif
7099
0
  if (ret) *ret = std::move(obj);
7100
0
  return true;
7101
0
}
7102
7103
static void SerializeValue(const std::string &key, const Value &value,
7104
0
                           detail::json &obj) {
7105
0
  detail::json ret;
7106
0
  if (ValueToJson(value, &ret)) {
7107
0
    detail::JsonAddMember(obj, key.c_str(), std::move(ret));
7108
0
  }
7109
0
}
7110
7111
static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
7112
0
                                    detail::json &o) {
7113
0
  std::string header = "data:application/octet-stream;base64,";
7114
0
  if (data.size() > 0) {
7115
0
    std::string encodedData =
7116
0
        base64_encode(&data[0], static_cast<unsigned int>(data.size()));
7117
0
    SerializeStringProperty("uri", header + encodedData, o);
7118
0
  } else {
7119
    // Issue #229
7120
    // size 0 is allowed. Just emit mime header.
7121
0
    SerializeStringProperty("uri", header, o);
7122
0
  }
7123
0
}
7124
7125
static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
7126
0
                                    const std::string &binFilename) {
7127
#ifdef _WIN32
7128
#if defined(__GLIBCXX__)  // mingw
7129
  int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
7130
                               _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7131
  __gnu_cxx::stdio_filebuf<char> wfile_buf(
7132
      file_descriptor, std::ios_base::out | std::ios_base::binary);
7133
  std::ostream output(&wfile_buf);
7134
  if (!wfile_buf.is_open()) return false;
7135
#elif defined(_MSC_VER)
7136
  std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
7137
  if (!output.is_open()) return false;
7138
#else
7139
  std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7140
  if (!output.is_open()) return false;
7141
#endif
7142
#else
7143
0
  std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7144
0
  if (!output.is_open()) return false;
7145
0
#endif
7146
0
  if (data.size() > 0) {
7147
0
    output.write(reinterpret_cast<const char *>(&data[0]),
7148
0
                 std::streamsize(data.size()));
7149
0
  } else {
7150
    // Issue #229
7151
    // size 0 will be still valid buffer data.
7152
    // write empty file.
7153
0
  }
7154
0
  return true;
7155
0
}
7156
7157
#if 0  // FIXME(syoyo): not used. will be removed in the future release.
7158
static void SerializeParameterMap(ParameterMap &param, detail::json &o) {
7159
  for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
7160
       ++paramIt) {
7161
    if (paramIt->second.number_array.size()) {
7162
      SerializeNumberArrayProperty<double>(paramIt->first,
7163
                                           paramIt->second.number_array, o);
7164
    } else if (paramIt->second.json_double_value.size()) {
7165
      detail::json json_double_value;
7166
      for (std::map<std::string, double>::iterator it =
7167
               paramIt->second.json_double_value.begin();
7168
           it != paramIt->second.json_double_value.end(); ++it) {
7169
        if (it->first == "index") {
7170
          json_double_value[it->first] = paramIt->second.TextureIndex();
7171
        } else {
7172
          json_double_value[it->first] = it->second;
7173
        }
7174
      }
7175
7176
      o[paramIt->first] = json_double_value;
7177
    } else if (!paramIt->second.string_value.empty()) {
7178
      SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
7179
    } else if (paramIt->second.has_number_value) {
7180
      o[paramIt->first] = paramIt->second.number_value;
7181
    } else {
7182
      o[paramIt->first] = paramIt->second.bool_value;
7183
    }
7184
  }
7185
}
7186
#endif
7187
7188
static void SerializeExtensionMap(const ExtensionMap &extensions,
7189
0
                                  detail::json &o) {
7190
0
  if (!extensions.size()) return;
7191
7192
0
  detail::json extMap;
7193
0
  for (ExtensionMap::const_iterator extIt = extensions.begin();
7194
0
       extIt != extensions.end(); ++extIt) {
7195
    // Allow an empty object for extension(#97)
7196
0
    detail::json ret;
7197
0
    bool isNull = true;
7198
0
    if (ValueToJson(extIt->second, &ret)) {
7199
0
      isNull = detail::JsonIsNull(ret);
7200
0
      detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
7201
0
    }
7202
0
    if (isNull) {
7203
0
      if (!(extIt->first.empty())) {  // name should not be empty, but for sure
7204
        // create empty object so that an extension name is still included in
7205
        // json.
7206
0
        detail::json empty;
7207
0
        detail::JsonSetObject(empty);
7208
0
        detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
7209
0
      }
7210
0
    }
7211
0
  }
7212
0
  detail::JsonAddMember(o, "extensions", std::move(extMap));
7213
0
}
7214
7215
0
static void SerializeExtras(const Value &extras, detail::json &o) {
7216
0
  if (extras.Type() != NULL_TYPE) SerializeValue("extras", extras, o);
7217
0
}
7218
7219
template <typename GltfType>
7220
0
void SerializeExtrasAndExtensions(const GltfType &obj, detail::json &o) {
7221
0
  SerializeExtensionMap(obj.extensions, o);
7222
0
  SerializeExtras(obj.extras, o);
7223
0
}
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Accessor>(tinygltf::Accessor const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Accessor::Sparse::{unnamed type#1}>(tinygltf::Accessor::Sparse::{unnamed type#1} const&, nlohmann::basic_json<std::__1::map, nlohmann::basic_json::vector, nlohmann::basic_json::basic_string<char, nlohmann::basic_json::char_traits<char>, nlohmann::basic_json::allocator<char> >, bool, long, unsigned long, double, nlohmann::basic_json::char_traits<char>, tinygltf::Accessor::Sparse::{unnamed type#1} const&::adl_serializer, std::__1::map<unsigned char, nlohmann::basic_json::char_traits<char><unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Accessor::Sparse::{unnamed type#2}>(tinygltf::Accessor::Sparse::{unnamed type#2} const&, nlohmann::basic_json<std::__1::map, nlohmann::basic_json::vector, nlohmann::basic_json::basic_string<char, nlohmann::basic_json::char_traits<char>, nlohmann::basic_json::allocator<char> >, bool, long, unsigned long, double, nlohmann::basic_json::char_traits<char>, tinygltf::Accessor::Sparse::{unnamed type#2} const&::adl_serializer, std::__1::map<unsigned char, nlohmann::basic_json::char_traits<char><unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Accessor::Sparse>(tinygltf::Accessor::Sparse const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::AnimationChannel>(tinygltf::AnimationChannel const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::AnimationSampler>(tinygltf::AnimationSampler const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Animation>(tinygltf::Animation const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Asset>(tinygltf::Asset const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::BufferView>(tinygltf::BufferView const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::NormalTextureInfo>(tinygltf::NormalTextureInfo const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::OcclusionTextureInfo>(tinygltf::OcclusionTextureInfo const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::TextureInfo>(tinygltf::TextureInfo const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::PbrMetallicRoughness>(tinygltf::PbrMetallicRoughness const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Material>(tinygltf::Material const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Primitive>(tinygltf::Primitive const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Mesh>(tinygltf::Mesh const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Node>(tinygltf::Node const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Scene>(tinygltf::Scene const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Skin>(tinygltf::Skin const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Texture>(tinygltf::Texture const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Sampler>(tinygltf::Sampler const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::OrthographicCamera>(tinygltf::OrthographicCamera const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::PerspectiveCamera>(tinygltf::PerspectiveCamera const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Camera>(tinygltf::Camera const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Model>(tinygltf::Model const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::SpotLight>(tinygltf::SpotLight const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Light>(tinygltf::Light const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::PositionalEmitter>(tinygltf::PositionalEmitter const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::AudioEmitter>(tinygltf::AudioEmitter const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::AudioSource>(tinygltf::AudioSource const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Buffer>(tinygltf::Buffer const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
Unexecuted instantiation: void tinygltf::SerializeExtrasAndExtensions<tinygltf::Image>(tinygltf::Image const&, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > >&)
7224
7225
0
static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
7226
0
  if (accessor.bufferView >= 0)
7227
0
    SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
7228
7229
0
  if (accessor.byteOffset != 0)
7230
0
    SerializeNumberProperty<size_t>("byteOffset", accessor.byteOffset, o);
7231
7232
0
  SerializeNumberProperty<int>("componentType", accessor.componentType, o);
7233
0
  SerializeNumberProperty<size_t>("count", accessor.count, o);
7234
7235
0
  if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
7236
0
      (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
7237
0
    SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
7238
0
    SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
7239
0
  } else {
7240
    // Issue #301. Serialize as integer.
7241
    // Assume int value is within [-2**31-1, 2**31-1]
7242
0
    {
7243
0
      std::vector<int> values;
7244
0
      std::transform(accessor.minValues.begin(), accessor.minValues.end(),
7245
0
                     std::back_inserter(values),
7246
0
                     [](double v) { return static_cast<int>(v); });
7247
7248
0
      SerializeNumberArrayProperty<int>("min", values, o);
7249
0
    }
7250
7251
0
    {
7252
0
      std::vector<int> values;
7253
0
      std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
7254
0
                     std::back_inserter(values),
7255
0
                     [](double v) { return static_cast<int>(v); });
7256
7257
0
      SerializeNumberArrayProperty<int>("max", values, o);
7258
0
    }
7259
0
  }
7260
7261
0
  if (accessor.normalized)
7262
0
    SerializeValue("normalized", Value(accessor.normalized), o);
7263
0
  std::string type;
7264
0
  switch (accessor.type) {
7265
0
    case TINYGLTF_TYPE_SCALAR:
7266
0
      type = "SCALAR";
7267
0
      break;
7268
0
    case TINYGLTF_TYPE_VEC2:
7269
0
      type = "VEC2";
7270
0
      break;
7271
0
    case TINYGLTF_TYPE_VEC3:
7272
0
      type = "VEC3";
7273
0
      break;
7274
0
    case TINYGLTF_TYPE_VEC4:
7275
0
      type = "VEC4";
7276
0
      break;
7277
0
    case TINYGLTF_TYPE_MAT2:
7278
0
      type = "MAT2";
7279
0
      break;
7280
0
    case TINYGLTF_TYPE_MAT3:
7281
0
      type = "MAT3";
7282
0
      break;
7283
0
    case TINYGLTF_TYPE_MAT4:
7284
0
      type = "MAT4";
7285
0
      break;
7286
0
  }
7287
7288
0
  SerializeStringProperty("type", type, o);
7289
0
  if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
7290
7291
0
  SerializeExtrasAndExtensions(accessor, o);
7292
7293
  // sparse
7294
0
  if (accessor.sparse.isSparse) {
7295
0
    detail::json sparse;
7296
0
    SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
7297
0
    {
7298
0
      detail::json indices;
7299
0
      SerializeNumberProperty<int>("bufferView",
7300
0
                                   accessor.sparse.indices.bufferView, indices);
7301
0
      SerializeNumberProperty<size_t>("byteOffset",
7302
0
                                   accessor.sparse.indices.byteOffset, indices);
7303
0
      SerializeNumberProperty<int>(
7304
0
          "componentType", accessor.sparse.indices.componentType, indices);
7305
0
      SerializeExtrasAndExtensions(accessor.sparse.indices, indices);
7306
0
      detail::JsonAddMember(sparse, "indices", std::move(indices));
7307
0
    }
7308
0
    {
7309
0
      detail::json values;
7310
0
      SerializeNumberProperty<int>("bufferView",
7311
0
                                   accessor.sparse.values.bufferView, values);
7312
0
      SerializeNumberProperty<size_t>("byteOffset",
7313
0
                                   accessor.sparse.values.byteOffset, values);
7314
0
      SerializeExtrasAndExtensions(accessor.sparse.values, values);
7315
0
      detail::JsonAddMember(sparse, "values", std::move(values));
7316
0
    }
7317
0
    SerializeExtrasAndExtensions(accessor.sparse, sparse);
7318
0
    detail::JsonAddMember(o, "sparse", std::move(sparse));
7319
0
  }
7320
0
}
7321
7322
static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
7323
0
                                          detail::json &o) {
7324
0
  SerializeNumberProperty("sampler", channel.sampler, o);
7325
0
  {
7326
0
    detail::json target;
7327
7328
0
    if (channel.target_node >= 0) {
7329
0
      SerializeNumberProperty("node", channel.target_node, target);
7330
0
    }
7331
7332
0
    SerializeStringProperty("path", channel.target_path, target);
7333
7334
0
    SerializeExtensionMap(channel.target_extensions, target);
7335
0
    SerializeExtras(channel.target_extras, target);
7336
7337
0
    detail::JsonAddMember(o, "target", std::move(target));
7338
0
  }
7339
7340
0
  SerializeExtrasAndExtensions(channel, o);
7341
0
}
7342
7343
static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
7344
0
                                          detail::json &o) {
7345
0
  SerializeNumberProperty("input", sampler.input, o);
7346
0
  SerializeNumberProperty("output", sampler.output, o);
7347
0
  SerializeStringProperty("interpolation", sampler.interpolation, o);
7348
7349
0
  SerializeExtrasAndExtensions(sampler, o);
7350
0
}
7351
7352
static void SerializeGltfAnimation(const Animation &animation,
7353
0
                                   detail::json &o) {
7354
0
  if (!animation.name.empty())
7355
0
    SerializeStringProperty("name", animation.name, o);
7356
7357
0
  {
7358
0
    detail::json channels;
7359
0
    detail::JsonReserveArray(channels, animation.channels.size());
7360
0
    for (unsigned int i = 0; i < animation.channels.size(); ++i) {
7361
0
      detail::json channel;
7362
0
      AnimationChannel gltfChannel = animation.channels[i];
7363
0
      SerializeGltfAnimationChannel(gltfChannel, channel);
7364
0
      detail::JsonPushBack(channels, std::move(channel));
7365
0
    }
7366
7367
0
    detail::JsonAddMember(o, "channels", std::move(channels));
7368
0
  }
7369
7370
0
  {
7371
0
    detail::json samplers;
7372
0
    detail::JsonReserveArray(samplers, animation.samplers.size());
7373
0
    for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
7374
0
      detail::json sampler;
7375
0
      AnimationSampler gltfSampler = animation.samplers[i];
7376
0
      SerializeGltfAnimationSampler(gltfSampler, sampler);
7377
0
      detail::JsonPushBack(samplers, std::move(sampler));
7378
0
    }
7379
0
    detail::JsonAddMember(o, "samplers", std::move(samplers));
7380
0
  }
7381
7382
0
  SerializeExtrasAndExtensions(animation, o);
7383
0
}
7384
7385
0
static void SerializeGltfAsset(const Asset &asset, detail::json &o) {
7386
0
  if (!asset.generator.empty()) {
7387
0
    SerializeStringProperty("generator", asset.generator, o);
7388
0
  }
7389
7390
0
  if (!asset.copyright.empty()) {
7391
0
    SerializeStringProperty("copyright", asset.copyright, o);
7392
0
  }
7393
7394
0
  auto version = asset.version;
7395
0
  if (version.empty()) {
7396
    // Just in case
7397
    // `version` must be defined
7398
0
    version = "2.0";
7399
0
  }
7400
7401
  // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
7402
0
  SerializeStringProperty("version", version, o);
7403
7404
0
  SerializeExtrasAndExtensions(asset, o);
7405
0
}
7406
7407
static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o,
7408
0
                                   std::vector<unsigned char> &binBuffer) {
7409
0
  SerializeNumberProperty("byteLength", buffer.data.size(), o);
7410
0
  binBuffer = buffer.data;
7411
7412
0
  if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7413
7414
0
  SerializeExtrasAndExtensions(buffer, o);
7415
0
}
7416
7417
0
static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) {
7418
0
  SerializeNumberProperty("byteLength", buffer.data.size(), o);
7419
0
  SerializeGltfBufferData(buffer.data, o);
7420
7421
0
  if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7422
7423
0
  SerializeExtrasAndExtensions(buffer, o);
7424
0
}
7425
7426
static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o,
7427
                                const std::string &binFilename,
7428
0
                                const std::string &binUri) {
7429
0
  if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
7430
0
  SerializeNumberProperty("byteLength", buffer.data.size(), o);
7431
0
  SerializeStringProperty("uri", binUri, o);
7432
7433
0
  if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7434
7435
0
  SerializeExtrasAndExtensions(buffer, o);
7436
0
  return true;
7437
0
}
7438
7439
static void SerializeGltfBufferView(const BufferView &bufferView,
7440
0
                                    detail::json &o) {
7441
0
  SerializeNumberProperty("buffer", bufferView.buffer, o);
7442
0
  SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
7443
7444
  // byteStride is optional, minimum allowed is 4
7445
0
  if (bufferView.byteStride >= 4) {
7446
0
    SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
7447
0
  }
7448
  // byteOffset is optional, default is 0
7449
0
  if (bufferView.byteOffset > 0) {
7450
0
    SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
7451
0
  }
7452
  // Target is optional, check if it contains a valid value
7453
0
  if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
7454
0
      bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
7455
0
    SerializeNumberProperty("target", bufferView.target, o);
7456
0
  }
7457
0
  if (bufferView.name.size()) {
7458
0
    SerializeStringProperty("name", bufferView.name, o);
7459
0
  }
7460
7461
0
  SerializeExtrasAndExtensions(bufferView, o);
7462
0
}
7463
7464
static void SerializeGltfImage(const Image &image, const std::string &uri,
7465
0
                               detail::json &o) {
7466
  // From 2.7.0, we look for `uri` parameter, not `Image.uri`
7467
  // if uri is empty, the mimeType and bufferview should be set
7468
0
  if (uri.empty()) {
7469
0
    SerializeStringProperty("mimeType", image.mimeType, o);
7470
0
    SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7471
0
  } else {
7472
0
    SerializeStringProperty("uri", uri, o);
7473
0
  }
7474
7475
0
  if (image.name.size()) {
7476
0
    SerializeStringProperty("name", image.name, o);
7477
0
  }
7478
7479
0
  SerializeExtrasAndExtensions(image, o);
7480
0
}
7481
7482
static void SerializeGltfTextureInfo(const TextureInfo &texinfo,
7483
0
                                     detail::json &o) {
7484
0
  SerializeNumberProperty("index", texinfo.index, o);
7485
7486
0
  if (texinfo.texCoord != 0) {
7487
0
    SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7488
0
  }
7489
7490
0
  SerializeExtrasAndExtensions(texinfo, o);
7491
0
}
7492
7493
static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
7494
0
                                           detail::json &o) {
7495
0
  SerializeNumberProperty("index", texinfo.index, o);
7496
7497
0
  if (texinfo.texCoord != 0) {
7498
0
    SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7499
0
  }
7500
7501
0
  if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
7502
0
    SerializeNumberProperty("scale", texinfo.scale, o);
7503
0
  }
7504
7505
0
  SerializeExtrasAndExtensions(texinfo, o);
7506
0
}
7507
7508
static void SerializeGltfOcclusionTextureInfo(
7509
0
    const OcclusionTextureInfo &texinfo, detail::json &o) {
7510
0
  SerializeNumberProperty("index", texinfo.index, o);
7511
7512
0
  if (texinfo.texCoord != 0) {
7513
0
    SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7514
0
  }
7515
7516
0
  if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
7517
0
    SerializeNumberProperty("strength", texinfo.strength, o);
7518
0
  }
7519
7520
0
  SerializeExtrasAndExtensions(texinfo, o);
7521
0
}
7522
7523
static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
7524
0
                                              detail::json &o) {
7525
0
  std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7526
0
  if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7527
0
    SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7528
0
                                         o);
7529
0
  }
7530
7531
0
  if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
7532
0
    SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7533
0
  }
7534
7535
0
  if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
7536
0
    SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7537
0
  }
7538
7539
0
  if (pbr.baseColorTexture.index > -1) {
7540
0
    detail::json texinfo;
7541
0
    SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
7542
0
    detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo));
7543
0
  }
7544
7545
0
  if (pbr.metallicRoughnessTexture.index > -1) {
7546
0
    detail::json texinfo;
7547
0
    SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
7548
0
    detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
7549
0
  }
7550
7551
0
  SerializeExtrasAndExtensions(pbr, o);
7552
0
}
7553
7554
0
static void SerializeGltfMaterial(const Material &material, detail::json &o) {
7555
0
  if (material.name.size()) {
7556
0
    SerializeStringProperty("name", material.name, o);
7557
0
  }
7558
7559
  // QUESTION(syoyo): Write material parameters regardless of its default value?
7560
7561
0
  if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7562
0
    SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7563
0
  }
7564
7565
0
  if (material.alphaMode.compare("OPAQUE") != 0) {
7566
0
    SerializeStringProperty("alphaMode", material.alphaMode, o);
7567
0
  }
7568
7569
0
  if (material.doubleSided != false)
7570
0
    detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided));
7571
7572
0
  if (material.normalTexture.index > -1) {
7573
0
    detail::json texinfo;
7574
0
    SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
7575
0
    detail::JsonAddMember(o, "normalTexture", std::move(texinfo));
7576
0
  }
7577
7578
0
  if (material.occlusionTexture.index > -1) {
7579
0
    detail::json texinfo;
7580
0
    SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
7581
0
    detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo));
7582
0
  }
7583
7584
0
  if (material.emissiveTexture.index > -1) {
7585
0
    detail::json texinfo;
7586
0
    SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
7587
0
    detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo));
7588
0
  }
7589
7590
0
  std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7591
0
  if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7592
0
    SerializeNumberArrayProperty<double>("emissiveFactor",
7593
0
                                         material.emissiveFactor, o);
7594
0
  }
7595
7596
0
  {
7597
0
    detail::json pbrMetallicRoughness;
7598
0
    SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7599
0
                                      pbrMetallicRoughness);
7600
    // Issue 204
7601
    // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7602
    // default values(json is null). Otherwise it will serialize to
7603
    // `pbrMetallicRoughness : null`, which cannot be read by other glTF
7604
    // importers (and validators).
7605
    //
7606
0
    if (!detail::JsonIsNull(pbrMetallicRoughness)) {
7607
0
      detail::JsonAddMember(o, "pbrMetallicRoughness",
7608
0
                            std::move(pbrMetallicRoughness));
7609
0
    }
7610
0
  }
7611
7612
#if 0  // legacy way. just for the record.
7613
  if (material.values.size()) {
7614
    detail::json pbrMetallicRoughness;
7615
    SerializeParameterMap(material.values, pbrMetallicRoughness);
7616
    detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7617
  }
7618
7619
  SerializeParameterMap(material.additionalValues, o);
7620
#endif
7621
7622
0
  SerializeExtrasAndExtensions(material, o);
7623
7624
  // MSFT_lod
7625
0
  if (!material.lods.empty()) {
7626
0
    detail::json_iterator it;
7627
0
    if (!detail::FindMember(o, "extensions", it)) {
7628
0
      detail::json extensions;
7629
0
      detail::JsonSetObject(extensions);
7630
0
      detail::JsonAddMember(o, "extensions", std::move(extensions));
7631
0
      detail::FindMember(o, "extensions", it);
7632
0
    }
7633
0
    auto &extensions = detail::GetValue(it);
7634
0
    if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7635
0
      detail::json lod;
7636
0
      detail::JsonSetObject(lod);
7637
0
      detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7638
0
      detail::FindMember(extensions, "MSFT_lod", it);
7639
0
    }
7640
0
    SerializeNumberArrayProperty<int>("ids", material.lods, detail::GetValue(it));
7641
0
  } else {
7642
0
    detail::json_iterator ext_it;
7643
0
    if (detail::FindMember(o, "extensions", ext_it)) {
7644
0
      auto &extensions = detail::GetValue(ext_it);
7645
0
      detail::json_iterator lp_it;
7646
0
      if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7647
0
        detail::Erase(extensions, lp_it);
7648
0
      }
7649
0
      if (detail::IsEmpty(extensions)) {
7650
0
        detail::Erase(o, ext_it);
7651
0
      }
7652
0
    }
7653
0
  }
7654
0
}
7655
7656
0
static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
7657
0
  detail::json primitives;
7658
0
  detail::JsonReserveArray(primitives, mesh.primitives.size());
7659
0
  for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
7660
0
    detail::json primitive;
7661
0
    const Primitive &gltfPrimitive = mesh.primitives[i];  // don't make a copy
7662
0
    {
7663
0
      detail::json attributes;
7664
0
      for (auto attrIt = gltfPrimitive.attributes.begin();
7665
0
           attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
7666
0
        SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7667
0
      }
7668
7669
0
      detail::JsonAddMember(primitive, "attributes", std::move(attributes));
7670
0
    }
7671
7672
    // Indices is optional
7673
0
    if (gltfPrimitive.indices > -1) {
7674
0
      SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7675
0
    }
7676
    // Material is optional
7677
0
    if (gltfPrimitive.material > -1) {
7678
0
      SerializeNumberProperty<int>("material", gltfPrimitive.material,
7679
0
                                   primitive);
7680
0
    }
7681
0
    SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
7682
7683
    // Morph targets
7684
0
    if (gltfPrimitive.targets.size()) {
7685
0
      detail::json targets;
7686
0
      detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
7687
0
      for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
7688
0
        detail::json targetAttributes;
7689
0
        std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7690
0
        for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7691
0
             attrIt != targetData.end(); ++attrIt) {
7692
0
          SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7693
0
                                       targetAttributes);
7694
0
        }
7695
0
        detail::JsonPushBack(targets, std::move(targetAttributes));
7696
0
      }
7697
0
      detail::JsonAddMember(primitive, "targets", std::move(targets));
7698
0
    }
7699
7700
0
    SerializeExtrasAndExtensions(gltfPrimitive, primitive);
7701
7702
0
    detail::JsonPushBack(primitives, std::move(primitive));
7703
0
  }
7704
7705
0
  detail::JsonAddMember(o, "primitives", std::move(primitives));
7706
7707
0
  if (mesh.weights.size()) {
7708
0
    SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7709
0
  }
7710
7711
0
  if (mesh.name.size()) {
7712
0
    SerializeStringProperty("name", mesh.name, o);
7713
0
  }
7714
7715
0
  SerializeExtrasAndExtensions(mesh, o);
7716
0
}
7717
7718
0
static void SerializeSpotLight(const SpotLight &spot, detail::json &o) {
7719
0
  SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7720
0
  SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7721
0
  SerializeExtrasAndExtensions(spot, o);
7722
0
}
7723
7724
0
static void SerializeGltfLight(const Light &light, detail::json &o) {
7725
0
  if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
7726
0
  SerializeNumberProperty("intensity", light.intensity, o);
7727
0
  if (light.range > 0.0) {
7728
0
    SerializeNumberProperty("range", light.range, o);
7729
0
  }
7730
0
  SerializeNumberArrayProperty("color", light.color, o);
7731
0
  SerializeStringProperty("type", light.type, o);
7732
0
  if (light.type == "spot") {
7733
0
    detail::json spot;
7734
0
    SerializeSpotLight(light.spot, spot);
7735
0
    detail::JsonAddMember(o, "spot", std::move(spot));
7736
0
  }
7737
0
  SerializeExtrasAndExtensions(light, o);
7738
0
}
7739
7740
static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional,
7741
0
                                           detail::json &o) {
7742
0
  if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586))
7743
0
    SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o);
7744
0
  if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586))
7745
0
    SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o);
7746
0
  if (positional.coneOuterGain > 0.0)
7747
0
    SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o);
7748
0
  if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0))
7749
0
    SerializeNumberProperty("maxDistance", positional.maxDistance, o);
7750
0
  if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0))
7751
0
    SerializeNumberProperty("refDistance", positional.refDistance, o);
7752
0
  if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0))
7753
0
    SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o);
7754
7755
0
  SerializeExtrasAndExtensions(positional, o);
7756
0
}
7757
7758
static void SerializeGltfAudioEmitter(const AudioEmitter &emitter,
7759
0
                                      detail::json &o) {
7760
0
  if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o);
7761
0
  if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0))
7762
0
    SerializeNumberProperty("gain", emitter.gain, o);
7763
0
  if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o);
7764
0
  if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o);
7765
0
  if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o);
7766
0
  if (!emitter.distanceModel.empty())
7767
0
    SerializeStringProperty("distanceModel", emitter.distanceModel, o);
7768
0
  if (emitter.type == "positional") {
7769
0
    detail::json positional;
7770
0
    SerializeGltfPositionalEmitter(emitter.positional, positional);
7771
0
    detail::JsonAddMember(o, "positional", std::move(positional));
7772
0
  }
7773
0
  SerializeNumberProperty("source", emitter.source, o);
7774
0
  SerializeExtrasAndExtensions(emitter, o);
7775
0
}
7776
7777
static void SerializeGltfAudioSource(const AudioSource &source,
7778
0
                                     detail::json &o) {
7779
0
  std::string name;
7780
0
  std::string uri;
7781
0
  std::string mimeType;  // (required if no uri) ["audio/mp3", "audio/ogg",
7782
                         // "audio/wav", "audio/m4a"]
7783
7784
0
  if (!source.name.empty()) SerializeStringProperty("name", source.name, o);
7785
0
  if (source.uri.empty()) {
7786
0
    SerializeStringProperty("mimeType", source.mimeType, o);
7787
0
    SerializeNumberProperty<int>("bufferView", source.bufferView, o);
7788
0
  } else {
7789
0
    SerializeStringProperty("uri", source.uri, o);
7790
0
  }
7791
0
  SerializeExtrasAndExtensions(source, o);
7792
0
}
7793
7794
0
static void SerializeGltfNode(const Node &node, detail::json &o) {
7795
0
  if (node.translation.size() > 0) {
7796
0
    SerializeNumberArrayProperty<double>("translation", node.translation, o);
7797
0
  }
7798
0
  if (node.rotation.size() > 0) {
7799
0
    SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7800
0
  }
7801
0
  if (node.scale.size() > 0) {
7802
0
    SerializeNumberArrayProperty<double>("scale", node.scale, o);
7803
0
  }
7804
0
  if (node.matrix.size() > 0) {
7805
0
    SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7806
0
  }
7807
0
  if (node.mesh != -1) {
7808
0
    SerializeNumberProperty<int>("mesh", node.mesh, o);
7809
0
  }
7810
7811
0
  if (node.skin != -1) {
7812
0
    SerializeNumberProperty<int>("skin", node.skin, o);
7813
0
  }
7814
7815
0
  if (node.camera != -1) {
7816
0
    SerializeNumberProperty<int>("camera", node.camera, o);
7817
0
  }
7818
7819
0
  if (node.weights.size() > 0) {
7820
0
    SerializeNumberArrayProperty<double>("weights", node.weights, o);
7821
0
  }
7822
7823
0
  SerializeExtrasAndExtensions(node, o);
7824
7825
  // Note(agnat): If the asset was loaded from disk, the node may already
7826
  // contain the KHR_lights_punctual extension. If it was constructed in
7827
  // memory it does not. In any case we update the JSON property using
7828
  // the value from the struct. Last, if the node does not have a light
7829
  // reference but the extension is still present, we remove it.
7830
0
  if (node.light != -1) {
7831
0
    detail::json_iterator it;
7832
0
    if (!detail::FindMember(o, "extensions", it)) {
7833
0
      detail::json extensions;
7834
0
      detail::JsonSetObject(extensions);
7835
0
      detail::JsonAddMember(o, "extensions", std::move(extensions));
7836
0
      detail::FindMember(o, "extensions", it);
7837
0
    }
7838
0
    auto &extensions = detail::GetValue(it);
7839
0
    if (!detail::FindMember(extensions, "KHR_lights_punctual", it)) {
7840
0
      detail::json lights_punctual;
7841
0
      detail::JsonSetObject(lights_punctual);
7842
0
      detail::JsonAddMember(extensions, "KHR_lights_punctual",
7843
0
                            std::move(lights_punctual));
7844
0
      detail::FindMember(extensions, "KHR_lights_punctual", it);
7845
0
    }
7846
0
    SerializeNumberProperty("light", node.light, detail::GetValue(it));
7847
0
  } else {
7848
    // node has no light ref (any longer)... so we clean up
7849
0
    detail::json_iterator ext_it;
7850
0
    if (detail::FindMember(o, "extensions", ext_it)) {
7851
0
      auto &extensions = detail::GetValue(ext_it);
7852
0
      detail::json_iterator lp_it;
7853
0
      if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) {
7854
0
        detail::Erase(extensions, lp_it);
7855
0
      }
7856
0
      if (detail::IsEmpty(extensions)) {
7857
0
        detail::Erase(o, ext_it);
7858
0
      }
7859
0
    }
7860
0
  }
7861
7862
  // KHR_audio
7863
0
  if (node.emitter != -1) {
7864
0
    detail::json_iterator it;
7865
0
    if (!detail::FindMember(o, "extensions", it)) {
7866
0
      detail::json extensions;
7867
0
      detail::JsonSetObject(extensions);
7868
0
      detail::JsonAddMember(o, "extensions", std::move(extensions));
7869
0
      detail::FindMember(o, "extensions", it);
7870
0
    }
7871
0
    auto &extensions = detail::GetValue(it);
7872
0
    if (!detail::FindMember(extensions, "KHR_audio", it)) {
7873
0
      detail::json audio;
7874
0
      detail::JsonSetObject(audio);
7875
0
      detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
7876
0
      detail::FindMember(extensions, "KHR_audio", it);
7877
0
    }
7878
0
    SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it));
7879
0
  } else {
7880
0
    detail::json_iterator ext_it;
7881
0
    if (detail::FindMember(o, "extensions", ext_it)) {
7882
0
      auto &extensions = detail::GetValue(ext_it);
7883
0
      detail::json_iterator lp_it;
7884
0
      if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
7885
0
        detail::Erase(extensions, lp_it);
7886
0
      }
7887
0
      if (detail::IsEmpty(extensions)) {
7888
0
        detail::Erase(o, ext_it);
7889
0
      }
7890
0
    }
7891
0
  }
7892
7893
  // MSFT_lod
7894
0
  if (!node.lods.empty()) {
7895
0
    detail::json_iterator it;
7896
0
    if (!detail::FindMember(o, "extensions", it)) {
7897
0
      detail::json extensions;
7898
0
      detail::JsonSetObject(extensions);
7899
0
      detail::JsonAddMember(o, "extensions", std::move(extensions));
7900
0
      detail::FindMember(o, "extensions", it);
7901
0
    }
7902
0
    auto &extensions = detail::GetValue(it);
7903
0
    if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7904
0
      detail::json lod;
7905
0
      detail::JsonSetObject(lod);
7906
0
      detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7907
0
      detail::FindMember(extensions, "MSFT_lod", it);
7908
0
    }
7909
0
    SerializeNumberArrayProperty<int>("ids", node.lods, detail::GetValue(it));
7910
0
  } else {
7911
0
    detail::json_iterator ext_it;
7912
0
    if (detail::FindMember(o, "extensions", ext_it)) {
7913
0
      auto &extensions = detail::GetValue(ext_it);
7914
0
      detail::json_iterator lp_it;
7915
0
      if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7916
0
        detail::Erase(extensions, lp_it);
7917
0
      }
7918
0
      if (detail::IsEmpty(extensions)) {
7919
0
        detail::Erase(o, ext_it);
7920
0
      }
7921
0
    }
7922
0
  }
7923
7924
0
  if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
7925
0
  SerializeNumberArrayProperty<int>("children", node.children, o);
7926
0
}
7927
7928
0
static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) {
7929
0
  if (!sampler.name.empty()) {
7930
0
    SerializeStringProperty("name", sampler.name, o);
7931
0
  }
7932
0
  if (sampler.magFilter != -1) {
7933
0
    SerializeNumberProperty("magFilter", sampler.magFilter, o);
7934
0
  }
7935
0
  if (sampler.minFilter != -1) {
7936
0
    SerializeNumberProperty("minFilter", sampler.minFilter, o);
7937
0
  }
7938
  // SerializeNumberProperty("wrapR", sampler.wrapR, o);
7939
0
  SerializeNumberProperty("wrapS", sampler.wrapS, o);
7940
0
  SerializeNumberProperty("wrapT", sampler.wrapT, o);
7941
7942
0
  SerializeExtrasAndExtensions(sampler, o);
7943
0
}
7944
7945
static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
7946
0
                                            detail::json &o) {
7947
0
  SerializeNumberProperty("zfar", camera.zfar, o);
7948
0
  SerializeNumberProperty("znear", camera.znear, o);
7949
0
  SerializeNumberProperty("xmag", camera.xmag, o);
7950
0
  SerializeNumberProperty("ymag", camera.ymag, o);
7951
7952
0
  SerializeExtrasAndExtensions(camera, o);
7953
0
}
7954
7955
static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
7956
0
                                           detail::json &o) {
7957
0
  SerializeNumberProperty("zfar", camera.zfar, o);
7958
0
  SerializeNumberProperty("znear", camera.znear, o);
7959
0
  if (camera.aspectRatio > 0) {
7960
0
    SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7961
0
  }
7962
7963
0
  if (camera.yfov > 0) {
7964
0
    SerializeNumberProperty("yfov", camera.yfov, o);
7965
0
  }
7966
7967
0
  SerializeExtrasAndExtensions(camera, o);
7968
0
}
7969
7970
0
static void SerializeGltfCamera(const Camera &camera, detail::json &o) {
7971
0
  SerializeStringProperty("type", camera.type, o);
7972
0
  if (!camera.name.empty()) {
7973
0
    SerializeStringProperty("name", camera.name, o);
7974
0
  }
7975
7976
0
  if (camera.type.compare("orthographic") == 0) {
7977
0
    detail::json orthographic;
7978
0
    SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
7979
0
    detail::JsonAddMember(o, "orthographic", std::move(orthographic));
7980
0
  } else if (camera.type.compare("perspective") == 0) {
7981
0
    detail::json perspective;
7982
0
    SerializeGltfPerspectiveCamera(camera.perspective, perspective);
7983
0
    detail::JsonAddMember(o, "perspective", std::move(perspective));
7984
0
  } else {
7985
    // ???
7986
0
  }
7987
7988
0
  SerializeExtrasAndExtensions(camera, o);
7989
0
}
7990
7991
0
static void SerializeGltfScene(const Scene &scene, detail::json &o) {
7992
0
  SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7993
7994
0
  if (scene.name.size()) {
7995
0
    SerializeStringProperty("name", scene.name, o);
7996
0
  }
7997
0
  SerializeExtrasAndExtensions(scene, o);
7998
7999
  // KHR_audio
8000
0
  if (!scene.audioEmitters.empty()) {
8001
0
    detail::json_iterator it;
8002
0
    if (!detail::FindMember(o, "extensions", it)) {
8003
0
      detail::json extensions;
8004
0
      detail::JsonSetObject(extensions);
8005
0
      detail::JsonAddMember(o, "extensions", std::move(extensions));
8006
0
      detail::FindMember(o, "extensions", it);
8007
0
    }
8008
0
    auto &extensions = detail::GetValue(it);
8009
0
    if (!detail::FindMember(extensions, "KHR_audio", it)) {
8010
0
      detail::json audio;
8011
0
      detail::JsonSetObject(audio);
8012
0
      detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
8013
0
      detail::FindMember(o, "KHR_audio", it);
8014
0
    }
8015
0
    SerializeNumberArrayProperty("emitters", scene.audioEmitters,
8016
0
                                 detail::GetValue(it));
8017
0
  } else {
8018
0
    detail::json_iterator ext_it;
8019
0
    if (detail::FindMember(o, "extensions", ext_it)) {
8020
0
      auto &extensions = detail::GetValue(ext_it);
8021
0
      detail::json_iterator lp_it;
8022
0
      if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
8023
0
        detail::Erase(extensions, lp_it);
8024
0
      }
8025
0
      if (detail::IsEmpty(extensions)) {
8026
0
        detail::Erase(o, ext_it);
8027
0
      }
8028
0
    }
8029
0
  }
8030
0
}
8031
8032
0
static void SerializeGltfSkin(const Skin &skin, detail::json &o) {
8033
  // required
8034
0
  SerializeNumberArrayProperty<int>("joints", skin.joints, o);
8035
8036
0
  if (skin.inverseBindMatrices >= 0) {
8037
0
    SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
8038
0
  }
8039
8040
0
  if (skin.skeleton >= 0) {
8041
0
    SerializeNumberProperty("skeleton", skin.skeleton, o);
8042
0
  }
8043
8044
0
  if (skin.name.size()) {
8045
0
    SerializeStringProperty("name", skin.name, o);
8046
0
  }
8047
8048
0
  SerializeExtrasAndExtensions(skin, o);
8049
0
}
8050
8051
0
static void SerializeGltfTexture(const Texture &texture, detail::json &o) {
8052
0
  if (texture.sampler > -1) {
8053
0
    SerializeNumberProperty("sampler", texture.sampler, o);
8054
0
  }
8055
0
  if (texture.source > -1) {
8056
0
    SerializeNumberProperty("source", texture.source, o);
8057
0
  }
8058
0
  if (texture.name.size()) {
8059
0
    SerializeStringProperty("name", texture.name, o);
8060
0
  }
8061
0
  SerializeExtrasAndExtensions(texture, o);
8062
0
}
8063
8064
///
8065
/// Serialize all properties except buffers and images.
8066
///
8067
0
static void SerializeGltfModel(const Model *model, detail::json &o) {
8068
  // ACCESSORS
8069
0
  if (model->accessors.size()) {
8070
0
    detail::json accessors;
8071
0
    detail::JsonReserveArray(accessors, model->accessors.size());
8072
0
    for (unsigned int i = 0; i < model->accessors.size(); ++i) {
8073
0
      detail::json accessor;
8074
0
      SerializeGltfAccessor(model->accessors[i], accessor);
8075
0
      detail::JsonPushBack(accessors, std::move(accessor));
8076
0
    }
8077
0
    detail::JsonAddMember(o, "accessors", std::move(accessors));
8078
0
  }
8079
8080
  // ANIMATIONS
8081
0
  if (model->animations.size()) {
8082
0
    detail::json animations;
8083
0
    detail::JsonReserveArray(animations, model->animations.size());
8084
0
    for (unsigned int i = 0; i < model->animations.size(); ++i) {
8085
0
      if (model->animations[i].channels.size()) {
8086
0
        detail::json animation;
8087
0
        SerializeGltfAnimation(model->animations[i], animation);
8088
0
        detail::JsonPushBack(animations, std::move(animation));
8089
0
      }
8090
0
    }
8091
8092
0
    detail::JsonAddMember(o, "animations", std::move(animations));
8093
0
  }
8094
8095
  // ASSET
8096
0
  detail::json asset;
8097
0
  SerializeGltfAsset(model->asset, asset);
8098
0
  detail::JsonAddMember(o, "asset", std::move(asset));
8099
8100
  // BUFFERVIEWS
8101
0
  if (model->bufferViews.size()) {
8102
0
    detail::json bufferViews;
8103
0
    detail::JsonReserveArray(bufferViews, model->bufferViews.size());
8104
0
    for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
8105
0
      detail::json bufferView;
8106
0
      SerializeGltfBufferView(model->bufferViews[i], bufferView);
8107
0
      detail::JsonPushBack(bufferViews, std::move(bufferView));
8108
0
    }
8109
0
    detail::JsonAddMember(o, "bufferViews", std::move(bufferViews));
8110
0
  }
8111
8112
  // Extensions required
8113
0
  if (model->extensionsRequired.size()) {
8114
0
    SerializeStringArrayProperty("extensionsRequired",
8115
0
                                 model->extensionsRequired, o);
8116
0
  }
8117
8118
  // MATERIALS
8119
0
  if (model->materials.size()) {
8120
0
    detail::json materials;
8121
0
    detail::JsonReserveArray(materials, model->materials.size());
8122
0
    for (unsigned int i = 0; i < model->materials.size(); ++i) {
8123
0
      detail::json material;
8124
0
      SerializeGltfMaterial(model->materials[i], material);
8125
8126
0
      if (detail::JsonIsNull(material)) {
8127
        // Issue 294.
8128
        // `material` does not have any required parameters
8129
        // so the result may be null(unmodified) when all material parameters
8130
        // have default value.
8131
        //
8132
        // null is not allowed thus we create an empty JSON object.
8133
0
        detail::JsonSetObject(material);
8134
0
      }
8135
0
      detail::JsonPushBack(materials, std::move(material));
8136
0
    }
8137
0
    detail::JsonAddMember(o, "materials", std::move(materials));
8138
0
  }
8139
8140
  // MESHES
8141
0
  if (model->meshes.size()) {
8142
0
    detail::json meshes;
8143
0
    detail::JsonReserveArray(meshes, model->meshes.size());
8144
0
    for (unsigned int i = 0; i < model->meshes.size(); ++i) {
8145
0
      detail::json mesh;
8146
0
      SerializeGltfMesh(model->meshes[i], mesh);
8147
0
      detail::JsonPushBack(meshes, std::move(mesh));
8148
0
    }
8149
0
    detail::JsonAddMember(o, "meshes", std::move(meshes));
8150
0
  }
8151
8152
  // NODES
8153
0
  if (model->nodes.size()) {
8154
0
    detail::json nodes;
8155
0
    detail::JsonReserveArray(nodes, model->nodes.size());
8156
0
    for (unsigned int i = 0; i < model->nodes.size(); ++i) {
8157
0
      detail::json node;
8158
0
      SerializeGltfNode(model->nodes[i], node);
8159
8160
0
      if (detail::JsonIsNull(node)) {
8161
        // Issue 457.
8162
        // `node` does not have any required parameters,
8163
        // so the result may be null(unmodified) when all node parameters
8164
        // have default value.
8165
        //
8166
        // null is not allowed thus we create an empty JSON object.
8167
0
        detail::JsonSetObject(node);
8168
0
      }
8169
0
      detail::JsonPushBack(nodes, std::move(node));
8170
0
    }
8171
0
    detail::JsonAddMember(o, "nodes", std::move(nodes));
8172
0
  }
8173
8174
  // SCENE
8175
0
  if (model->defaultScene > -1) {
8176
0
    SerializeNumberProperty<int>("scene", model->defaultScene, o);
8177
0
  }
8178
8179
  // SCENES
8180
0
  if (model->scenes.size()) {
8181
0
    detail::json scenes;
8182
0
    detail::JsonReserveArray(scenes, model->scenes.size());
8183
0
    for (unsigned int i = 0; i < model->scenes.size(); ++i) {
8184
0
      detail::json currentScene;
8185
0
      SerializeGltfScene(model->scenes[i], currentScene);
8186
0
      if (detail::JsonIsNull(currentScene)) {
8187
        // Issue 464.
8188
        // `scene` does not have any required parameters,
8189
        // so the result may be null(unmodified) when all scene parameters
8190
        // have default value.
8191
        //
8192
        // null is not allowed thus we create an empty JSON object.
8193
0
        detail::JsonSetObject(currentScene);
8194
0
      }
8195
0
      detail::JsonPushBack(scenes, std::move(currentScene));
8196
0
    }
8197
0
    detail::JsonAddMember(o, "scenes", std::move(scenes));
8198
0
  }
8199
8200
  // SKINS
8201
0
  if (model->skins.size()) {
8202
0
    detail::json skins;
8203
0
    detail::JsonReserveArray(skins, model->skins.size());
8204
0
    for (unsigned int i = 0; i < model->skins.size(); ++i) {
8205
0
      detail::json skin;
8206
0
      SerializeGltfSkin(model->skins[i], skin);
8207
0
      detail::JsonPushBack(skins, std::move(skin));
8208
0
    }
8209
0
    detail::JsonAddMember(o, "skins", std::move(skins));
8210
0
  }
8211
8212
  // TEXTURES
8213
0
  if (model->textures.size()) {
8214
0
    detail::json textures;
8215
0
    detail::JsonReserveArray(textures, model->textures.size());
8216
0
    for (unsigned int i = 0; i < model->textures.size(); ++i) {
8217
0
      detail::json texture;
8218
0
      SerializeGltfTexture(model->textures[i], texture);
8219
0
      detail::JsonPushBack(textures, std::move(texture));
8220
0
    }
8221
0
    detail::JsonAddMember(o, "textures", std::move(textures));
8222
0
  }
8223
8224
  // SAMPLERS
8225
0
  if (model->samplers.size()) {
8226
0
    detail::json samplers;
8227
0
    detail::JsonReserveArray(samplers, model->samplers.size());
8228
0
    for (unsigned int i = 0; i < model->samplers.size(); ++i) {
8229
0
      detail::json sampler;
8230
0
      SerializeGltfSampler(model->samplers[i], sampler);
8231
0
      detail::JsonPushBack(samplers, std::move(sampler));
8232
0
    }
8233
0
    detail::JsonAddMember(o, "samplers", std::move(samplers));
8234
0
  }
8235
8236
  // CAMERAS
8237
0
  if (model->cameras.size()) {
8238
0
    detail::json cameras;
8239
0
    detail::JsonReserveArray(cameras, model->cameras.size());
8240
0
    for (unsigned int i = 0; i < model->cameras.size(); ++i) {
8241
0
      detail::json camera;
8242
0
      SerializeGltfCamera(model->cameras[i], camera);
8243
0
      detail::JsonPushBack(cameras, std::move(camera));
8244
0
    }
8245
0
    detail::JsonAddMember(o, "cameras", std::move(cameras));
8246
0
  }
8247
8248
  // EXTRAS & EXTENSIONS
8249
0
  SerializeExtrasAndExtensions(*model, o);
8250
8251
0
  auto extensionsUsed = model->extensionsUsed;
8252
8253
  // LIGHTS as KHR_lights_punctual
8254
0
  if (model->lights.size()) {
8255
0
    detail::json lights;
8256
0
    detail::JsonReserveArray(lights, model->lights.size());
8257
0
    for (unsigned int i = 0; i < model->lights.size(); ++i) {
8258
0
      detail::json light;
8259
0
      SerializeGltfLight(model->lights[i], light);
8260
0
      detail::JsonPushBack(lights, std::move(light));
8261
0
    }
8262
0
    detail::json khr_lights_cmn;
8263
0
    detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
8264
0
    detail::json ext_j;
8265
8266
0
    {
8267
0
      detail::json_const_iterator it;
8268
0
      if (detail::FindMember(o, "extensions", it)) {
8269
0
        detail::JsonAssign(ext_j, detail::GetValue(it));
8270
0
      }
8271
0
    }
8272
8273
0
    detail::JsonAddMember(ext_j, "KHR_lights_punctual",
8274
0
                          std::move(khr_lights_cmn));
8275
8276
0
    detail::JsonAddMember(o, "extensions", std::move(ext_j));
8277
8278
    // Also add "KHR_lights_punctual" to `extensionsUsed`
8279
0
    {
8280
0
      auto has_khr_lights_punctual =
8281
0
          std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
8282
0
                       [](const std::string &s) {
8283
0
                         return (s.compare("KHR_lights_punctual") == 0);
8284
0
                       });
8285
8286
0
      if (has_khr_lights_punctual == extensionsUsed.end()) {
8287
0
        extensionsUsed.push_back("KHR_lights_punctual");
8288
0
      }
8289
0
    }
8290
0
  }
8291
8292
  // KHR_audio
8293
0
  if (!model->audioEmitters.empty() || !model->audioSources.empty()) {
8294
0
    detail::json emitters;
8295
0
    detail::JsonReserveArray(emitters, model->audioEmitters.size());
8296
0
    for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) {
8297
0
      detail::json emitter;
8298
0
      SerializeGltfAudioEmitter(model->audioEmitters[i], emitter);
8299
0
      detail::JsonPushBack(emitters, std::move(emitter));
8300
0
    }
8301
0
    detail::json khr_audio_cmn;
8302
0
    detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters));
8303
8304
0
    detail::json sources;
8305
0
    detail::JsonReserveArray(sources, model->audioSources.size());
8306
0
    for (unsigned int i = 0; i < model->audioSources.size(); ++i) {
8307
0
      detail::json source;
8308
0
      SerializeGltfAudioSource(model->audioSources[i], source);
8309
0
      detail::JsonPushBack(sources, std::move(source));
8310
0
    }
8311
0
    detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources));
8312
8313
0
    detail::json ext_j;
8314
0
    {
8315
0
      detail::json_const_iterator it;
8316
0
      if (detail::FindMember(o, "extensions", it)) {
8317
0
        detail::JsonAssign(ext_j, detail::GetValue(it));
8318
0
      }
8319
0
    }
8320
8321
0
    detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn));
8322
8323
0
    detail::JsonAddMember(o, "extensions", std::move(ext_j));
8324
8325
    // Also add "KHR_audio" to `extensionsUsed`
8326
0
    {
8327
0
      auto has_khr_audio = std::find_if(
8328
0
          extensionsUsed.begin(), extensionsUsed.end(),
8329
0
          [](const std::string &s) { return (s.compare("KHR_audio") == 0); });
8330
8331
0
      if (has_khr_audio == extensionsUsed.end()) {
8332
0
        extensionsUsed.push_back("KHR_audio");
8333
0
      }
8334
0
    }
8335
0
  }
8336
8337
  // Extensions used
8338
0
  if (extensionsUsed.size()) {
8339
0
    SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
8340
0
  }
8341
0
}
8342
8343
0
static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
8344
0
  stream << content << std::endl;
8345
0
  return stream.good();
8346
0
}
8347
8348
static bool WriteGltfFile(const std::string &output,
8349
0
                          const std::string &content) {
8350
#ifdef _WIN32
8351
#if defined(_MSC_VER)
8352
  std::ofstream gltfFile(UTF8ToWchar(output).c_str());
8353
#elif defined(__GLIBCXX__)
8354
  int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8355
                               _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
8356
  __gnu_cxx::stdio_filebuf<char> wfile_buf(
8357
      file_descriptor, std::ios_base::out | std::ios_base::binary);
8358
  std::ostream gltfFile(&wfile_buf);
8359
  if (!wfile_buf.is_open()) return false;
8360
#else
8361
  std::ofstream gltfFile(output.c_str());
8362
  if (!gltfFile.is_open()) return false;
8363
#endif
8364
#else
8365
0
  std::ofstream gltfFile(output.c_str());
8366
0
  if (!gltfFile.is_open()) return false;
8367
0
#endif
8368
0
  return WriteGltfStream(gltfFile, content);
8369
0
}
8370
8371
static bool WriteBinaryGltfStream(std::ostream &stream,
8372
                                  const std::string &content,
8373
0
                                  const std::vector<unsigned char> &binBuffer) {
8374
0
  const std::string header = "glTF";
8375
0
  const int version = 2;
8376
8377
0
  const uint32_t content_size = uint32_t(content.size());
8378
0
  const uint32_t binBuffer_size = uint32_t(binBuffer.size());
8379
  // determine number of padding bytes required to ensure 4 byte alignment
8380
0
  const uint32_t content_padding_size =
8381
0
      content_size % 4 == 0 ? 0 : 4 - content_size % 4;
8382
0
  const uint32_t bin_padding_size =
8383
0
      binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
8384
8385
  // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
8386
  // Chunk data must be located at 4-byte boundary, which may require padding
8387
0
  const uint32_t length =
8388
0
      12 + 8 + content_size + content_padding_size +
8389
0
      (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
8390
8391
0
  stream.write(header.c_str(), std::streamsize(header.size()));
8392
0
  stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
8393
0
  stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
8394
8395
  // JSON chunk info, then JSON data
8396
0
  const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
8397
0
  const uint32_t model_format = 0x4E4F534A;
8398
0
  stream.write(reinterpret_cast<const char *>(&model_length),
8399
0
               sizeof(model_length));
8400
0
  stream.write(reinterpret_cast<const char *>(&model_format),
8401
0
               sizeof(model_format));
8402
0
  stream.write(content.c_str(), std::streamsize(content.size()));
8403
8404
  // Chunk must be multiplies of 4, so pad with spaces
8405
0
  if (content_padding_size > 0) {
8406
0
    const std::string padding = std::string(size_t(content_padding_size), ' ');
8407
0
    stream.write(padding.c_str(), std::streamsize(padding.size()));
8408
0
  }
8409
0
  if (binBuffer.size() > 0) {
8410
    // BIN chunk info, then BIN data
8411
0
    const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
8412
0
    const uint32_t bin_format = 0x004e4942;
8413
0
    stream.write(reinterpret_cast<const char *>(&bin_length),
8414
0
                 sizeof(bin_length));
8415
0
    stream.write(reinterpret_cast<const char *>(&bin_format),
8416
0
                 sizeof(bin_format));
8417
0
    stream.write(reinterpret_cast<const char *>(binBuffer.data()),
8418
0
                 std::streamsize(binBuffer.size()));
8419
    // Chunksize must be multiplies of 4, so pad with zeroes
8420
0
    if (bin_padding_size > 0) {
8421
0
      const std::vector<unsigned char> padding =
8422
0
          std::vector<unsigned char>(size_t(bin_padding_size), 0);
8423
0
      stream.write(reinterpret_cast<const char *>(padding.data()),
8424
0
                   std::streamsize(padding.size()));
8425
0
    }
8426
0
  }
8427
8428
0
  stream.flush();
8429
0
  return stream.good();
8430
0
}
8431
8432
static bool WriteBinaryGltfFile(const std::string &output,
8433
                                const std::string &content,
8434
0
                                const std::vector<unsigned char> &binBuffer) {
8435
#ifdef _WIN32
8436
#if defined(_MSC_VER)
8437
  std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
8438
#elif defined(__GLIBCXX__)
8439
  int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8440
                               _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
8441
  __gnu_cxx::stdio_filebuf<char> wfile_buf(
8442
      file_descriptor, std::ios_base::out | std::ios_base::binary);
8443
  std::ostream gltfFile(&wfile_buf);
8444
#else
8445
  std::ofstream gltfFile(output.c_str(), std::ios::binary);
8446
#endif
8447
#else
8448
0
  std::ofstream gltfFile(output.c_str(), std::ios::binary);
8449
0
#endif
8450
0
  return WriteBinaryGltfStream(gltfFile, content, binBuffer);
8451
0
}
8452
8453
bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
8454
                                      bool prettyPrint = true,
8455
0
                                      bool writeBinary = false) {
8456
0
  detail::JsonDocument output;
8457
8458
  /// Serialize all properties except buffers and images.
8459
0
  SerializeGltfModel(model, output);
8460
8461
  // BUFFERS
8462
0
  std::vector<unsigned char> binBuffer;
8463
0
  if (model->buffers.size()) {
8464
0
    detail::json buffers;
8465
0
    detail::JsonReserveArray(buffers, model->buffers.size());
8466
0
    for (unsigned int i = 0; i < model->buffers.size(); ++i) {
8467
0
      detail::json buffer;
8468
0
      if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8469
0
        SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
8470
0
      } else {
8471
0
        SerializeGltfBuffer(model->buffers[i], buffer);
8472
0
      }
8473
0
      detail::JsonPushBack(buffers, std::move(buffer));
8474
0
    }
8475
0
    detail::JsonAddMember(output, "buffers", std::move(buffers));
8476
0
  }
8477
8478
  // IMAGES
8479
0
  if (model->images.size()) {
8480
0
    detail::json images;
8481
0
    detail::JsonReserveArray(images, model->images.size());
8482
0
    for (unsigned int i = 0; i < model->images.size(); ++i) {
8483
0
      detail::json image;
8484
8485
0
      std::string dummystring;
8486
      // UpdateImageObject need baseDir but only uses it if embeddedImages is
8487
      // enabled, since we won't write separate images when writing to a stream
8488
      // we
8489
0
      std::string uri;
8490
0
      if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
8491
0
                             &uri_cb, &this->WriteImageData,
8492
0
                             this->write_image_user_data_, &uri)) {
8493
0
        return false;
8494
0
      }
8495
0
      SerializeGltfImage(model->images[i], uri, image);
8496
0
      detail::JsonPushBack(images, std::move(image));
8497
0
    }
8498
0
    detail::JsonAddMember(output, "images", std::move(images));
8499
0
  }
8500
8501
0
  if (writeBinary) {
8502
0
    return WriteBinaryGltfStream(stream, detail::JsonToString(output),
8503
0
                                 binBuffer);
8504
0
  } else {
8505
0
    return WriteGltfStream(stream,
8506
0
                           detail::JsonToString(output, prettyPrint ? 2 : -1));
8507
0
  }
8508
0
}
8509
8510
bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
8511
                                    const std::string &filename,
8512
                                    bool embedImages = false,
8513
                                    bool embedBuffers = false,
8514
                                    bool prettyPrint = true,
8515
0
                                    bool writeBinary = false) {
8516
0
  detail::JsonDocument output;
8517
0
  std::string defaultBinFilename = GetBaseFilename(filename);
8518
0
  std::string defaultBinFileExt = ".bin";
8519
0
  std::string::size_type pos =
8520
0
      defaultBinFilename.rfind('.', defaultBinFilename.length());
8521
8522
0
  if (pos != std::string::npos) {
8523
0
    defaultBinFilename = defaultBinFilename.substr(0, pos);
8524
0
  }
8525
0
  std::string baseDir = GetBaseDir(filename);
8526
0
  if (baseDir.empty()) {
8527
0
    baseDir = "./";
8528
0
  }
8529
  /// Serialize all properties except buffers and images.
8530
0
  SerializeGltfModel(model, output);
8531
8532
  // BUFFERS
8533
0
  std::vector<std::string> usedFilenames;
8534
0
  std::vector<unsigned char> binBuffer;
8535
0
  if (model->buffers.size()) {
8536
0
    detail::json buffers;
8537
0
    detail::JsonReserveArray(buffers, model->buffers.size());
8538
0
    for (unsigned int i = 0; i < model->buffers.size(); ++i) {
8539
0
      detail::json buffer;
8540
0
      if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8541
0
        SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
8542
0
      } else if (embedBuffers) {
8543
0
        SerializeGltfBuffer(model->buffers[i], buffer);
8544
0
      } else {
8545
0
        std::string binSavePath;
8546
0
        std::string binFilename;
8547
0
        std::string binUri;
8548
0
        if (!model->buffers[i].uri.empty() &&
8549
0
            !IsDataURI(model->buffers[i].uri)) {
8550
0
          binUri = model->buffers[i].uri;
8551
0
          if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
8552
0
            return false;
8553
0
          }
8554
0
        } else {
8555
0
          binFilename = defaultBinFilename + defaultBinFileExt;
8556
0
          bool inUse = true;
8557
0
          int numUsed = 0;
8558
0
          while (inUse) {
8559
0
            inUse = false;
8560
0
            for (const std::string &usedName : usedFilenames) {
8561
0
              if (binFilename.compare(usedName) != 0) continue;
8562
0
              inUse = true;
8563
0
              binFilename = defaultBinFilename + std::to_string(numUsed++) +
8564
0
                            defaultBinFileExt;
8565
0
              break;
8566
0
            }
8567
0
          }
8568
8569
0
          if (uri_cb.encode) {
8570
0
            if (!uri_cb.encode(binFilename, "buffer", &binUri,
8571
0
                               uri_cb.user_data)) {
8572
0
              return false;
8573
0
            }
8574
0
          } else {
8575
0
            binUri = binFilename;
8576
0
          }
8577
0
        }
8578
0
        usedFilenames.push_back(binFilename);
8579
0
        binSavePath = JoinPath(baseDir, binFilename);
8580
0
        if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
8581
0
                                 binUri)) {
8582
0
          return false;
8583
0
        }
8584
0
      }
8585
0
      detail::JsonPushBack(buffers, std::move(buffer));
8586
0
    }
8587
0
    detail::JsonAddMember(output, "buffers", std::move(buffers));
8588
0
  }
8589
8590
  // IMAGES
8591
0
  if (model->images.size()) {
8592
0
    detail::json images;
8593
0
    detail::JsonReserveArray(images, model->images.size());
8594
0
    for (unsigned int i = 0; i < model->images.size(); ++i) {
8595
0
      detail::json image;
8596
8597
0
      std::string uri;
8598
0
      if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
8599
0
                             &uri_cb, &this->WriteImageData,
8600
0
                             this->write_image_user_data_, &uri)) {
8601
0
        return false;
8602
0
      }
8603
0
      SerializeGltfImage(model->images[i], uri, image);
8604
0
      detail::JsonPushBack(images, std::move(image));
8605
0
    }
8606
0
    detail::JsonAddMember(output, "images", std::move(images));
8607
0
  }
8608
8609
0
  if (writeBinary) {
8610
0
    return WriteBinaryGltfFile(filename, detail::JsonToString(output),
8611
0
                               binBuffer);
8612
0
  } else {
8613
0
    return WriteGltfFile(filename,
8614
0
                         detail::JsonToString(output, (prettyPrint ? 2 : -1)));
8615
0
  }
8616
0
}
8617
8618
}  // namespace tinygltf
8619
8620
#ifdef __clang__
8621
#pragma clang diagnostic pop
8622
#endif
8623
8624
#endif  // TINYGLTF_IMPLEMENTATION