Coverage Report

Created: 2026-07-16 07:12

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