Coverage Report

Created: 2025-08-26 06:47

/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
13.0M
  ~x() = default;                      \
tinygltf::Value::~Value()
Line
Count
Source
70
8.66M
  ~x() = default;                      \
tinygltf::Scene::~Scene()
Line
Count
Source
70
116k
  ~x() = default;                      \
tinygltf::Camera::~Camera()
Line
Count
Source
70
11
  ~x() = default;                      \
tinygltf::OrthographicCamera::~OrthographicCamera()
Line
Count
Source
70
11
  ~x() = default;                      \
tinygltf::PerspectiveCamera::~PerspectiveCamera()
Line
Count
Source
70
11
  ~x() = default;                      \
tinygltf::Sampler::~Sampler()
Line
Count
Source
70
10.1k
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::Skin::~Skin()
tinygltf::Image::~Image()
Line
Count
Source
70
191k
  ~x() = default;                      \
tinygltf::Texture::~Texture()
Line
Count
Source
70
6.04k
  ~x() = default;                      \
tinygltf::Node::~Node()
Line
Count
Source
70
34.1k
  ~x() = default;                      \
tinygltf::Mesh::~Mesh()
Line
Count
Source
70
35.1k
  ~x() = default;                      \
tinygltf::Primitive::~Primitive()
Line
Count
Source
70
150k
  ~x() = default;                      \
tinygltf::Material::~Material()
Line
Count
Source
70
190k
  ~x() = default;                      \
tinygltf::Parameter::~Parameter()
Line
Count
Source
70
63.9k
  ~x() = default;                      \
tinygltf::TextureInfo::~TextureInfo()
Line
Count
Source
70
570k
  ~x() = default;                      \
tinygltf::OcclusionTextureInfo::~OcclusionTextureInfo()
Line
Count
Source
70
190k
  ~x() = default;                      \
tinygltf::NormalTextureInfo::~NormalTextureInfo()
Line
Count
Source
70
190k
  ~x() = default;                      \
tinygltf::PbrMetallicRoughness::~PbrMetallicRoughness()
Line
Count
Source
70
190k
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::BufferView::~BufferView()
Unexecuted instantiation: tinygltf::Buffer::~Buffer()
tinygltf::Animation::~Animation()
Line
Count
Source
70
147k
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::AnimationSampler::~AnimationSampler()
tinygltf::AnimationChannel::~AnimationChannel()
Line
Count
Source
70
9.92k
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::Accessor::~Accessor()
tinygltf::Model::~Model()
Line
Count
Source
70
10.0k
  ~x() = default;                      \
tinygltf::Asset::~Asset()
Line
Count
Source
70
10.0k
  ~x() = default;                      \
Unexecuted instantiation: tinygltf::Light::~Light()
Unexecuted instantiation: tinygltf::SpotLight::~SpotLight()
Unexecuted instantiation: tinygltf::AudioEmitter::~AudioEmitter()
Unexecuted instantiation: tinygltf::PositionalEmitter::~PositionalEmitter()
tinygltf::AudioSource::~AudioSource()
Line
Count
Source
70
2.28M
  ~x() = default;                      \
71
283k
  x(const x &) = default;              \
tinygltf::Value::Value(tinygltf::Value const&)
Line
Count
Source
71
283k
  x(const x &) = default;              \
Unexecuted instantiation: tinygltf::AnimationChannel::AnimationChannel(tinygltf::AnimationChannel const&)
Unexecuted instantiation: tinygltf::AnimationSampler::AnimationSampler(tinygltf::AnimationSampler const&)
72
7.23M
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Value::Value(tinygltf::Value&&)
Line
Count
Source
72
4.09M
  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
108k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Mesh::Mesh(tinygltf::Mesh&&)
Line
Count
Source
72
25.2k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Node::Node(tinygltf::Node&&)
Line
Count
Source
72
24.1k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Scene::Scene(tinygltf::Scene&&)
Line
Count
Source
72
86.5k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Parameter::Parameter(tinygltf::Parameter&&)
Line
Count
Source
72
28.8k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Material::Material(tinygltf::Material&&)
Line
Count
Source
72
139k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::PbrMetallicRoughness::PbrMetallicRoughness(tinygltf::PbrMetallicRoughness&&)
Line
Count
Source
72
139k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::NormalTextureInfo::NormalTextureInfo(tinygltf::NormalTextureInfo&&)
Line
Count
Source
72
139k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::OcclusionTextureInfo::OcclusionTextureInfo(tinygltf::OcclusionTextureInfo&&)
Line
Count
Source
72
139k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::TextureInfo::TextureInfo(tinygltf::TextureInfo&&)
Line
Count
Source
72
418k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Image::Image(tinygltf::Image&&)
Line
Count
Source
72
137k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::Texture::Texture(tinygltf::Texture&&)
Line
Count
Source
72
4.33k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
tinygltf::AnimationChannel::AnimationChannel(tinygltf::AnimationChannel&&)
Line
Count
Source
72
5.50k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
Unexecuted instantiation: tinygltf::AnimationSampler::AnimationSampler(tinygltf::AnimationSampler&&)
tinygltf::Animation::Animation(tinygltf::Animation&&)
Line
Count
Source
72
109k
  x(x &&) TINYGLTF_NOEXCEPT = default; \
Unexecuted instantiation: tinygltf::Skin::Skin(tinygltf::Skin&&)
tinygltf::Sampler::Sampler(tinygltf::Sampler&&)
Line
Count
Source
72
7.40k
  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.63M
  x(x &&) TINYGLTF_NOEXCEPT = default; \
73
  x &operator=(const x &) = default;   \
74
2.05M
  x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
tinygltf::Model::operator=(tinygltf::Model&&)
Line
Count
Source
74
4.64k
  x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
tinygltf::Asset::operator=(tinygltf::Asset&&)
Line
Count
Source
74
4.64k
  x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
tinygltf::Value::operator=(tinygltf::Value&&)
Line
Count
Source
74
2.05M
  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
41.7k
#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
41.0k
#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
88
#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
89
1.17k
#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
5.51k
#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
3.29M
  Value() = default;
262
263
184
  explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
264
939k
  explicit Value(int i) : type_(INT_TYPE) {
265
939k
    int_value_ = i;
266
939k
    real_value_ = i;
267
939k
  }
268
4.62k
  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
17.0k
      : 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.91k
  explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
284
1.91k
                                       array_value_(std::move(a)) {}
285
286
0
  explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
287
38.6k
  explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
288
38.6k
                                        object_value_(std::move(o)) {}
289
290
  DEFAULT_METHODS(Value)
291
292
2.04M
  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
540k
  bool IsArray() const { return (type_ == ARRAY_TYPE); }
307
308
6.12k
  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
272k
  int GetNumberAsInt() const {
322
272k
    if (type_ == REAL_TYPE) {
323
888
      return int(real_value_);
324
271k
    } else {
325
271k
      return int_value_;
326
271k
    }
327
272k
  }
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
269k
  const Value &Get(size_t idx) const {
337
269k
    static Value null_value;
338
269k
    assert(IsArray());
339
269k
    return (idx < array_value_.size())
340
269k
               ? array_value_[idx]
341
269k
               : null_value;
342
269k
  }
343
344
  // Lookup value from a key-value pair
345
3.03k
  const Value &Get(const std::string &key) const {
346
3.03k
    static Value null_value;
347
3.03k
    assert(IsObject());
348
3.03k
    Object::const_iterator it = object_value_.find(key);
349
3.03k
    return (it != object_value_.end()) ? it->second : null_value;
350
3.03k
  }
351
352
270k
  size_t ArrayLen() const {
353
270k
    if (!IsArray()) return 0;
354
270k
    return array_value_.size();
355
270k
  }
356
357
  // Valid only for object type.
358
3.09k
  bool Has(const std::string &key) const {
359
3.09k
    if (!IsObject()) return false;
360
3.09k
    Object::const_iterator it = object_value_.find(key);
361
3.09k
    return (it != object_value_.end()) ? true : false;
362
3.09k
  }
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
35.0k
  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.42k
  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
0
  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
37.5k
  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.75k
  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
53.6k
  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.71k
  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
152k
  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
50.6k
  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
50.6k
  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
50.6k
  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
50.6k
  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
11
  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
11
  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
11
  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
41.7k
  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
9.90k
  Mesh() = default;
992
  DEFAULT_METHODS(Mesh)
993
  bool operator==(const Mesh &) const;
994
};
995
996
class Node {
997
 public:
998
9.94k
  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
10.0k
  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
30.0k
  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
0
  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
0
  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
656k
  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
10.0k
  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
5.35k
  TinyGLTF() = default;
1399
1400
#ifdef __clang__
1401
#pragma clang diagnostic pop
1402
#endif
1403
1404
5.35k
  ~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
5.35k
               bool throwExc = false) {
1876
#ifdef TINYGLTF_USE_RAPIDJSON
1877
  (void)throwExc;
1878
  doc.Parse(str, length);
1879
#else
1880
5.35k
  doc = detail::json::parse(str, str + length, nullptr, throwExc);
1881
5.35k
#endif
1882
5.35k
}
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
58.2k
                            const std::string &path1) {
2208
58.2k
  if (path0.empty()) {
2209
29.1k
    return path1;
2210
29.1k
  } else {
2211
    // check '/'
2212
29.1k
    char lastChar = *path0.rbegin();
2213
29.1k
    if (lastChar != '/') {
2214
29.1k
      return path0 + std::string("/") + path1;
2215
29.1k
    } else {
2216
0
      return path0 + path1;
2217
0
    }
2218
29.1k
  }
2219
58.2k
}
2220
2221
static std::string FindFile(const std::vector<std::string> &paths,
2222
31.3k
                            const std::string &filepath, FsCallbacks *fs) {
2223
31.3k
  if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2224
31.3k
      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
31.3k
  size_t slength = strlen(filepath.c_str());
2234
31.3k
  if (slength == 0) {
2235
2.22k
    return std::string();
2236
2.22k
  }
2237
2238
29.1k
  std::string cleaned_filepath = std::string(filepath.c_str());
2239
2240
87.4k
  for (size_t i = 0; i < paths.size(); i++) {
2241
58.2k
    std::string absPath =
2242
58.2k
        fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data);
2243
58.2k
    if (fs->FileExists(absPath, fs->user_data)) {
2244
0
      return absPath;
2245
0
    }
2246
58.2k
  }
2247
2248
29.1k
  return std::string();
2249
29.1k
}
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
3.42M
static inline bool is_base64(unsigned char c) {
2306
3.42M
  return (isalnum(c) || (c == '+') || (c == '/'));
2307
3.42M
}
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
22.2k
std::string base64_decode(std::string const &encoded_string) {
2355
22.2k
  int in_len = static_cast<int>(encoded_string.size());
2356
22.2k
  int i = 0;
2357
22.2k
  int j = 0;
2358
22.2k
  int in_ = 0;
2359
22.2k
  unsigned char char_array_4[4], char_array_3[3];
2360
22.2k
  std::string ret;
2361
2362
22.2k
  const std::string base64_chars =
2363
22.2k
      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2364
22.2k
      "abcdefghijklmnopqrstuvwxyz"
2365
22.2k
      "0123456789+/";
2366
2367
3.43M
  while (in_len-- && (encoded_string[in_] != '=') &&
2368
3.43M
         is_base64(encoded_string[in_])) {
2369
3.41M
    char_array_4[i++] = encoded_string[in_];
2370
3.41M
    in_++;
2371
3.41M
    if (i == 4) {
2372
4.22M
      for (i = 0; i < 4; i++)
2373
3.37M
        char_array_4[i] =
2374
3.37M
            static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
2375
2376
844k
      char_array_3[0] =
2377
844k
          (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2378
844k
      char_array_3[1] =
2379
844k
          ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2380
844k
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2381
2382
3.37M
      for (i = 0; (i < 3); i++) ret += char_array_3[i];
2383
844k
      i = 0;
2384
844k
    }
2385
3.41M
  }
2386
2387
22.2k
  if (i) {
2388
47.8k
    for (j = i; j < 4; j++) char_array_4[j] = 0;
2389
2390
86.3k
    for (j = 0; j < 4; j++)
2391
69.1k
      char_array_4[j] =
2392
69.1k
          static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
2393
2394
17.2k
    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2395
17.2k
    char_array_3[1] =
2396
17.2k
        ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2397
17.2k
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2398
2399
38.4k
    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
2400
17.2k
  }
2401
2402
22.2k
  return ret;
2403
22.2k
}
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
83.1k
inline unsigned char from_hex(unsigned char ch) {
2442
83.1k
  if (ch <= '9' && ch >= '0')
2443
1.44k
    ch -= '0';
2444
81.7k
  else if (ch <= 'f' && ch >= 'a')
2445
36.5k
    ch -= 'a' - 10;
2446
45.1k
  else if (ch <= 'F' && ch >= 'A')
2447
1.78k
    ch -= 'A' - 10;
2448
43.3k
  else
2449
43.3k
    ch = 0;
2450
83.1k
  return ch;
2451
83.1k
}
2452
2453
31.3k
static const std::string urldecode(const std::string &str) {
2454
31.3k
  using namespace std;
2455
31.3k
  string result;
2456
31.3k
  string::size_type i;
2457
9.08M
  for (i = 0; i < str.size(); ++i) {
2458
9.05M
    if (str[i] == '+') {
2459
49.1k
      result += ' ';
2460
9.00M
    } else if (str[i] == '%' && str.size() > i + 2) {
2461
41.5k
      const unsigned char ch1 =
2462
41.5k
          from_hex(static_cast<unsigned char>(str[i + 1]));
2463
41.5k
      const unsigned char ch2 =
2464
41.5k
          from_hex(static_cast<unsigned char>(str[i + 2]));
2465
41.5k
      const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2466
41.5k
      result += static_cast<char>(ch);
2467
41.5k
      i += 2;
2468
8.95M
    } else {
2469
8.95M
      result += str[i];
2470
8.95M
    }
2471
9.05M
  }
2472
31.3k
  return result;
2473
31.3k
}
2474
2475
}  // namespace dlib
2476
// --- dlib end --------------------------------------------------------------
2477
2478
bool URIDecode(const std::string &in_uri, std::string *out_uri,
2479
31.3k
               void *user_data) {
2480
31.3k
  (void)user_data;
2481
31.3k
  *out_uri = dlib::urldecode(in_uri);
2482
31.3k
  return true;
2483
31.3k
}
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
31.3k
                             size_t maxFileSize, FsCallbacks *fs) {
2490
31.3k
  if (fs == nullptr || fs->FileExists == nullptr ||
2491
31.3k
      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
31.3k
  std::string *failMsgOut = required ? err : warn;
2500
2501
31.3k
  out->clear();
2502
2503
31.3k
  std::vector<std::string> paths;
2504
31.3k
  paths.push_back(basedir);
2505
31.3k
  paths.push_back(".");
2506
2507
31.3k
  std::string filepath = FindFile(paths, filename, fs);
2508
31.3k
  if (filepath.empty() || filename.empty()) {
2509
31.3k
    if (failMsgOut) {
2510
31.3k
      (*failMsgOut) += "File not found : " + filename + "\n";
2511
31.3k
    }
2512
31.3k
    return false;
2513
31.3k
  }
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
22.0k
                   const unsigned char *bytes, int size, void *user_data) {
2610
22.0k
  (void)warn;
2611
2612
22.0k
  LoadImageDataOption option;
2613
22.0k
  if (user_data) {
2614
22.0k
    option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2615
22.0k
  }
2616
2617
22.0k
  int w = 0, h = 0, comp = 0, req_comp = 0;
2618
2619
  // Try to decode image header
2620
22.0k
  if (!stbi_info_from_memory(bytes, size, &w, &h, &comp)) {
2621
    // On failure, if we load images as is, we just warn.
2622
713
    std::string* msgOut = option.as_is ? warn : err;
2623
713
    if (msgOut) {
2624
713
      (*msgOut) +=
2625
713
        "Unknown image format. STB cannot decode image header for image[" +
2626
713
        std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
2627
713
    }
2628
713
    if (!option.as_is) {
2629
      // If we decode images, error out.
2630
713
      return false;
2631
713
    } 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
713
  }
2641
2642
21.3k
  int bits = 8;
2643
21.3k
  int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
2644
2645
21.3k
  if (stbi_is_16_bit_from_memory(bytes, size)) {
2646
1.17k
    bits = 16;
2647
1.17k
    pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2648
1.17k
  }
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
21.3k
  req_comp = (option.preserve_channels || option.as_is) ? 0 : 4;
2654
2655
21.3k
  unsigned char* data = nullptr;
2656
  // Perform image decoding if requested
2657
21.3k
  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
21.3k
    if (bits == 16) {
2661
1.17k
      data = reinterpret_cast<unsigned char *>(stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
2662
1.17k
    }
2663
    // Load as 8 bit per channel data
2664
21.3k
    if (!data) {
2665
20.2k
      data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
2666
20.2k
      if (!data) {
2667
514
        if (err) {
2668
514
          (*err) +=
2669
514
            "Unknown image format. STB cannot decode image data for image[" +
2670
514
            std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
2671
514
        }
2672
514
        return false;
2673
514
      }
2674
      // If we were succesful, mark as 8 bit
2675
19.6k
      bits = 8;
2676
19.6k
      pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
2677
19.6k
    }
2678
21.3k
  }
2679
2680
20.8k
  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.8k
  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.8k
  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.8k
  if (req_comp != 0) {
2714
    // loaded data has `req_comp` channels(components)
2715
20.8k
    comp = req_comp;
2716
20.8k
  }
2717
2718
20.8k
  image->width = w;
2719
20.8k
  image->height = h;
2720
20.8k
  image->component = comp;
2721
20.8k
  image->bits = bits;
2722
20.8k
  image->pixel_type = pixel_type;
2723
20.8k
  image->as_is = option.as_is;
2724
2725
20.8k
  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.8k
  else {
2731
    // Store the decoded image data
2732
20.8k
    image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2733
20.8k
    std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
2734
20.8k
  }
2735
2736
20.8k
  stbi_image_free(data);
2737
20.8k
  return true;
2738
20.8k
}
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
58.2k
bool FileExists(const std::string &abs_filename, void *) {
2902
58.2k
  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
58.2k
  struct stat sb;
2949
58.2k
  if (stat(abs_filename.c_str(), &sb)) {
2950
57.8k
    return false;
2951
57.8k
  }
2952
466
  if (S_ISDIR(sb.st_mode)) {
2953
466
    return false;
2954
466
  }
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
466
}
2968
2969
58.2k
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
58.2k
  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
58.2k
}
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
53.6k
bool IsDataURI(const std::string &in) {
3330
53.6k
  std::string header = "data:application/octet-stream;base64,";
3331
53.6k
  if (in.find(header) == 0) {
3332
1.10k
    return true;
3333
1.10k
  }
3334
3335
52.5k
  header = "data:image/jpeg;base64,";
3336
52.5k
  if (in.find(header) == 0) {
3337
10.9k
    return true;
3338
10.9k
  }
3339
3340
41.5k
  header = "data:image/png;base64,";
3341
41.5k
  if (in.find(header) == 0) {
3342
2.51k
    return true;
3343
2.51k
  }
3344
3345
39.0k
  header = "data:image/bmp;base64,";
3346
39.0k
  if (in.find(header) == 0) {
3347
2.32k
    return true;
3348
2.32k
  }
3349
3350
36.7k
  header = "data:image/gif;base64,";
3351
36.7k
  if (in.find(header) == 0) {
3352
2.16k
    return true;
3353
2.16k
  }
3354
3355
34.5k
  header = "data:text/plain;base64,";
3356
34.5k
  if (in.find(header) == 0) {
3357
1.65k
    return true;
3358
1.65k
  }
3359
3360
32.9k
  header = "data:application/gltf-buffer;base64,";
3361
32.9k
  if (in.find(header) == 0) {
3362
1.54k
    return true;
3363
1.54k
  }
3364
3365
31.3k
  return false;
3366
32.9k
}
3367
3368
bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3369
22.2k
                   const std::string &in, size_t reqBytes, bool checkSize) {
3370
22.2k
  std::string header = "data:application/octet-stream;base64,";
3371
22.2k
  std::string data;
3372
22.2k
  if (in.find(header) == 0) {
3373
1.10k
    data = base64_decode(in.substr(header.size()));  // cut mime string.
3374
1.10k
  }
3375
3376
22.2k
  if (data.empty()) {
3377
21.2k
    header = "data:image/jpeg;base64,";
3378
21.2k
    if (in.find(header) == 0) {
3379
10.9k
      mime_type = "image/jpeg";
3380
10.9k
      data = base64_decode(in.substr(header.size()));  // cut mime string.
3381
10.9k
    }
3382
21.2k
  }
3383
3384
22.2k
  if (data.empty()) {
3385
10.2k
    header = "data:image/png;base64,";
3386
10.2k
    if (in.find(header) == 0) {
3387
2.51k
      mime_type = "image/png";
3388
2.51k
      data = base64_decode(in.substr(header.size()));  // cut mime string.
3389
2.51k
    }
3390
10.2k
  }
3391
3392
22.2k
  if (data.empty()) {
3393
7.79k
    header = "data:image/bmp;base64,";
3394
7.79k
    if (in.find(header) == 0) {
3395
2.32k
      mime_type = "image/bmp";
3396
2.32k
      data = base64_decode(in.substr(header.size()));  // cut mime string.
3397
2.32k
    }
3398
7.79k
  }
3399
3400
22.2k
  if (data.empty()) {
3401
5.50k
    header = "data:image/gif;base64,";
3402
5.50k
    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
5.50k
  }
3407
3408
22.2k
  if (data.empty()) {
3409
3.38k
    header = "data:text/plain;base64,";
3410
3.38k
    if (in.find(header) == 0) {
3411
1.65k
      mime_type = "text/plain";
3412
1.65k
      data = base64_decode(in.substr(header.size()));
3413
1.65k
    }
3414
3.38k
  }
3415
3416
22.2k
  if (data.empty()) {
3417
1.74k
    header = "data:application/gltf-buffer;base64,";
3418
1.74k
    if (in.find(header) == 0) {
3419
1.54k
      data = base64_decode(in.substr(header.size()));
3420
1.54k
    }
3421
1.74k
  }
3422
3423
  // TODO(syoyo): Allow empty buffer? #229
3424
22.2k
  if (data.empty()) {
3425
204
    return false;
3426
204
  }
3427
3428
22.0k
  if (checkSize) {
3429
0
    if (data.size() != reqBytes) {
3430
0
      return false;
3431
0
    }
3432
0
    out->resize(reqBytes);
3433
22.0k
  } else {
3434
22.0k
    out->resize(data.size());
3435
22.0k
  }
3436
22.0k
  std::copy(data.begin(), data.end(), out->begin());
3437
22.0k
  return true;
3438
22.0k
}
3439
3440
namespace detail {
3441
37.9k
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
37.9k
  auto type = o.type();
3462
3463
37.9k
  if ((type == detail::json::value_t::number_integer) ||
3464
37.9k
      (type == detail::json::value_t::number_unsigned)) {
3465
22.0k
    val = static_cast<int>(o.get<int64_t>());
3466
22.0k
    return true;
3467
22.0k
  }
3468
3469
15.9k
  return false;
3470
37.9k
#endif
3471
37.9k
}
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
827k
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
827k
  if (o.is_number()) {
3494
802k
    val = o.get<double>();
3495
802k
    return true;
3496
802k
  }
3497
3498
24.9k
  return false;
3499
827k
#endif
3500
827k
}
3501
3502
200k
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
200k
  if (o.type() == detail::json::value_t::string) {
3512
70.4k
    val = o.get<std::string>();
3513
70.4k
    return true;
3514
70.4k
  }
3515
3516
130k
  return false;
3517
200k
#endif
3518
200k
}
3519
3520
54.4k
bool IsArray(const detail::json &o) {
3521
#ifdef TINYGLTF_USE_RAPIDJSON
3522
  return o.IsArray();
3523
#else
3524
54.4k
  return o.is_array();
3525
54.4k
#endif
3526
54.4k
}
3527
3528
22.3k
detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
3529
#ifdef TINYGLTF_USE_RAPIDJSON
3530
  return o.Begin();
3531
#else
3532
22.3k
  return o.begin();
3533
22.3k
#endif
3534
22.3k
}
3535
3536
22.3k
detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
3537
#ifdef TINYGLTF_USE_RAPIDJSON
3538
  return o.End();
3539
#else
3540
22.3k
  return o.end();
3541
22.3k
#endif
3542
22.3k
}
3543
3544
324k
bool IsObject(const detail::json &o) {
3545
#ifdef TINYGLTF_USE_RAPIDJSON
3546
  return o.IsObject();
3547
#else
3548
324k
  return o.is_object();
3549
324k
#endif
3550
324k
}
3551
3552
136k
detail::json_const_iterator ObjectBegin(const detail::json &o) {
3553
#ifdef TINYGLTF_USE_RAPIDJSON
3554
  return o.MemberBegin();
3555
#else
3556
136k
  return o.begin();
3557
136k
#endif
3558
136k
}
3559
3560
136k
detail::json_const_iterator ObjectEnd(const detail::json &o) {
3561
#ifdef TINYGLTF_USE_RAPIDJSON
3562
  return o.MemberEnd();
3563
#else
3564
136k
  return o.end();
3565
136k
#endif
3566
136k
}
3567
3568
// Making this a const char* results in a pointer to a temporary when
3569
// TINYGLTF_USE_RAPIDJSON is off.
3570
106k
std::string GetKey(detail::json_const_iterator &it) {
3571
#ifdef TINYGLTF_USE_RAPIDJSON
3572
  return it->name.GetString();
3573
#else
3574
106k
  return it.key().c_str();
3575
106k
#endif
3576
106k
}
3577
3578
bool FindMember(const detail::json &o, const char *member,
3579
5.92M
                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.92M
  it = o.find(member);
3588
5.92M
  return it != o.end();
3589
5.92M
#endif
3590
5.92M
}
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
406k
const detail::json &GetValue(detail::json_const_iterator &it) {
3623
#ifdef TINYGLTF_USE_RAPIDJSON
3624
  return it->value;
3625
#else
3626
406k
  return it.value();
3627
406k
#endif
3628
406k
}
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
1.03M
static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
3665
1.03M
  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
1.03M
  switch (o.type()) {
3714
52.7k
    case detail::json::value_t::object: {
3715
52.7k
      Value::Object value_object;
3716
105k
      for (auto it = o.begin(); it != o.end(); it++) {
3717
52.5k
        Value entry;
3718
52.5k
        ParseJsonAsValue(&entry, it.value());
3719
52.5k
        if (entry.Type() != NULL_TYPE)
3720
43.7k
          value_object.emplace(it.key(), std::move(entry));
3721
52.5k
      }
3722
52.7k
      if (value_object.size() > 0) val = Value(std::move(value_object));
3723
52.7k
    } break;
3724
25.6k
    case detail::json::value_t::array: {
3725
25.6k
      Value::Array value_array;
3726
25.6k
      value_array.reserve(o.size());
3727
979k
      for (auto it = o.begin(); it != o.end(); it++) {
3728
953k
        Value entry;
3729
953k
        ParseJsonAsValue(&entry, it.value());
3730
953k
        if (entry.Type() != NULL_TYPE)
3731
927k
          value_array.emplace_back(std::move(entry));
3732
953k
      }
3733
25.6k
      if (value_array.size() > 0) val = Value(std::move(value_array));
3734
25.6k
    } break;
3735
17.0k
    case detail::json::value_t::string:
3736
17.0k
      val = Value(o.get<std::string>());
3737
17.0k
      break;
3738
184
    case detail::json::value_t::boolean:
3739
184
      val = Value(o.get<bool>());
3740
184
      break;
3741
6.05k
    case detail::json::value_t::number_integer:
3742
939k
    case detail::json::value_t::number_unsigned:
3743
939k
      val = Value(static_cast<int>(o.get<int64_t>()));
3744
939k
      break;
3745
4.62k
    case detail::json::value_t::number_float:
3746
4.62k
      val = Value(o.get<double>());
3747
4.62k
      break;
3748
32
    case detail::json::value_t::null:
3749
32
    case detail::json::value_t::discarded:
3750
32
    case detail::json::value_t::binary:
3751
      // default:
3752
32
      break;
3753
1.03M
  }
3754
1.03M
#endif
3755
1.03M
  const bool isNotNull = val.Type() != NULL_TYPE;
3756
3757
1.03M
  if (ret) *ret = std::move(val);
3758
3759
1.03M
  return isNotNull;
3760
1.03M
}
3761
3762
903k
static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
3763
903k
  detail::json_const_iterator it;
3764
903k
  if (!detail::FindMember(o, "extras", it)) {
3765
889k
    return false;
3766
889k
  }
3767
3768
14.4k
  return ParseJsonAsValue(ret, detail::GetValue(it));
3769
903k
}
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
54.5k
                                 const std::string &parent_node = "") {
3776
54.5k
  detail::json_const_iterator it;
3777
54.5k
  if (!detail::FindMember(o, property.c_str(), it)) {
3778
52.2k
    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
52.2k
    return false;
3788
52.2k
  }
3789
3790
2.21k
  auto &value = detail::GetValue(it);
3791
3792
2.21k
  bool isBoolean;
3793
2.21k
  bool boolValue = false;
3794
#ifdef TINYGLTF_USE_RAPIDJSON
3795
  isBoolean = value.IsBool();
3796
  if (isBoolean) {
3797
    boolValue = value.GetBool();
3798
  }
3799
#else
3800
2.21k
  isBoolean = value.is_boolean();
3801
2.21k
  if (isBoolean) {
3802
184
    boolValue = value.get<bool>();
3803
184
  }
3804
2.21k
#endif
3805
2.21k
  if (!isBoolean) {
3806
2.03k
    if (required) {
3807
0
      if (err) {
3808
0
        (*err) += "'" + property + "' property is not a bool type.\n";
3809
0
      }
3810
0
    }
3811
2.03k
    return false;
3812
2.03k
  }
3813
3814
184
  if (ret) {
3815
184
    (*ret) = boolValue;
3816
184
  }
3817
3818
184
  return true;
3819
2.21k
}
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
833k
                                 const std::string &parent_node = "") {
3826
833k
  detail::json_const_iterator it;
3827
833k
  if (!detail::FindMember(o, property.c_str(), it)) {
3828
828k
    if (required) {
3829
658k
      if (err) {
3830
658k
        (*err) += "'" + property + "' property is missing";
3831
658k
        if (!parent_node.empty()) {
3832
2.59k
          (*err) += " in " + parent_node;
3833
2.59k
        }
3834
658k
        (*err) += ".\n";
3835
658k
      }
3836
658k
    }
3837
828k
    return false;
3838
828k
  }
3839
3840
4.30k
  int intValue;
3841
4.30k
  bool isInt = detail::GetInt(detail::GetValue(it), intValue);
3842
4.30k
  if (!isInt) {
3843
821
    if (required) {
3844
637
      if (err) {
3845
637
        (*err) += "'" + property + "' property is not an integer type.\n";
3846
637
      }
3847
637
    }
3848
821
    return false;
3849
821
  }
3850
3851
3.48k
  if (ret) {
3852
3.48k
    (*ret) = intValue;
3853
3.48k
  }
3854
3855
3.48k
  return true;
3856
4.30k
}
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
85.9k
                                const std::string &parent_node = "") {
3917
85.9k
  detail::json_const_iterator it;
3918
3919
85.9k
  if (!detail::FindMember(o, property.c_str(), it)) {
3920
56.2k
    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
56.2k
    return false;
3930
56.2k
  }
3931
3932
29.7k
  double numberValue;
3933
29.7k
  bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
3934
3935
29.7k
  if (!isNumber) {
3936
17.3k
    if (required) {
3937
0
      if (err) {
3938
0
        (*err) += "'" + property + "' property is not a number type.\n";
3939
0
      }
3940
0
    }
3941
17.3k
    return false;
3942
17.3k
  }
3943
3944
12.3k
  if (ret) {
3945
12.3k
    (*ret) = numberValue;
3946
12.3k
  }
3947
3948
12.3k
  return true;
3949
29.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
144k
                                     const std::string &parent_node = "") {
3955
144k
  detail::json_const_iterator it;
3956
144k
  if (!detail::FindMember(o, property.c_str(), it)) {
3957
111k
    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
111k
    return false;
3967
111k
  }
3968
3969
33.1k
  if (!detail::IsArray(detail::GetValue(it))) {
3970
27.6k
    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
27.6k
    return false;
3980
27.6k
  }
3981
3982
5.47k
  ret->clear();
3983
5.47k
  auto end = detail::ArrayEnd(detail::GetValue(it));
3984
786k
  for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
3985
783k
    double numberValue;
3986
783k
    const bool isNumber = detail::GetNumber(*i, numberValue);
3987
783k
    if (!isNumber) {
3988
2.46k
      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
2.46k
      return false;
3998
2.46k
    }
3999
781k
    ret->push_back(numberValue);
4000
781k
  }
4001
4002
3.00k
  return true;
4003
5.47k
}
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
40.0k
                                      const std::string &parent_node = "") {
4010
40.0k
  detail::json_const_iterator it;
4011
40.0k
  if (!detail::FindMember(o, property.c_str(), it)) {
4012
38.8k
    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
38.8k
    return false;
4022
38.8k
  }
4023
4024
1.17k
  if (!detail::IsArray(detail::GetValue(it))) {
4025
185
    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
185
    return false;
4035
185
  }
4036
4037
985
  ret->clear();
4038
985
  auto end = detail::ArrayEnd(detail::GetValue(it));
4039
3.92k
  for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
4040
3.22k
    int numberValue;
4041
3.22k
    bool isNumber = detail::GetInt(*i, numberValue);
4042
3.22k
    if (!isNumber) {
4043
285
      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
285
      return false;
4053
285
    }
4054
2.94k
    ret->push_back(numberValue);
4055
2.94k
  }
4056
4057
700
  return true;
4058
985
}
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.37M
    const std::string &parent_node = std::string()) {
4064
2.37M
  detail::json_const_iterator it;
4065
2.37M
  if (!detail::FindMember(o, property.c_str(), it)) {
4066
2.27M
    if (required) {
4067
655k
      if (err) {
4068
655k
        (*err) += "'" + property + "' property is missing";
4069
655k
        if (parent_node.empty()) {
4070
655k
          (*err) += ".\n";
4071
655k
        } else {
4072
0
          (*err) += " in `" + parent_node + "'.\n";
4073
0
        }
4074
655k
      }
4075
655k
    }
4076
2.27M
    return false;
4077
2.27M
  }
4078
4079
96.9k
  std::string strValue;
4080
96.9k
  if (!detail::GetString(detail::GetValue(it), strValue)) {
4081
31.7k
    if (required) {
4082
297
      if (err) {
4083
297
        (*err) += "'" + property + "' property is not a string type.\n";
4084
297
      }
4085
297
    }
4086
31.7k
    return false;
4087
31.7k
  }
4088
4089
65.1k
  if (ret) {
4090
65.1k
    (*ret) = std::move(strValue);
4091
65.1k
  }
4092
4093
65.1k
  return true;
4094
96.9k
}
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
41.7k
                                       const std::string &parent = "") {
4101
41.7k
  detail::json_const_iterator it;
4102
41.7k
  if (!detail::FindMember(o, property.c_str(), it)) {
4103
3.61k
    if (required) {
4104
3.61k
      if (err) {
4105
3.61k
        if (!parent.empty()) {
4106
3.61k
          (*err) +=
4107
3.61k
              "'" + property + "' property is missing in " + parent + ".\n";
4108
3.61k
        } else {
4109
0
          (*err) += "'" + property + "' property is missing.\n";
4110
0
        }
4111
3.61k
      }
4112
3.61k
    }
4113
3.61k
    return false;
4114
3.61k
  }
4115
4116
38.1k
  const detail::json &dict = detail::GetValue(it);
4117
4118
  // Make sure we are dealing with an object / dictionary.
4119
38.1k
  if (!detail::IsObject(dict)) {
4120
63
    if (required) {
4121
63
      if (err) {
4122
63
        (*err) += "'" + property + "' property is not an object.\n";
4123
63
      }
4124
63
    }
4125
63
    return false;
4126
63
  }
4127
4128
38.0k
  ret->clear();
4129
4130
38.0k
  detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4131
38.0k
  detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
4132
4133
45.3k
  for (; dictIt != dictItEnd; ++dictIt) {
4134
7.57k
    int intVal;
4135
7.57k
    if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
4136
278
      if (required) {
4137
278
        if (err) {
4138
278
          (*err) += "'" + property + "' value is not an integer type.\n";
4139
278
        }
4140
278
      }
4141
278
      return false;
4142
278
    }
4143
4144
    // Insert into the list.
4145
7.29k
    (*ret)[detail::GetKey(dictIt)] = intVal;
4146
7.29k
  }
4147
37.8k
  return true;
4148
38.0k
}
4149
4150
static bool ParseJSONProperty(std::map<std::string, double> *ret,
4151
                              std::string *err, const detail::json &o,
4152
18.9k
                              const std::string &property, bool required) {
4153
18.9k
  detail::json_const_iterator it;
4154
18.9k
  if (!detail::FindMember(o, property.c_str(), it)) {
4155
1.61k
    if (required) {
4156
0
      if (err) {
4157
0
        (*err) += "'" + property + "' property is missing. \n'";
4158
0
      }
4159
0
    }
4160
1.61k
    return false;
4161
1.61k
  }
4162
4163
17.3k
  const detail::json &obj = detail::GetValue(it);
4164
4165
17.3k
  if (!detail::IsObject(obj)) {
4166
2.21k
    if (required) {
4167
0
      if (err) {
4168
0
        (*err) += "'" + property + "' property is not a JSON object.\n";
4169
0
      }
4170
0
    }
4171
2.21k
    return false;
4172
2.21k
  }
4173
4174
15.1k
  ret->clear();
4175
4176
15.1k
  detail::json_const_iterator it2(detail::ObjectBegin(obj));
4177
15.1k
  detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
4178
29.4k
  for (; it2 != itEnd; ++it2) {
4179
14.3k
    double numVal;
4180
14.3k
    if (detail::GetNumber(detail::GetValue(it2), numVal))
4181
9.20k
      ret->emplace(std::string(detail::GetKey(it2)), numVal);
4182
14.3k
  }
4183
4184
15.1k
  return true;
4185
17.3k
}
4186
4187
static bool ParseParameterProperty(Parameter *param, std::string *err,
4188
                                   const detail::json &o,
4189
35.0k
                                   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
35.0k
  if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
4196
    // Found string property.
4197
2.01k
    return true;
4198
33.0k
  } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
4199
33.0k
                                      false)) {
4200
    // Found a number array.
4201
1.75k
    return true;
4202
31.3k
  } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
4203
12.3k
    param->has_number_value = true;
4204
12.3k
    return true;
4205
18.9k
  } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
4206
18.9k
                               false)) {
4207
15.1k
    return true;
4208
15.1k
  } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
4209
184
    return true;
4210
3.64k
  } else {
4211
3.64k
    if (required) {
4212
0
      if (err) {
4213
0
        (*err) += "parameter must be a string or number / number array.\n";
4214
0
      }
4215
0
    }
4216
3.64k
    return false;
4217
3.64k
  }
4218
35.0k
}
4219
4220
static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
4221
903k
                                    const detail::json &o) {
4222
903k
  (void)err;
4223
4224
903k
  detail::json_const_iterator it;
4225
903k
  if (!detail::FindMember(o, "extensions", it)) {
4226
894k
    return false;
4227
894k
  }
4228
4229
9.32k
  auto &obj = detail::GetValue(it);
4230
9.32k
  if (!detail::IsObject(obj)) {
4231
199
    return false;
4232
199
  }
4233
9.12k
  ExtensionMap extensions;
4234
9.12k
  detail::json_const_iterator extIt =
4235
9.12k
      detail::ObjectBegin(obj);  // it.value().begin();
4236
9.12k
  detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
4237
32.7k
  for (; extIt != extEnd; ++extIt) {
4238
23.6k
    auto &itObj = detail::GetValue(extIt);
4239
23.6k
    if (!detail::IsObject(itObj)) continue;
4240
18.6k
    std::string key(detail::GetKey(extIt));
4241
18.6k
    if (!ParseJsonAsValue(&extensions[key], itObj)) {
4242
13.6k
      if (!key.empty()) {
4243
        // create empty object so that an extension object is still of type
4244
        // object
4245
10.9k
        extensions[key] = Value{Value::Object{}};
4246
10.9k
      }
4247
13.6k
    }
4248
18.6k
  }
4249
9.12k
  if (ret) {
4250
9.12k
    (*ret) = std::move(extensions);
4251
9.12k
  }
4252
9.12k
  return true;
4253
9.32k
}
4254
4255
template <typename GltfType>
4256
static bool ParseExtrasAndExtensions(GltfType *target, std::string *err,
4257
                                     const detail::json &o,
4258
903k
                                     bool store_json_strings) {
4259
903k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
903k
  ParseExtrasProperty(&target->extras, o);
4261
4262
903k
  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
903k
  return true;
4278
903k
}
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.64k
                                     bool store_json_strings) {
4259
4.64k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
4.64k
  ParseExtrasProperty(&target->extras, o);
4261
4262
4.64k
  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.64k
  return true;
4278
4.64k
}
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
656k
                                     bool store_json_strings) {
4259
656k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
656k
  ParseExtrasProperty(&target->extras, o);
4261
4262
656k
  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
656k
  return true;
4278
656k
}
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
37.8k
                                     bool store_json_strings) {
4259
37.8k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
37.8k
  ParseExtrasProperty(&target->extras, o);
4261
4262
37.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
37.8k
  return true;
4278
37.8k
}
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
9.90k
                                     bool store_json_strings) {
4259
9.90k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
9.90k
  ParseExtrasProperty(&target->extras, o);
4261
4262
9.90k
  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.90k
  return true;
4278
9.90k
}
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
9.94k
                                     bool store_json_strings) {
4259
9.94k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
9.94k
  ParseExtrasProperty(&target->extras, o);
4261
4262
9.94k
  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.94k
  return true;
4278
9.94k
}
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
30.0k
                                     bool store_json_strings) {
4259
30.0k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
30.0k
  ParseExtrasProperty(&target->extras, o);
4261
4262
30.0k
  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
30.0k
  return true;
4278
30.0k
}
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
1.79k
                                     bool store_json_strings) {
4259
1.79k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
1.79k
  ParseExtrasProperty(&target->extras, o);
4261
4262
1.79k
  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.79k
  return true;
4278
1.79k
}
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
191
                                     bool store_json_strings) {
4259
191
  ParseExtensionsProperty(&target->extensions, err, o);
4260
191
  ParseExtrasProperty(&target->extras, o);
4261
4262
191
  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
191
  return true;
4278
191
}
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
192
                                     bool store_json_strings) {
4259
192
  ParseExtensionsProperty(&target->extensions, err, o);
4260
192
  ParseExtrasProperty(&target->extras, o);
4261
4262
192
  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
192
  return true;
4278
192
}
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
313
                                     bool store_json_strings) {
4259
313
  ParseExtensionsProperty(&target->extensions, err, o);
4260
313
  ParseExtrasProperty(&target->extras, o);
4261
4262
313
  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
313
  return true;
4278
313
}
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
50.6k
                                     bool store_json_strings) {
4259
50.6k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
50.6k
  ParseExtrasProperty(&target->extras, o);
4261
4262
50.6k
  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
50.6k
  return true;
4278
50.6k
}
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
53.6k
                                     bool store_json_strings) {
4259
53.6k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
53.6k
  ParseExtrasProperty(&target->extras, o);
4261
4262
53.6k
  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
53.6k
  return true;
4278
53.6k
}
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.71k
                                     bool store_json_strings) {
4259
1.71k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
1.71k
  ParseExtrasProperty(&target->extras, o);
4261
4262
1.71k
  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.71k
  return true;
4278
1.71k
}
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.59k
                                     bool store_json_strings) {
4259
2.59k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
2.59k
  ParseExtrasProperty(&target->extras, o);
4261
4262
2.59k
  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.59k
  return true;
4278
2.59k
}
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
37.5k
                                     bool store_json_strings) {
4259
37.5k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
37.5k
  ParseExtrasProperty(&target->extras, o);
4261
4262
37.5k
  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
37.5k
  return true;
4278
37.5k
}
Unexecuted instantiation: fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Skin>(tinygltf::Skin*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
fuzz_gltf.cc:bool tinygltf::ParseExtrasAndExtensions<tinygltf::Sampler>(tinygltf::Sampler*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, nlohmann::basic_json<std::__1::map, std::__1::vector, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, bool, long, unsigned long, double, std::__1::allocator, nlohmann::adl_serializer, std::__1::vector<unsigned char, std::__1::allocator<unsigned char> > > const&, bool)
Line
Count
Source
4258
2.75k
                                     bool store_json_strings) {
4259
2.75k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
2.75k
  ParseExtrasProperty(&target->extras, o);
4261
4262
2.75k
  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.75k
  return true;
4278
2.75k
}
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
3.06k
                                     bool store_json_strings) {
4259
3.06k
  ParseExtensionsProperty(&target->extensions, err, o);
4260
3.06k
  ParseExtrasProperty(&target->extras, o);
4261
4262
3.06k
  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
3.06k
  return true;
4278
3.06k
}
4279
4280
static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
4281
4.64k
                       bool store_original_json_for_extras_and_extensions) {
4282
4.64k
  ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
4283
4.64k
  ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
4284
4.64k
  ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
4285
4.64k
  ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
4286
4287
4.64k
  ParseExtrasAndExtensions(asset, err, o,
4288
4.64k
                           store_original_json_for_extras_and_extensions);
4289
4.64k
  return true;
4290
4.64k
}
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
53.6k
                       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
53.6k
  detail::json_const_iterator it;
4304
53.6k
  bool hasBufferView = detail::FindMember(o, "bufferView", it);
4305
53.6k
  bool hasURI = detail::FindMember(o, "uri", it);
4306
4307
53.6k
  ParseStringProperty(&image->name, err, o, "name", false);
4308
4309
53.6k
  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
53.6k
  if (!hasBufferView && !hasURI) {
4321
21
    if (err) {
4322
21
      (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
4323
21
                std::to_string(image_idx) + "] name = \"" + image->name +
4324
21
                "\"\n";
4325
21
    }
4326
21
    return false;
4327
21
  }
4328
4329
53.6k
  ParseExtrasAndExtensions(image, err, o,
4330
53.6k
                           store_original_json_for_extras_and_extensions);
4331
4332
53.6k
  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
53.6k
  std::string uri;
4365
53.6k
  std::string tmp_err;
4366
53.6k
  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
53.6k
  std::vector<unsigned char> img;
4375
4376
53.6k
  if (IsDataURI(uri)) {
4377
22.2k
    if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
4378
204
      if (err) {
4379
204
        (*err) += "Failed to decode 'uri' for image[" +
4380
204
                  std::to_string(image_idx) + "] name = \"" + image->name +
4381
204
                  "\"\n";
4382
204
      }
4383
204
      return false;
4384
204
    }
4385
31.3k
  } else {
4386
    // Assume external file
4387
    // Unconditionally keep the external URI of the image
4388
31.3k
    image->uri = uri;
4389
#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
4390
    return true;
4391
#else
4392
31.3k
    std::string decoded_uri;
4393
31.3k
    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
31.3k
    if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4405
31.3k
                          /* required */ false, /* required bytes */ 0,
4406
31.3k
                          /* checksize */ false,
4407
31.3k
                          /* max file size */ max_file_size, fs)) {
4408
31.3k
      if (warn) {
4409
31.3k
        (*warn) += "Failed to load external 'uri' for image[" +
4410
31.3k
                   std::to_string(image_idx) + "] name = \"" + decoded_uri +
4411
31.3k
                   "\"\n";
4412
31.3k
      }
4413
      // If the image cannot be loaded, keep uri as image->uri.
4414
31.3k
      return true;
4415
31.3k
    }
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
22.0k
  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
22.0k
  return LoadImageData(image, image_idx, err, warn, 0, 0, &img.at(0),
4436
22.0k
                       static_cast<int>(img.size()), load_image_user_data);
4437
22.0k
}
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.71k
                         const std::string &basedir) {
4443
1.71k
  (void)basedir;
4444
1.71k
  int sampler = -1;
4445
1.71k
  int source = -1;
4446
1.71k
  ParseIntegerProperty(&sampler, err, o, "sampler", false);
4447
4448
1.71k
  ParseIntegerProperty(&source, err, o, "source", false);
4449
4450
1.71k
  texture->sampler = sampler;
4451
1.71k
  texture->source = source;
4452
4453
1.71k
  ParseExtrasAndExtensions(texture, err, o,
4454
1.71k
                           store_original_json_for_extras_and_extensions);
4455
4456
1.71k
  ParseStringProperty(&texture->name, err, o, "name", false);
4457
4458
1.71k
  return true;
4459
1.71k
}
4460
4461
static bool ParseTextureInfo(
4462
    TextureInfo *texinfo, std::string *err, const detail::json &o,
4463
856
    bool store_original_json_for_extras_and_extensions) {
4464
856
  if (texinfo == nullptr) {
4465
0
    return false;
4466
0
  }
4467
4468
856
  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4469
856
                            /* required */ true, "TextureInfo")) {
4470
543
    return false;
4471
543
  }
4472
4473
313
  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4474
4475
313
  ParseExtrasAndExtensions(texinfo, err, o,
4476
313
                           store_original_json_for_extras_and_extensions);
4477
4478
313
  return true;
4479
856
}
4480
4481
static bool ParseNormalTextureInfo(
4482
    NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
4483
563
    bool store_original_json_for_extras_and_extensions) {
4484
563
  if (texinfo == nullptr) {
4485
0
    return false;
4486
0
  }
4487
4488
563
  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4489
563
                            /* required */ true, "NormalTextureInfo")) {
4490
372
    return false;
4491
372
  }
4492
4493
191
  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4494
191
  ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4495
4496
191
  ParseExtrasAndExtensions(texinfo, err, o,
4497
191
                           store_original_json_for_extras_and_extensions);
4498
4499
191
  return true;
4500
563
}
4501
4502
static bool ParseOcclusionTextureInfo(
4503
    OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
4504
432
    bool store_original_json_for_extras_and_extensions) {
4505
432
  if (texinfo == nullptr) {
4506
0
    return false;
4507
0
  }
4508
4509
432
  if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4510
432
                            /* required */ true, "NormalTextureInfo")) {
4511
240
    return false;
4512
240
  }
4513
4514
192
  ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4515
192
  ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4516
4517
192
  ParseExtrasAndExtensions(texinfo, err, o,
4518
192
                           store_original_json_for_extras_and_extensions);
4519
4520
192
  return true;
4521
432
}
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
41.7k
                           ParseStrictness strictness) {
5091
41.7k
  int material = -1;
5092
41.7k
  ParseIntegerProperty(&material, err, o, "material", false);
5093
41.7k
  primitive->material = material;
5094
5095
41.7k
  int mode = TINYGLTF_MODE_TRIANGLES;
5096
41.7k
  ParseIntegerProperty(&mode, err, o, "mode", false);
5097
41.7k
  primitive->mode = mode;  // Why only triangles were supported ?
5098
5099
41.7k
  int indices = -1;
5100
41.7k
  ParseIntegerProperty(&indices, err, o, "indices", false);
5101
41.7k
  primitive->indices = indices;
5102
41.7k
  if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
5103
41.7k
                                  true, "Primitive")) {
5104
3.95k
    return false;
5105
3.95k
  }
5106
5107
  // Look for morph targets
5108
37.8k
  detail::json_const_iterator targetsObject;
5109
37.8k
  if (detail::FindMember(o, "targets", targetsObject) &&
5110
37.8k
      detail::IsArray(detail::GetValue(targetsObject))) {
5111
852
    auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
5112
852
    for (detail::json_const_array_iterator i =
5113
852
             detail::ArrayBegin(detail::GetValue(targetsObject));
5114
22.5k
         i != targetsObjectEnd; ++i) {
5115
21.7k
      std::map<std::string, int> targetAttribues;
5116
5117
21.7k
      const detail::json &dict = *i;
5118
21.7k
      if (detail::IsObject(dict)) {
5119
21.4k
        detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
5120
21.4k
        detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
5121
5122
44.2k
        for (; dictIt != dictItEnd; ++dictIt) {
5123
22.8k
          int iVal;
5124
22.8k
          if (detail::GetInt(detail::GetValue(dictIt), iVal))
5125
8.31k
            targetAttribues[detail::GetKey(dictIt)] = iVal;
5126
22.8k
        }
5127
21.4k
        primitive->targets.emplace_back(std::move(targetAttribues));
5128
21.4k
      }
5129
21.7k
    }
5130
852
  }
5131
5132
37.8k
  ParseExtrasAndExtensions(primitive, err, o,
5133
37.8k
                           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
37.8k
  (void)model;
5143
37.8k
  (void)warn;
5144
37.8k
  (void)strictness;
5145
37.8k
#endif
5146
5147
37.8k
  return true;
5148
41.7k
}
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
9.90k
                      ParseStrictness strictness) {
5155
9.90k
  ParseStringProperty(&mesh->name, err, o, "name", false);
5156
5157
9.90k
  mesh->primitives.clear();
5158
9.90k
  detail::json_const_iterator primObject;
5159
9.90k
  if (detail::FindMember(o, "primitives", primObject) &&
5160
9.90k
      detail::IsArray(detail::GetValue(primObject))) {
5161
1.18k
    detail::json_const_array_iterator primEnd =
5162
1.18k
        detail::ArrayEnd(detail::GetValue(primObject));
5163
1.18k
    for (detail::json_const_array_iterator i =
5164
1.18k
             detail::ArrayBegin(detail::GetValue(primObject));
5165
42.9k
         i != primEnd; ++i) {
5166
41.7k
      Primitive primitive;
5167
41.7k
      if (ParsePrimitive(&primitive, model, err, warn, *i,
5168
41.7k
                         store_original_json_for_extras_and_extensions,
5169
41.7k
                         strictness)) {
5170
        // Only add the primitive if the parsing succeeds.
5171
37.8k
        mesh->primitives.emplace_back(std::move(primitive));
5172
37.8k
      }
5173
41.7k
    }
5174
1.18k
  }
5175
5176
  // Should probably check if has targets and if dimensions fit
5177
9.90k
  ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
5178
5179
9.90k
  ParseExtrasAndExtensions(mesh, err, o,
5180
9.90k
                           store_original_json_for_extras_and_extensions);
5181
5182
9.90k
  return true;
5183
9.90k
}
5184
5185
static bool ParseNode(Node *node, std::string *err, const detail::json &o,
5186
9.94k
                      bool store_original_json_for_extras_and_extensions) {
5187
9.94k
  ParseStringProperty(&node->name, err, o, "name", false);
5188
5189
9.94k
  int skin = -1;
5190
9.94k
  ParseIntegerProperty(&skin, err, o, "skin", false);
5191
9.94k
  node->skin = skin;
5192
5193
  // Matrix and T/R/S are exclusive
5194
9.94k
  if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
5195
9.75k
    ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
5196
9.75k
    ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
5197
9.75k
    ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
5198
9.75k
  }
5199
5200
9.94k
  int camera = -1;
5201
9.94k
  ParseIntegerProperty(&camera, err, o, "camera", false);
5202
9.94k
  node->camera = camera;
5203
5204
9.94k
  int mesh = -1;
5205
9.94k
  ParseIntegerProperty(&mesh, err, o, "mesh", false);
5206
9.94k
  node->mesh = mesh;
5207
5208
9.94k
  node->children.clear();
5209
9.94k
  ParseIntegerArrayProperty(&node->children, err, o, "children", false);
5210
5211
9.94k
  ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
5212
5213
9.94k
  ParseExtrasAndExtensions(node, err, o,
5214
9.94k
                           store_original_json_for_extras_and_extensions);
5215
5216
  // KHR_lights_punctual: parse light source reference
5217
9.94k
  int light = -1;
5218
9.94k
  if (node->extensions.count("KHR_lights_punctual") != 0) {
5219
1.09k
    auto const &light_ext = node->extensions["KHR_lights_punctual"];
5220
1.09k
    if (light_ext.Has("light")) {
5221
1.08k
      light = light_ext.Get("light").GetNumberAsInt();
5222
1.08k
    } else {
5223
13
      if (err) {
5224
13
        *err +=
5225
13
            "Node has extension KHR_lights_punctual, but does not reference "
5226
13
            "a light source.\n";
5227
13
      }
5228
13
      return false;
5229
13
    }
5230
1.09k
  }
5231
9.93k
  node->light = light;
5232
5233
  // KHR_audio: parse audio source reference
5234
9.93k
  int emitter = -1;
5235
9.93k
  if (node->extensions.count("KHR_audio") != 0) {
5236
1.06k
    auto const &audio_ext = node->extensions["KHR_audio"];
5237
1.06k
    if (audio_ext.Has("emitter")) {
5238
1.04k
      emitter = audio_ext.Get("emitter").GetNumberAsInt();
5239
1.04k
    } else {
5240
20
      if (err) {
5241
20
        *err +=
5242
20
            "Node has extension KHR_audio, but does not reference "
5243
20
            "a audio emitter.\n";
5244
20
      }
5245
20
      return false;
5246
20
    }
5247
1.06k
  }
5248
9.91k
  node->emitter = emitter;
5249
5250
9.91k
  node->lods.clear();
5251
9.91k
  if (node->extensions.count("MSFT_lod") != 0) {
5252
0
    auto const &msft_lod_ext = node->extensions["MSFT_lod"];
5253
0
    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
0
    } else {
5259
0
      if (err) {
5260
0
        *err +=
5261
0
            "Node has extension MSFT_lod, but does not reference "
5262
0
            "other nodes via their ids.\n";
5263
0
      }
5264
0
      return false;
5265
0
    }
5266
0
  }
5267
5268
9.91k
  return true;
5269
9.91k
}
5270
5271
static bool ParseScene(Scene *scene, std::string *err, const detail::json &o,
5272
30.0k
                       bool store_original_json_for_extras_and_extensions) {
5273
30.0k
  ParseStringProperty(&scene->name, err, o, "name", false);
5274
30.0k
  ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false);
5275
5276
30.0k
  ParseExtrasAndExtensions(scene, err, o,
5277
30.0k
                           store_original_json_for_extras_and_extensions);
5278
5279
  // Parse KHR_audio global emitters
5280
30.0k
  if (scene->extensions.count("KHR_audio") != 0) {
5281
929
    auto const &audio_ext = scene->extensions["KHR_audio"];
5282
929
    if (audio_ext.Has("emitters")) {
5283
899
      auto emittersArr = audio_ext.Get("emitters");
5284
270k
      for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) {
5285
269k
        scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt());
5286
269k
      }
5287
899
    } else {
5288
30
      if (err) {
5289
30
        *err +=
5290
30
            "Node has extension KHR_audio, but does not reference "
5291
30
            "a audio emitter.\n";
5292
30
      }
5293
30
      return false;
5294
30
    }
5295
929
  }
5296
5297
30.0k
  return true;
5298
30.0k
}
5299
5300
static bool ParsePbrMetallicRoughness(
5301
    PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
5302
1.79k
    bool store_original_json_for_extras_and_extensions) {
5303
1.79k
  if (pbr == nullptr) {
5304
0
    return false;
5305
0
  }
5306
5307
1.79k
  std::vector<double> baseColorFactor;
5308
1.79k
  if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
5309
1.79k
                               /* 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
1.79k
  {
5323
1.79k
    detail::json_const_iterator it;
5324
1.79k
    if (detail::FindMember(o, "baseColorTexture", it)) {
5325
242
      ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
5326
242
                       store_original_json_for_extras_and_extensions);
5327
242
    }
5328
1.79k
  }
5329
5330
1.79k
  {
5331
1.79k
    detail::json_const_iterator it;
5332
1.79k
    if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
5333
216
      ParseTextureInfo(&pbr->metallicRoughnessTexture, err,
5334
216
                       detail::GetValue(it),
5335
216
                       store_original_json_for_extras_and_extensions);
5336
216
    }
5337
1.79k
  }
5338
5339
1.79k
  ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
5340
1.79k
  ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
5341
5342
1.79k
  ParseExtrasAndExtensions(pbr, err, o,
5343
1.79k
                           store_original_json_for_extras_and_extensions);
5344
5345
1.79k
  return true;
5346
1.79k
}
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
50.6k
                          ParseStrictness strictness) {
5352
50.6k
  ParseStringProperty(&material->name, err, o, "name", /* required */ false);
5353
5354
50.6k
  if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5355
50.6k
                               "emissiveFactor",
5356
50.6k
                               /* required */ false)) {
5357
259
    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
259
    else if (material->emissiveFactor.size() != 3) {
5366
6
      if (err) {
5367
6
        (*err) +=
5368
6
            "Array length of `emissiveFactor` parameter in "
5369
6
            "material must be 3, but got " +
5370
6
            std::to_string(material->emissiveFactor.size()) + "\n";
5371
6
      }
5372
6
      return false;
5373
6
    }
5374
50.4k
  } else {
5375
    // fill with default values
5376
50.4k
    material->emissiveFactor = {0.0, 0.0, 0.0};
5377
50.4k
  }
5378
5379
50.6k
  ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
5380
50.6k
                      /* required */ false);
5381
50.6k
  ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
5382
50.6k
                      /* required */ false);
5383
50.6k
  ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
5384
50.6k
                       /* required */ false);
5385
5386
50.6k
  {
5387
50.6k
    detail::json_const_iterator it;
5388
50.6k
    if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
5389
1.79k
      ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
5390
1.79k
                                detail::GetValue(it),
5391
1.79k
                                store_original_json_for_extras_and_extensions);
5392
1.79k
    }
5393
50.6k
  }
5394
5395
50.6k
  {
5396
50.6k
    detail::json_const_iterator it;
5397
50.6k
    if (detail::FindMember(o, "normalTexture", it)) {
5398
563
      ParseNormalTextureInfo(&material->normalTexture, err,
5399
563
                             detail::GetValue(it),
5400
563
                             store_original_json_for_extras_and_extensions);
5401
563
    }
5402
50.6k
  }
5403
5404
50.6k
  {
5405
50.6k
    detail::json_const_iterator it;
5406
50.6k
    if (detail::FindMember(o, "occlusionTexture", it)) {
5407
432
      ParseOcclusionTextureInfo(&material->occlusionTexture, err,
5408
432
                                detail::GetValue(it),
5409
432
                                store_original_json_for_extras_and_extensions);
5410
432
    }
5411
50.6k
  }
5412
5413
50.6k
  {
5414
50.6k
    detail::json_const_iterator it;
5415
50.6k
    if (detail::FindMember(o, "emissiveTexture", it)) {
5416
398
      ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
5417
398
                       store_original_json_for_extras_and_extensions);
5418
398
    }
5419
50.6k
  }
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
50.6k
  material->values.clear();
5427
50.6k
  material->additionalValues.clear();
5428
5429
50.6k
  detail::json_const_iterator it(detail::ObjectBegin(o));
5430
50.6k
  detail::json_const_iterator itEnd(detail::ObjectEnd(o));
5431
5432
89.0k
  for (; it != itEnd; ++it) {
5433
38.3k
    std::string key(detail::GetKey(it));
5434
38.3k
    if (key == "pbrMetallicRoughness") {
5435
2.40k
      if (detail::IsObject(detail::GetValue(it))) {
5436
1.72k
        const detail::json &values_object = detail::GetValue(it);
5437
5438
1.72k
        detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5439
1.72k
        detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
5440
5441
12.6k
        for (; itVal != itValEnd; ++itVal) {
5442
10.9k
          Parameter param;
5443
10.9k
          if (ParseParameterProperty(&param, err, values_object,
5444
10.9k
                                     detail::GetKey(itVal), false)) {
5445
9.65k
            material->values.emplace(detail::GetKey(itVal), std::move(param));
5446
9.65k
          }
5447
10.9k
        }
5448
1.72k
      }
5449
35.9k
    } 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
24.1k
    } else {
5453
24.1k
      Parameter param;
5454
24.1k
      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
21.8k
        if (key != "name")
5458
20.8k
          material->additionalValues.emplace(std::move(key), std::move(param));
5459
21.8k
      }
5460
24.1k
    }
5461
38.3k
  }
5462
5463
50.6k
  material->extensions.clear();  // Note(agnat): Why?
5464
50.6k
  ParseExtrasAndExtensions(material, err, o,
5465
50.6k
                           store_original_json_for_extras_and_extensions);
5466
5467
50.6k
  material->lods.clear();
5468
50.6k
  if (material->extensions.count("MSFT_lod") != 0) {
5469
1
    auto const &msft_lod_ext = material->extensions["MSFT_lod"];
5470
1
    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
1
    } else {
5476
1
      if (err) {
5477
1
        *err +=
5478
1
            "Material has extension MSFT_lod, but does not reference "
5479
1
            "other materials via their ids.\n";
5480
1
      }
5481
1
      return false;
5482
1
    }
5483
1
  }
5484
5485
50.6k
  return true;
5486
50.6k
}
5487
5488
static bool ParseAnimationChannel(
5489
    AnimationChannel *channel, std::string *err, const detail::json &o,
5490
4.42k
    bool store_original_json_for_extras_and_extensions) {
5491
4.42k
  int samplerIndex = -1;
5492
4.42k
  int targetIndex = -1;
5493
4.42k
  if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
5494
4.42k
                            "AnimationChannel")) {
5495
1.63k
    if (err) {
5496
1.63k
      (*err) += "`sampler` field is missing in animation channels\n";
5497
1.63k
    }
5498
1.63k
    return false;
5499
1.63k
  }
5500
5501
2.78k
  detail::json_const_iterator targetIt;
5502
2.78k
  if (detail::FindMember(o, "target", targetIt) &&
5503
2.78k
      detail::IsObject(detail::GetValue(targetIt))) {
5504
192
    const detail::json &target_object = detail::GetValue(targetIt);
5505
5506
192
    ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
5507
5508
192
    if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5509
192
                             true)) {
5510
192
      if (err) {
5511
192
        (*err) += "`path` field is missing in animation.channels.target\n";
5512
192
      }
5513
192
      return false;
5514
192
    }
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.59k
  channel->sampler = samplerIndex;
5536
2.59k
  channel->target_node = targetIndex;
5537
5538
2.59k
  ParseExtrasAndExtensions(channel, err, o,
5539
2.59k
                           store_original_json_for_extras_and_extensions);
5540
5541
2.59k
  return true;
5542
2.78k
}
5543
5544
static bool ParseAnimation(Animation *animation, std::string *err,
5545
                           const detail::json &o,
5546
37.5k
                           bool store_original_json_for_extras_and_extensions) {
5547
37.5k
  {
5548
37.5k
    detail::json_const_iterator channelsIt;
5549
37.5k
    if (detail::FindMember(o, "channels", channelsIt) &&
5550
37.5k
        detail::IsArray(detail::GetValue(channelsIt))) {
5551
5.82k
      detail::json_const_array_iterator channelEnd =
5552
5.82k
          detail::ArrayEnd(detail::GetValue(channelsIt));
5553
5.82k
      for (detail::json_const_array_iterator i =
5554
5.82k
               detail::ArrayBegin(detail::GetValue(channelsIt));
5555
10.2k
           i != channelEnd; ++i) {
5556
4.42k
        AnimationChannel channel;
5557
4.42k
        if (ParseAnimationChannel(
5558
4.42k
                &channel, err, *i,
5559
4.42k
                store_original_json_for_extras_and_extensions)) {
5560
          // Only add the channel if the parsing succeeds.
5561
2.59k
          animation->channels.emplace_back(std::move(channel));
5562
2.59k
        }
5563
4.42k
      }
5564
5.82k
    }
5565
37.5k
  }
5566
5567
37.5k
  {
5568
37.5k
    detail::json_const_iterator samplerIt;
5569
37.5k
    if (detail::FindMember(o, "samplers", samplerIt) &&
5570
37.5k
        detail::IsArray(detail::GetValue(samplerIt))) {
5571
3.58k
      const detail::json &sampler_array = detail::GetValue(samplerIt);
5572
5573
3.58k
      detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5574
3.58k
      detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
5575
5576
3.58k
      for (; it != itEnd; ++it) {
5577
0
        const detail::json &s = *it;
5578
5579
0
        AnimationSampler sampler;
5580
0
        int inputIndex = -1;
5581
0
        int outputIndex = -1;
5582
0
        if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
5583
0
          if (err) {
5584
0
            (*err) += "`input` field is missing in animation.sampler\n";
5585
0
          }
5586
0
          return false;
5587
0
        }
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.58k
    }
5604
37.5k
  }
5605
5606
37.5k
  ParseStringProperty(&animation->name, err, o, "name", false);
5607
5608
37.5k
  ParseExtrasAndExtensions(animation, err, o,
5609
37.5k
                           store_original_json_for_extras_and_extensions);
5610
5611
37.5k
  return true;
5612
37.5k
}
5613
5614
static bool ParseSampler(Sampler *sampler, std::string *err,
5615
                         const detail::json &o,
5616
2.75k
                         bool store_original_json_for_extras_and_extensions) {
5617
2.75k
  ParseStringProperty(&sampler->name, err, o, "name", false);
5618
5619
2.75k
  int minFilter = -1;
5620
2.75k
  int magFilter = -1;
5621
2.75k
  int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5622
2.75k
  int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
5623
  // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
5624
2.75k
  ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5625
2.75k
  ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5626
2.75k
  ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5627
2.75k
  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.75k
  sampler->minFilter = minFilter;
5635
2.75k
  sampler->magFilter = magFilter;
5636
2.75k
  sampler->wrapS = wrapS;
5637
2.75k
  sampler->wrapT = wrapT;
5638
  // sampler->wrapR = wrapR;
5639
5640
2.75k
  ParseExtrasAndExtensions(sampler, err, o,
5641
2.75k
                           store_original_json_for_extras_and_extensions);
5642
5643
2.75k
  return true;
5644
2.75k
}
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
11
                        bool store_original_json_for_extras_and_extensions) {
5757
11
  if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5758
0
    return false;
5759
0
  }
5760
5761
11
  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
11
  } 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
11
  } else {
5814
11
    if (err) {
5815
11
      std::stringstream ss;
5816
11
      ss << "Invalid camera type: \"" << camera->type
5817
11
         << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5818
11
      (*err) += ss.str();
5819
11
    }
5820
11
    return false;
5821
11
  }
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
11
}
5830
5831
static bool ParseLight(Light *light, std::string *err, const detail::json &o,
5832
0
                       bool store_original_json_for_extras_and_extensions) {
5833
0
  if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5834
0
    return false;
5835
0
  }
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
656k
    bool store_original_json_for_extras_and_extensions) {
5946
656k
  ParseStringProperty(&source->name, err, o, "name", false);
5947
656k
  ParseStringProperty(&source->uri, err, o, "uri", false);
5948
5949
656k
  if (source->uri.empty()) {
5950
656k
    ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true);
5951
656k
    ParseStringProperty(&source->mimeType, err, o, "mimeType", true);
5952
656k
  }
5953
5954
656k
  ParseExtrasAndExtensions(source, err, o,
5955
656k
                           store_original_json_for_extras_and_extensions);
5956
5957
656k
  return true;
5958
656k
}
5959
5960
namespace detail {
5961
5962
template <typename Callback>
5963
61.6k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
61.6k
  detail::json_const_iterator itm;
5965
61.6k
  if (detail::FindMember(_v, member, itm) &&
5966
61.6k
      detail::IsArray(detail::GetValue(itm))) {
5967
4.33k
    const detail::json &root = detail::GetValue(itm);
5968
4.33k
    auto it = detail::ArrayBegin(root);
5969
4.33k
    auto end = detail::ArrayEnd(root);
5970
298k
    for (; it != end; ++it) {
5971
295k
      if (!cb(*it)) return false;
5972
295k
    }
5973
4.33k
  }
5974
60.0k
  return true;
5975
61.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.64k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.64k
  detail::json_const_iterator itm;
5965
4.64k
  if (detail::FindMember(_v, member, itm) &&
5966
4.64k
      detail::IsArray(detail::GetValue(itm))) {
5967
15
    const detail::json &root = detail::GetValue(itm);
5968
15
    auto it = detail::ArrayBegin(root);
5969
15
    auto end = detail::ArrayEnd(root);
5970
1.57k
    for (; it != end; ++it) {
5971
1.55k
      if (!cb(*it)) return false;
5972
1.55k
    }
5973
15
  }
5974
4.64k
  return true;
5975
4.64k
};
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.64k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.64k
  detail::json_const_iterator itm;
5965
4.64k
  if (detail::FindMember(_v, member, itm) &&
5966
4.64k
      detail::IsArray(detail::GetValue(itm))) {
5967
13
    const detail::json &root = detail::GetValue(itm);
5968
13
    auto it = detail::ArrayBegin(root);
5969
13
    auto end = detail::ArrayEnd(root);
5970
97.5k
    for (; it != end; ++it) {
5971
97.5k
      if (!cb(*it)) return false;
5972
97.5k
    }
5973
13
  }
5974
4.64k
  return true;
5975
4.64k
};
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.64k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.64k
  detail::json_const_iterator itm;
5965
4.64k
  if (detail::FindMember(_v, member, itm) &&
5966
4.64k
      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.64k
  return true;
5975
4.64k
};
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.64k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.64k
  detail::json_const_iterator itm;
5965
4.64k
  if (detail::FindMember(_v, member, itm) &&
5966
4.64k
      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.64k
  return true;
5975
4.64k
};
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.64k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.64k
  detail::json_const_iterator itm;
5965
4.64k
  if (detail::FindMember(_v, member, itm) &&
5966
4.64k
      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.64k
  return true;
5975
4.64k
};
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.64k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.64k
  detail::json_const_iterator itm;
5965
4.64k
  if (detail::FindMember(_v, member, itm) &&
5966
4.64k
      detail::IsArray(detail::GetValue(itm))) {
5967
515
    const detail::json &root = detail::GetValue(itm);
5968
515
    auto it = detail::ArrayBegin(root);
5969
515
    auto end = detail::ArrayEnd(root);
5970
10.4k
    for (; it != end; ++it) {
5971
9.90k
      if (!cb(*it)) return false;
5972
9.90k
    }
5973
515
  }
5974
4.64k
  return true;
5975
4.64k
};
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.64k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.64k
  detail::json_const_iterator itm;
5965
4.64k
  if (detail::FindMember(_v, member, itm) &&
5966
4.64k
      detail::IsArray(detail::GetValue(itm))) {
5967
220
    const detail::json &root = detail::GetValue(itm);
5968
220
    auto it = detail::ArrayBegin(root);
5969
220
    auto end = detail::ArrayEnd(root);
5970
10.1k
    for (; it != end; ++it) {
5971
9.94k
      if (!cb(*it)) return false;
5972
9.94k
    }
5973
220
  }
5974
4.61k
  return true;
5975
4.64k
};
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.61k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.61k
  detail::json_const_iterator itm;
5965
4.61k
  if (detail::FindMember(_v, member, itm) &&
5966
4.61k
      detail::IsArray(detail::GetValue(itm))) {
5967
317
    const detail::json &root = detail::GetValue(itm);
5968
317
    auto it = detail::ArrayBegin(root);
5969
317
    auto end = detail::ArrayEnd(root);
5970
30.3k
    for (; it != end; ++it) {
5971
30.0k
      if (!cb(*it)) return false;
5972
30.0k
    }
5973
317
  }
5974
4.58k
  return true;
5975
4.61k
};
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.58k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.58k
  detail::json_const_iterator itm;
5965
4.58k
  if (detail::FindMember(_v, member, itm) &&
5966
4.58k
      detail::IsArray(detail::GetValue(itm))) {
5967
717
    const detail::json &root = detail::GetValue(itm);
5968
717
    auto it = detail::ArrayBegin(root);
5969
717
    auto end = detail::ArrayEnd(root);
5970
51.3k
    for (; it != end; ++it) {
5971
50.7k
      if (!cb(*it)) return false;
5972
50.7k
    }
5973
717
  }
5974
4.55k
  return true;
5975
4.58k
};
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.55k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
4.55k
  detail::json_const_iterator itm;
5965
4.55k
  if (detail::FindMember(_v, member, itm) &&
5966
4.55k
      detail::IsArray(detail::GetValue(itm))) {
5967
2.32k
    const detail::json &root = detail::GetValue(itm);
5968
2.32k
    auto it = detail::ArrayBegin(root);
5969
2.32k
    auto end = detail::ArrayEnd(root);
5970
54.5k
    for (; it != end; ++it) {
5971
53.6k
      if (!cb(*it)) return false;
5972
53.6k
    }
5973
2.32k
  }
5974
3.07k
  return true;
5975
4.55k
};
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
3.07k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
3.07k
  detail::json_const_iterator itm;
5965
3.07k
  if (detail::FindMember(_v, member, itm) &&
5966
3.07k
      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
1.74k
    for (; it != end; ++it) {
5971
1.71k
      if (!cb(*it)) return false;
5972
1.71k
    }
5973
35
  }
5974
3.07k
  return true;
5975
3.07k
};
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
3.07k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
3.07k
  detail::json_const_iterator itm;
5965
3.07k
  if (detail::FindMember(_v, member, itm) &&
5966
3.07k
      detail::IsArray(detail::GetValue(itm))) {
5967
125
    const detail::json &root = detail::GetValue(itm);
5968
125
    auto it = detail::ArrayBegin(root);
5969
125
    auto end = detail::ArrayEnd(root);
5970
37.6k
    for (; it != end; ++it) {
5971
37.5k
      if (!cb(*it)) return false;
5972
37.5k
    }
5973
125
  }
5974
3.07k
  return true;
5975
3.07k
};
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
3.07k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
3.07k
  detail::json_const_iterator itm;
5965
3.07k
  if (detail::FindMember(_v, member, itm) &&
5966
3.07k
      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
3.07k
  return true;
5975
3.07k
};
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
3.07k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
3.07k
  detail::json_const_iterator itm;
5965
3.07k
  if (detail::FindMember(_v, member, itm) &&
5966
3.07k
      detail::IsArray(detail::GetValue(itm))) {
5967
36
    const detail::json &root = detail::GetValue(itm);
5968
36
    auto it = detail::ArrayBegin(root);
5969
36
    auto end = detail::ArrayEnd(root);
5970
2.79k
    for (; it != end; ++it) {
5971
2.75k
      if (!cb(*it)) return false;
5972
2.75k
    }
5973
36
  }
5974
3.07k
  return true;
5975
3.07k
};
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
3.07k
bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
5964
3.07k
  detail::json_const_iterator itm;
5965
3.07k
  if (detail::FindMember(_v, member, itm) &&
5966
3.07k
      detail::IsArray(detail::GetValue(itm))) {
5967
11
    const detail::json &root = detail::GetValue(itm);
5968
11
    auto it = detail::ArrayBegin(root);
5969
11
    auto end = detail::ArrayEnd(root);
5970
11
    for (; it != end; ++it) {
5971
11
      if (!cb(*it)) return false;
5972
11
    }
5973
11
  }
5974
3.06k
  return true;
5975
3.07k
};
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
5.35k
                              unsigned int check_sections) {
5984
5.35k
  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
5.35k
  detail::JsonDocument v;
5992
5993
5.35k
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5994
5.35k
     defined(_CPPUNWIND)) &&                               \
5995
5.35k
    !defined(TINYGLTF_NOEXCEPTION)
5996
5.35k
  try {
5997
5.35k
    detail::JsonParse(v, json_str, json_str_length, true);
5998
5999
5.35k
  } catch (const std::exception &e) {
6000
639
    if (err) {
6001
639
      (*err) = e.what();
6002
639
    }
6003
639
    return false;
6004
639
  }
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.71k
  if (!detail::IsObject(v)) {
6020
    // root is not an object.
6021
33
    if (err) {
6022
33
      (*err) = "Root element is not a JSON object\n";
6023
33
    }
6024
33
    return false;
6025
33
  }
6026
6027
4.68k
  {
6028
4.68k
    bool version_found = false;
6029
4.68k
    detail::json_const_iterator it;
6030
4.68k
    if (detail::FindMember(v, "asset", it) &&
6031
4.68k
        detail::IsObject(detail::GetValue(it))) {
6032
4.65k
      auto &itObj = detail::GetValue(it);
6033
4.65k
      detail::json_const_iterator version_it;
6034
4.65k
      std::string versionStr;
6035
4.65k
      if (detail::FindMember(itObj, "version", version_it) &&
6036
4.65k
          detail::GetString(detail::GetValue(version_it), versionStr)) {
6037
4.64k
        version_found = true;
6038
4.64k
      }
6039
4.65k
    }
6040
4.68k
    if (version_found) {
6041
      // OK
6042
4.64k
    } else if (check_sections & REQUIRE_VERSION) {
6043
35
      if (err) {
6044
35
        (*err) += "\"asset\" object not found in .gltf or not an object type\n";
6045
35
      }
6046
35
      return false;
6047
35
    }
6048
4.68k
  }
6049
6050
  // scene is not mandatory.
6051
  // FIXME Maybe a better way to handle it than removing the code
6052
6053
4.64k
  auto IsArrayMemberPresent = [](const detail::json &_v,
6054
4.64k
                                 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.64k
  {
6061
4.64k
    if ((check_sections & REQUIRE_SCENES) &&
6062
4.64k
        !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.64k
  }
6069
6070
4.64k
  {
6071
4.64k
    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.64k
  }
6078
6079
4.64k
  {
6080
4.64k
    if ((check_sections & REQUIRE_ACCESSORS) &&
6081
4.64k
        !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.64k
  }
6088
6089
4.64k
  {
6090
4.64k
    if ((check_sections & REQUIRE_BUFFERS) &&
6091
4.64k
        !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.64k
  }
6098
6099
4.64k
  {
6100
4.64k
    if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
6101
4.64k
        !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.64k
  }
6108
6109
  // Reset the model
6110
4.64k
  (*model) = Model();
6111
6112
  // 1. Parse Asset
6113
4.64k
  {
6114
4.64k
    detail::json_const_iterator it;
6115
4.64k
    if (detail::FindMember(v, "asset", it) &&
6116
4.64k
        detail::IsObject(detail::GetValue(it))) {
6117
4.64k
      const detail::json &root = detail::GetValue(it);
6118
6119
4.64k
      ParseAsset(&model->asset, err, root,
6120
4.64k
                 store_original_json_for_extras_and_extensions_);
6121
4.64k
    }
6122
4.64k
  }
6123
6124
4.64k
  using detail::ForEachInArray;
6125
6126
  // 2. Parse extensionUsed
6127
4.64k
  {
6128
4.64k
    ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
6129
1.55k
      std::string str;
6130
1.55k
      detail::GetString(o, str);
6131
1.55k
      model->extensionsUsed.emplace_back(std::move(str));
6132
1.55k
      return true;
6133
1.55k
    });
6134
4.64k
  }
6135
6136
4.64k
  {
6137
97.5k
    ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
6138
97.5k
      std::string str;
6139
97.5k
      detail::GetString(o, str);
6140
97.5k
      model->extensionsRequired.emplace_back(std::move(str));
6141
97.5k
      return true;
6142
97.5k
    });
6143
4.64k
  }
6144
6145
  // 3. Parse Buffer
6146
4.64k
  {
6147
4.64k
    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.64k
    if (!success) {
6167
0
      return false;
6168
0
    }
6169
4.64k
  }
6170
  // 4. Parse BufferView
6171
4.64k
  {
6172
4.64k
    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.64k
    if (!success) {
6190
0
      return false;
6191
0
    }
6192
4.64k
  }
6193
6194
  // 5. Parse Accessor
6195
4.64k
  {
6196
4.64k
    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.64k
    if (!success) {
6214
0
      return false;
6215
0
    }
6216
4.64k
  }
6217
6218
  // 6. Parse Mesh
6219
4.64k
  {
6220
9.90k
    bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
6221
9.90k
      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
9.90k
      Mesh mesh;
6228
9.90k
      if (!ParseMesh(&mesh, model, err, warn, o,
6229
9.90k
                     store_original_json_for_extras_and_extensions_,
6230
9.90k
                     strictness_)) {
6231
0
        return false;
6232
0
      }
6233
6234
9.90k
      model->meshes.emplace_back(std::move(mesh));
6235
9.90k
      return true;
6236
9.90k
    });
6237
6238
4.64k
    if (!success) {
6239
0
      return false;
6240
0
    }
6241
4.64k
  }
6242
6243
  // Assign missing bufferView target types
6244
  // - Look for missing Mesh indices
6245
  // - Look for missing Mesh attributes
6246
9.90k
  for (auto &mesh : model->meshes) {
6247
37.8k
    for (auto &primitive : mesh.primitives) {
6248
37.8k
      if (primitive.indices >
6249
37.8k
          -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
37.8k
      for (auto &attribute : primitive.attributes) {
6277
6.32k
        const auto accessorsIndex = size_t(attribute.second);
6278
6.32k
        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
6.32k
      }
6287
6288
37.8k
      for (auto &target : primitive.targets) {
6289
21.4k
        for (auto &attribute : target) {
6290
7.76k
          const auto accessorsIndex = size_t(attribute.second);
6291
7.76k
          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.76k
        }
6301
21.4k
      }
6302
37.8k
    }
6303
9.90k
  }
6304
6305
  // 7. Parse Node
6306
4.64k
  {
6307
9.94k
    bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
6308
9.94k
      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
9.94k
      Node node;
6315
9.94k
      if (!ParseNode(&node, err, o,
6316
9.94k
                     store_original_json_for_extras_and_extensions_)) {
6317
33
        return false;
6318
33
      }
6319
6320
9.91k
      model->nodes.emplace_back(std::move(node));
6321
9.91k
      return true;
6322
9.94k
    });
6323
6324
4.64k
    if (!success) {
6325
33
      return false;
6326
33
    }
6327
4.64k
  }
6328
6329
  // 8. Parse scenes.
6330
4.61k
  {
6331
30.0k
    bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
6332
30.0k
      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
30.0k
      Scene scene;
6340
30.0k
      if (!ParseScene(&scene, err, o,
6341
30.0k
                      store_original_json_for_extras_and_extensions_)) {
6342
30
        return false;
6343
30
      }
6344
6345
30.0k
      model->scenes.emplace_back(std::move(scene));
6346
30.0k
      return true;
6347
30.0k
    });
6348
6349
4.61k
    if (!success) {
6350
30
      return false;
6351
30
    }
6352
4.61k
  }
6353
6354
  // 9. Parse default scenes.
6355
4.58k
  {
6356
4.58k
    detail::json_const_iterator rootIt;
6357
4.58k
    int iVal;
6358
4.58k
    if (detail::FindMember(v, "scene", rootIt) &&
6359
4.58k
        detail::GetInt(detail::GetValue(rootIt), iVal)) {
6360
0
      model->defaultScene = iVal;
6361
0
    }
6362
4.58k
  }
6363
6364
  // 10. Parse Material
6365
4.58k
  {
6366
50.7k
    bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
6367
50.7k
      if (!detail::IsObject(o)) {
6368
21
        if (err) {
6369
21
          (*err) += "`materials' does not contain an JSON object.";
6370
21
        }
6371
21
        return false;
6372
21
      }
6373
50.6k
      Material material;
6374
50.6k
      ParseStringProperty(&material.name, err, o, "name", false);
6375
6376
50.6k
      if (!ParseMaterial(&material, err, warn, o,
6377
50.6k
                         store_original_json_for_extras_and_extensions_,
6378
50.6k
                         strictness_)) {
6379
7
        return false;
6380
7
      }
6381
6382
50.6k
      model->materials.emplace_back(std::move(material));
6383
50.6k
      return true;
6384
50.6k
    });
6385
6386
4.58k
    if (!success) {
6387
28
      return false;
6388
28
    }
6389
4.58k
  }
6390
6391
  // 11. Parse Image
6392
4.55k
  void *load_image_user_data{nullptr};
6393
6394
4.55k
  LoadImageDataOption load_image_option;
6395
6396
4.55k
  if (user_image_loader_) {
6397
    // Use user supplied pointer
6398
0
    load_image_user_data = load_image_user_data_;
6399
4.55k
  } else {
6400
4.55k
    load_image_option.preserve_channels = preserve_image_channels_;
6401
4.55k
    load_image_option.as_is = images_as_is_;
6402
4.55k
    load_image_user_data = reinterpret_cast<void *>(&load_image_option);
6403
4.55k
  }
6404
6405
4.55k
  {
6406
4.55k
    int idx = 0;
6407
53.6k
    bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
6408
53.6k
      if (!detail::IsObject(o)) {
6409
17
        if (err) {
6410
17
          (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
6411
17
        }
6412
17
        return false;
6413
17
      }
6414
53.6k
      Image image;
6415
53.6k
      if (!ParseImage(&image, idx, err, warn, o,
6416
53.6k
                      store_original_json_for_extras_and_extensions_, base_dir,
6417
53.6k
                      max_external_file_size_, &fs, &uri_cb,
6418
53.6k
                      this->LoadImageData, load_image_user_data)) {
6419
1.46k
        return false;
6420
1.46k
      }
6421
6422
52.2k
      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
52.2k
      model->images.emplace_back(std::move(image));
6472
52.2k
      ++idx;
6473
52.2k
      return true;
6474
52.2k
    });
6475
6476
4.55k
    if (!success) {
6477
1.48k
      return false;
6478
1.48k
    }
6479
4.55k
  }
6480
6481
  // 12. Parse Texture
6482
3.07k
  {
6483
3.07k
    bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
6484
1.71k
      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.71k
      Texture texture;
6491
1.71k
      if (!ParseTexture(&texture, err, o,
6492
1.71k
                        store_original_json_for_extras_and_extensions_,
6493
1.71k
                        base_dir)) {
6494
0
        return false;
6495
0
      }
6496
6497
1.71k
      model->textures.emplace_back(std::move(texture));
6498
1.71k
      return true;
6499
1.71k
    });
6500
6501
3.07k
    if (!success) {
6502
0
      return false;
6503
0
    }
6504
3.07k
  }
6505
6506
  // 13. Parse Animation
6507
3.07k
  {
6508
37.5k
    bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
6509
37.5k
      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
37.5k
      Animation animation;
6516
37.5k
      if (!ParseAnimation(&animation, err, o,
6517
37.5k
                          store_original_json_for_extras_and_extensions_)) {
6518
0
        return false;
6519
0
      }
6520
6521
37.5k
      model->animations.emplace_back(std::move(animation));
6522
37.5k
      return true;
6523
37.5k
    });
6524
6525
3.07k
    if (!success) {
6526
0
      return false;
6527
0
    }
6528
3.07k
  }
6529
6530
  // 14. Parse Skin
6531
3.07k
  {
6532
3.07k
    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
3.07k
    if (!success) {
6550
0
      return false;
6551
0
    }
6552
3.07k
  }
6553
6554
  // 15. Parse Sampler
6555
3.07k
  {
6556
3.07k
    bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
6557
2.75k
      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.75k
      Sampler sampler;
6564
2.75k
      if (!ParseSampler(&sampler, err, o,
6565
2.75k
                        store_original_json_for_extras_and_extensions_)) {
6566
0
        return false;
6567
0
      }
6568
6569
2.75k
      model->samplers.emplace_back(std::move(sampler));
6570
2.75k
      return true;
6571
2.75k
    });
6572
6573
3.07k
    if (!success) {
6574
0
      return false;
6575
0
    }
6576
3.07k
  }
6577
6578
  // 16. Parse Camera
6579
3.07k
  {
6580
3.07k
    bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
6581
11
      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
11
      Camera camera;
6588
11
      if (!ParseCamera(&camera, err, o,
6589
11
                       store_original_json_for_extras_and_extensions_)) {
6590
11
        return false;
6591
11
      }
6592
6593
0
      model->cameras.emplace_back(std::move(camera));
6594
0
      return true;
6595
11
    });
6596
6597
3.07k
    if (!success) {
6598
11
      return false;
6599
11
    }
6600
3.07k
  }
6601
6602
  // 17. Parse Extras & Extensions
6603
3.06k
  ParseExtrasAndExtensions(model, err, v,
6604
3.06k
                           store_original_json_for_extras_and_extensions_);
6605
6606
  // 18. Specific extension implementations
6607
3.06k
  {
6608
3.06k
    detail::json_const_iterator rootIt;
6609
3.06k
    if (detail::FindMember(v, "extensions", rootIt) &&
6610
3.06k
        detail::IsObject(detail::GetValue(rootIt))) {
6611
231
      const detail::json &root = detail::GetValue(rootIt);
6612
6613
231
      detail::json_const_iterator it(detail::ObjectBegin(root));
6614
231
      detail::json_const_iterator itEnd(detail::ObjectEnd(root));
6615
3.93k
      for (; it != itEnd; ++it) {
6616
        // parse KHR_lights_punctual extension
6617
3.70k
        std::string key(detail::GetKey(it));
6618
3.70k
        if ((key == "KHR_lights_punctual") &&
6619
3.70k
            detail::IsObject(detail::GetValue(it))) {
6620
176
          const detail::json &object = detail::GetValue(it);
6621
176
          detail::json_const_iterator itLight;
6622
176
          if (detail::FindMember(object, "lights", itLight)) {
6623
141
            const detail::json &lights = detail::GetValue(itLight);
6624
141
            if (!detail::IsArray(lights)) {
6625
107
              continue;
6626
107
            }
6627
6628
34
            auto arrayIt(detail::ArrayBegin(lights));
6629
34
            auto arrayItEnd(detail::ArrayEnd(lights));
6630
34
            for (; arrayIt != arrayItEnd; ++arrayIt) {
6631
0
              Light light;
6632
0
              if (!ParseLight(&light, err, *arrayIt,
6633
0
                              store_original_json_for_extras_and_extensions_)) {
6634
0
                return false;
6635
0
              }
6636
0
              model->lights.emplace_back(std::move(light));
6637
0
            }
6638
34
          }
6639
176
        }
6640
        // parse KHR_audio extension
6641
3.59k
        if ((key == "KHR_audio") && detail::IsObject(detail::GetValue(it))) {
6642
886
          const detail::json &object = detail::GetValue(it);
6643
886
          detail::json_const_iterator itKhrAudio;
6644
886
          if (detail::FindMember(object, "emitters", itKhrAudio)) {
6645
67
            const detail::json &emitters = detail::GetValue(itKhrAudio);
6646
67
            if (!detail::IsArray(emitters)) {
6647
66
              continue;
6648
66
            }
6649
6650
1
            auto arrayIt(detail::ArrayBegin(emitters));
6651
1
            auto arrayItEnd(detail::ArrayEnd(emitters));
6652
1
            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
1
          }
6662
6663
820
          if (detail::FindMember(object, "sources", itKhrAudio)) {
6664
313
            const detail::json &sources = detail::GetValue(itKhrAudio);
6665
313
            if (!detail::IsArray(sources)) {
6666
229
              continue;
6667
229
            }
6668
6669
84
            auto arrayIt(detail::ArrayBegin(sources));
6670
84
            auto arrayItEnd(detail::ArrayEnd(sources));
6671
657k
            for (; arrayIt != arrayItEnd; ++arrayIt) {
6672
656k
              AudioSource source;
6673
656k
              if (!ParseAudioSource(
6674
656k
                      &source, err, *arrayIt,
6675
656k
                      store_original_json_for_extras_and_extensions_)) {
6676
0
                return false;
6677
0
              }
6678
656k
              model->audioSources.emplace_back(std::move(source));
6679
656k
            }
6680
84
          }
6681
820
        }
6682
3.59k
      }
6683
231
    }
6684
3.06k
  }
6685
6686
3.06k
  return true;
6687
3.06k
}
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
5.35k
                                   unsigned int check_sections) {
6694
5.35k
  is_binary_ = false;
6695
5.35k
  bin_data_ = nullptr;
6696
5.35k
  bin_size_ = 0;
6697
6698
5.35k
  return LoadFromString(model, err, warn, str, length, base_dir,
6699
5.35k
                        check_sections);
6700
5.35k
}
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