Coverage Report

Created: 2026-02-12 07:06

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