Coverage Report

Created: 2023-11-19 06:39

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