Coverage Report

Created: 2025-07-11 06:51

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