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