RavEngine
Loading...
Searching...
No Matches
tiny_gltf.h
1//
2// Header-only tiny glTF 2.0 loader and serializer.
3//
4//
5// The MIT License (MIT)
6//
7// Copyright (c) 2015 - 2019 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.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
30// to @Ybalrid)
31// - v2.1.0 Add draco compression.
32// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
33// - v2.0.0 glTF 2.0!.
34//
35// Tiny glTF loader is using following third party libraries:
36//
37// - jsonhpp: C++ JSON library.
38// - base64: base64 decode/encode library.
39// - stb_image: Image loading library.
40//
41#ifndef TINY_GLTF_H_
42#define TINY_GLTF_H_
43
44#include <array>
45#include <cassert>
46#include <cstdint>
47#include <cstdlib>
48#include <cstring>
49#include <map>
50#include <string>
51#include <vector>
52
53#ifdef __ANDROID__
54#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
55#include <android/asset_manager.h>
56#endif
57#endif
58
59namespace tinygltf {
60
61#define TINYGLTF_MODE_POINTS (0)
62#define TINYGLTF_MODE_LINE (1)
63#define TINYGLTF_MODE_LINE_LOOP (2)
64#define TINYGLTF_MODE_LINE_STRIP (3)
65#define TINYGLTF_MODE_TRIANGLES (4)
66#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
67#define TINYGLTF_MODE_TRIANGLE_FAN (6)
68
69#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
70#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
71#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
72#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
73#define TINYGLTF_COMPONENT_TYPE_INT (5124)
74#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
75#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
76#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
77
78#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
79#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
80#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
81#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
82#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
83#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
84
85#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
86#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
87#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
88
89// Redeclarations of the above for technique.parameters.
90#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
91#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
92#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
93#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
94#define TINYGLTF_PARAMETER_TYPE_INT (5124)
95#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
96#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
97
98#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
99#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
100#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
101
102#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
103#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
104#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
105
106#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
107#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
108#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
109#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
110
111#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
112#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
113#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
114
115#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
116
117// End parameter types
118
119#define TINYGLTF_TYPE_VEC2 (2)
120#define TINYGLTF_TYPE_VEC3 (3)
121#define TINYGLTF_TYPE_VEC4 (4)
122#define TINYGLTF_TYPE_MAT2 (32 + 2)
123#define TINYGLTF_TYPE_MAT3 (32 + 3)
124#define TINYGLTF_TYPE_MAT4 (32 + 4)
125#define TINYGLTF_TYPE_SCALAR (64 + 1)
126#define TINYGLTF_TYPE_VECTOR (64 + 4)
127#define TINYGLTF_TYPE_MATRIX (64 + 16)
128
129#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
130#define TINYGLTF_IMAGE_FORMAT_PNG (1)
131#define TINYGLTF_IMAGE_FORMAT_BMP (2)
132#define TINYGLTF_IMAGE_FORMAT_GIF (3)
133
134#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
135#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
136#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
137#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
138#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
139
140#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
141#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
142
143#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
144#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
145
146#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
147#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
148
149#define TINYGLTF_DOUBLE_EPS (1.e-12)
150#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
151
152#ifdef __ANDROID__
153#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
154AAssetManager *asset_manager = nullptr;
155#endif
156#endif
157
158typedef enum {
159 NULL_TYPE = 0,
160 NUMBER_TYPE = 1,
161 INT_TYPE = 2,
162 BOOL_TYPE = 3,
163 STRING_TYPE = 4,
164 ARRAY_TYPE = 5,
165 BINARY_TYPE = 6,
166 OBJECT_TYPE = 7
167} Type;
168
169static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
170 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
171 return 1;
172 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
173 return 1;
174 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
175 return 2;
176 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
177 return 2;
178 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
179 return 4;
180 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
181 return 4;
182 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
183 return 4;
184 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
185 return 8;
186 } else {
187 // Unknown componenty type
188 return -1;
189 }
190}
191
192static inline int32_t GetTypeSizeInBytes(uint32_t ty) {
193 if (ty == TINYGLTF_TYPE_SCALAR) {
194 return 1;
195 } else if (ty == TINYGLTF_TYPE_VEC2) {
196 return 2;
197 } else if (ty == TINYGLTF_TYPE_VEC3) {
198 return 3;
199 } else if (ty == TINYGLTF_TYPE_VEC4) {
200 return 4;
201 } else if (ty == TINYGLTF_TYPE_MAT2) {
202 return 4;
203 } else if (ty == TINYGLTF_TYPE_MAT3) {
204 return 9;
205 } else if (ty == TINYGLTF_TYPE_MAT4) {
206 return 16;
207 } else {
208 // Unknown componenty type
209 return -1;
210 }
211}
212
213bool IsDataURI(const std::string &in);
214bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
215 const std::string &in, size_t reqBytes, bool checkSize);
216
217#ifdef __clang__
218#pragma clang diagnostic push
219// Suppress warning for : static Value null_value
220// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
221#pragma clang diagnostic ignored "-Wexit-time-destructors"
222#pragma clang diagnostic ignored "-Wpadded"
223#endif
224
225// Simple class to represent JSON object
226class Value {
227 public:
228 typedef std::vector<Value> Array;
229 typedef std::map<std::string, Value> Object;
230
231 Value() : type_(NULL_TYPE) {}
232
233 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
234 explicit Value(int i) : type_(INT_TYPE) { int_value_ = i; }
235 explicit Value(double n) : type_(NUMBER_TYPE) { number_value_ = n; }
236 explicit Value(const std::string &s) : type_(STRING_TYPE) {
237 string_value_ = s;
238 }
239 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
240 binary_value_.resize(n);
241 memcpy(binary_value_.data(), p, n);
242 }
243 explicit Value(const Array &a) : type_(ARRAY_TYPE) {
244 array_value_ = Array(a);
245 }
246 explicit Value(const Object &o) : type_(OBJECT_TYPE) {
247 object_value_ = Object(o);
248 }
249
250 char Type() const { return static_cast<const char>(type_); }
251
252 bool IsBool() const { return (type_ == BOOL_TYPE); }
253
254 bool IsInt() const { return (type_ == INT_TYPE); }
255
256 bool IsNumber() const { return (type_ == NUMBER_TYPE); }
257
258 bool IsString() const { return (type_ == STRING_TYPE); }
259
260 bool IsBinary() const { return (type_ == BINARY_TYPE); }
261
262 bool IsArray() const { return (type_ == ARRAY_TYPE); }
263
264 bool IsObject() const { return (type_ == OBJECT_TYPE); }
265
266 // Accessor
267 template <typename T>
268 const T &Get() const;
269 template <typename T>
270 T &Get();
271
272 // Lookup value from an array
273 const Value &Get(int idx) const {
274 static Value null_value;
275 assert(IsArray());
276 assert(idx >= 0);
277 return (static_cast<size_t>(idx) < array_value_.size())
278 ? array_value_[static_cast<size_t>(idx)]
279 : null_value;
280 }
281
282 // Lookup value from a key-value pair
283 const Value &Get(const std::string &key) const {
284 static Value null_value;
285 assert(IsObject());
286 Object::const_iterator it = object_value_.find(key);
287 return (it != object_value_.end()) ? it->second : null_value;
288 }
289
290 size_t ArrayLen() const {
291 if (!IsArray()) return 0;
292 return array_value_.size();
293 }
294
295 // Valid only for object type.
296 bool Has(const std::string &key) const {
297 if (!IsObject()) return false;
298 Object::const_iterator it = object_value_.find(key);
299 return (it != object_value_.end()) ? true : false;
300 }
301
302 // List keys
303 std::vector<std::string> Keys() const {
304 std::vector<std::string> keys;
305 if (!IsObject()) return keys; // empty
306
307 for (Object::const_iterator it = object_value_.begin();
308 it != object_value_.end(); ++it) {
309 keys.push_back(it->first);
310 }
311
312 return keys;
313 }
314
315 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
316
317 bool operator==(const tinygltf::Value &other) const;
318
319 protected:
320 int type_;
321
322 int int_value_;
323 double number_value_;
324 std::string string_value_;
325 std::vector<unsigned char> binary_value_;
326 Array array_value_;
327 Object object_value_;
328 bool boolean_value_;
329};
330
331#ifdef __clang__
332#pragma clang diagnostic pop
333#endif
334
335#define TINYGLTF_VALUE_GET(ctype, var) \
336 template <> \
337 inline const ctype &Value::Get<ctype>() const { \
338 return var; \
339 } \
340 template <> \
341 inline ctype &Value::Get<ctype>() { \
342 return var; \
343 }
344TINYGLTF_VALUE_GET(bool, boolean_value_)
345TINYGLTF_VALUE_GET(double, number_value_)
346TINYGLTF_VALUE_GET(int, int_value_)
347TINYGLTF_VALUE_GET(std::string, string_value_)
348TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
349TINYGLTF_VALUE_GET(Value::Array, array_value_)
350TINYGLTF_VALUE_GET(Value::Object, object_value_)
351#undef TINYGLTF_VALUE_GET
352
353#ifdef __clang__
354#pragma clang diagnostic push
355#pragma clang diagnostic ignored "-Wc++98-compat"
356#pragma clang diagnostic ignored "-Wpadded"
357#endif
358
360using ColorValue = std::array<double, 4>;
361
362struct Parameter {
363 bool bool_value = false;
364 bool has_number_value = false;
365 std::string string_value;
366 std::vector<double> number_array;
367 std::map<std::string, double> json_double_value;
368 double number_value = 0.0;
369 // context sensitive methods. depending the type of the Parameter you are
370 // accessing, these are either valid or not
371 // If this parameter represent a texture map in a material, will return the
372 // texture index
373
377 int TextureIndex() const {
378 const auto it = json_double_value.find("index");
379 if (it != std::end(json_double_value)) {
380 return int(it->second);
381 }
382 return -1;
383 }
384
388 int TextureTexCoord() const {
389 const auto it = json_double_value.find("texCoord");
390 if (it != std::end(json_double_value)) {
391 return int(it->second);
392 }
393 return 0;
394 }
395
399 double Factor() const { return number_value; }
400
404 ColorValue ColorFactor() const {
405 return {
406 {// this agregate intialize the std::array object, and uses C++11 RVO.
407 number_array[0], number_array[1], number_array[2],
408 (number_array.size() > 3 ? number_array[3] : 1.0)}};
409 }
410
411 bool operator==(const Parameter &) const;
412};
413
414#ifdef __clang__
415#pragma clang diagnostic pop
416#endif
417
418#ifdef __clang__
419#pragma clang diagnostic push
420#pragma clang diagnostic ignored "-Wpadded"
421#endif
422
423typedef std::map<std::string, Parameter> ParameterMap;
424typedef std::map<std::string, Value> ExtensionMap;
425
427 int sampler; // required
428 int target_node; // required (index of the node to target)
429 std::string target_path; // required in ["translation", "rotation", "scale",
430 // "weights"]
431 Value extras;
432
433 AnimationChannel() : sampler(-1), target_node(-1) {}
434 bool operator==(const AnimationChannel &) const;
435};
436
438 int input; // required
439 int output; // required
440 std::string interpolation; // in ["LINEAR", "STEP", "CATMULLROMSPLINE",
441 // "CUBICSPLINE"], default "LINEAR"
442 Value extras;
443
444 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
445 bool operator==(const AnimationSampler &) const;
446};
447
448struct Animation {
449 std::string name;
450 std::vector<AnimationChannel> channels;
451 std::vector<AnimationSampler> samplers;
452 Value extras;
453
454 bool operator==(const Animation &) const;
455};
456
457struct Skin {
458 std::string name;
459 int inverseBindMatrices; // required here but not in the spec
460 int skeleton; // The index of the node used as a skeleton root
461 std::vector<int> joints; // Indices of skeleton nodes
462
463 Skin() {
464 inverseBindMatrices = -1;
465 skeleton = -1;
466 }
467 bool operator==(const Skin &) const;
468};
469
470struct Sampler {
471 std::string name;
472 int minFilter; // ["NEAREST", "LINEAR", "NEAREST_MIPMAP_LINEAR",
473 // "LINEAR_MIPMAP_NEAREST", "NEAREST_MIPMAP_LINEAR",
474 // "LINEAR_MIPMAP_LINEAR"]
475 int magFilter; // ["NEAREST", "LINEAR"]
476 int wrapS; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
477 // "REPEAT"
478 int wrapT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default
479 // "REPEAT"
480 int wrapR; // TinyGLTF extension
481 Value extras;
482
483 Sampler()
484 : minFilter(TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR),
485 magFilter(TINYGLTF_TEXTURE_FILTER_LINEAR),
486 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
487 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT),
488 wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
489 bool operator==(const Sampler &) const;
490};
491
492struct Image {
493 std::string name;
494 int width;
495 int height;
496 int component;
497 int bits; // bit depth per channel. 8(byte), 16 or 32.
498 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
499 // UBYTE(bits = 8) or USHORT(bits = 16)
500 std::vector<unsigned char> image;
501 int bufferView; // (required if no uri)
502 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
503 // "image/bmp", "image/gif"]
504 std::string uri; // (required if no mimeType)
505 Value extras;
506 ExtensionMap extensions;
507
508 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
509 // compressed for "image/jpeg" mime) This feature is good if you use custom
510 // image loader function. (e.g. delayed decoding of images for faster glTF
511 // parsing) Default parser for Image does not provide as-is loading feature at
512 // the moment. (You can manipulate this by providing your own LoadImageData
513 // function)
514 bool as_is;
515
516 Image() : as_is(false) {
517 bufferView = -1;
518 width = -1;
519 height = -1;
520 component = -1;
521 }
522 bool operator==(const Image &) const;
523};
524
525struct Texture {
526 std::string name;
527
528 int sampler;
529 int source;
530 Value extras;
531 ExtensionMap extensions;
532
533 Texture() : sampler(-1), source(-1) {}
534 bool operator==(const Texture &) const;
535};
536
537// Each extension should be stored in a ParameterMap.
538// members not in the values could be included in the ParameterMap
539// to keep a single material model
540struct Material {
541 std::string name;
542
543 ParameterMap values; // PBR metal/roughness workflow
544 ParameterMap additionalValues; // normal/occlusion/emissive values
545
546 ExtensionMap extensions;
547 Value extras;
548
549 bool operator==(const Material &) const;
550};
551
553 std::string name;
554 int buffer; // Required
555 size_t byteOffset; // minimum 0, default 0
556 size_t byteLength; // required, minimum 1
557 size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 =
558 // understood to be tightly packed
559 int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
560 Value extras;
561 bool dracoDecoded; // Flag indicating this has been draco decoded
562
563 BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
564 bool operator==(const BufferView &) const;
565};
566
567struct Accessor {
568 int bufferView; // optional in spec but required here since sparse accessor
569 // are not supported
570 std::string name;
571 size_t byteOffset;
572 bool normalized; // optional.
573 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
574 size_t count; // required
575 int type; // (required) One of TINYGLTF_TYPE_*** ..
576 Value extras;
577
578 std::vector<double> minValues; // optional
579 std::vector<double> maxValues; // optional
580
581 struct {
582 int count;
583 bool isSparse;
584 struct {
585 int byteOffset;
586 int bufferView;
587 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
588 } indices;
589 struct {
590 int bufferView;
591 int byteOffset;
592 } values;
593 } sparse;
594
599 int ByteStride(const BufferView &bufferViewObject) const {
600 if (bufferViewObject.byteStride == 0) {
601 // Assume data is tightly packed.
602 int componentSizeInBytes =
603 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
604 if (componentSizeInBytes <= 0) {
605 return -1;
606 }
607
608 int typeSizeInBytes = GetTypeSizeInBytes(static_cast<uint32_t>(type));
609 if (typeSizeInBytes <= 0) {
610 return -1;
611 }
612
613 return componentSizeInBytes * typeSizeInBytes;
614 } else {
615 // Check if byteStride is a mulple of the size of the accessor's component
616 // type.
617 int componentSizeInBytes =
618 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
619 if (componentSizeInBytes <= 0) {
620 return -1;
621 }
622
623 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
624 return -1;
625 }
626 return static_cast<int>(bufferViewObject.byteStride);
627 }
628
629 return 0;
630 }
631
632 Accessor() {
633 bufferView = -1;
634 sparse.isSparse = false;
635 }
636 bool operator==(const tinygltf::Accessor &) const;
637};
638
640 double aspectRatio; // min > 0
641 double yfov; // required. min > 0
642 double zfar; // min > 0
643 double znear; // required. min > 0
644
646 : aspectRatio(0.0),
647 yfov(0.0),
648 zfar(0.0) // 0 = use infinite projecton matrix
649 ,
650 znear(0.0) {}
651 bool operator==(const PerspectiveCamera &) const;
652
653 ExtensionMap extensions;
654 Value extras;
655};
656
658 double xmag; // required. must not be zero.
659 double ymag; // required. must not be zero.
660 double zfar; // required. `zfar` must be greater than `znear`.
661 double znear; // required
662
663 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
664 bool operator==(const OrthographicCamera &) const;
665
666 ExtensionMap extensions;
667 Value extras;
668};
669
670struct Camera {
671 std::string type; // required. "perspective" or "orthographic"
672 std::string name;
673
674 PerspectiveCamera perspective;
675 OrthographicCamera orthographic;
676
677 Camera() {}
678 bool operator==(const Camera &) const;
679
680 ExtensionMap extensions;
681 Value extras;
682};
683
684struct Primitive {
685 std::map<std::string, int> attributes; // (required) A dictionary object of
686 // integer, where each integer
687 // is the index of the accessor
688 // containing an attribute.
689 int material; // The index of the material to apply to this primitive
690 // when rendering.
691 int indices; // The index of the accessor that contains the indices.
692 int mode; // one of TINYGLTF_MODE_***
693 std::vector<std::map<std::string, int> > targets; // array of morph targets,
694 // where each target is a dict with attribues in ["POSITION, "NORMAL",
695 // "TANGENT"] pointing
696 // to their corresponding accessors
697 ExtensionMap extensions;
698 Value extras;
699
700 Primitive() {
701 material = -1;
702 indices = -1;
703 }
704 bool operator==(const Primitive &) const;
705};
706
707struct Mesh {
708 std::string name;
709 std::vector<Primitive> primitives;
710 std::vector<double> weights; // weights to be applied to the Morph Targets
711 std::vector<std::map<std::string, int> > targets;
712 ExtensionMap extensions;
713 Value extras;
714
715 bool operator==(const Mesh &) const;
716};
717
718class Node {
719 public:
720 Node() : camera(-1), skin(-1), mesh(-1) {}
721
722 Node(const Node &rhs) {
723 camera = rhs.camera;
724
725 name = rhs.name;
726 skin = rhs.skin;
727 mesh = rhs.mesh;
728 children = rhs.children;
729 rotation = rhs.rotation;
730 scale = rhs.scale;
731 translation = rhs.translation;
732 matrix = rhs.matrix;
733 weights = rhs.weights;
734
735 extensions = rhs.extensions;
736 extras = rhs.extras;
737 }
738 ~Node() {}
739 bool operator==(const Node &) const;
740
741 int camera; // the index of the camera referenced by this node
742
743 std::string name;
744 int skin;
745 int mesh;
746 std::vector<int> children;
747 std::vector<double> rotation; // length must be 0 or 4
748 std::vector<double> scale; // length must be 0 or 3
749 std::vector<double> translation; // length must be 0 or 3
750 std::vector<double> matrix; // length must be 0 or 16
751 std::vector<double> weights; // The weights of the instantiated Morph Target
752
753 ExtensionMap extensions;
754 Value extras;
755};
756
757struct Buffer {
758 std::string name;
759 std::vector<unsigned char> data;
760 std::string
761 uri; // considered as required here but not in the spec (need to clarify)
762 Value extras;
763
764 bool operator==(const Buffer &) const;
765};
766
767struct Asset {
768 std::string version; // required
769 std::string generator;
770 std::string minVersion;
771 std::string copyright;
772 ExtensionMap extensions;
773 Value extras;
774
775 bool operator==(const Asset &) const;
776};
777
778struct Scene {
779 std::string name;
780 std::vector<int> nodes;
781
782 ExtensionMap extensions;
783 Value extras;
784
785 bool operator==(const Scene &) const;
786};
787
788struct Light {
789 std::string name;
790 std::vector<double> color;
791 std::string type;
792
793 bool operator==(const Light &) const;
794};
795
796class Model {
797 public:
798 Model() {}
799 ~Model() {}
800 bool operator==(const Model &) const;
801
802 std::vector<Accessor> accessors;
803 std::vector<Animation> animations;
804 std::vector<Buffer> buffers;
805 std::vector<BufferView> bufferViews;
806 std::vector<Material> materials;
807 std::vector<Mesh> meshes;
808 std::vector<Node> nodes;
809 std::vector<Texture> textures;
810 std::vector<Image> images;
811 std::vector<Skin> skins;
812 std::vector<Sampler> samplers;
813 std::vector<Camera> cameras;
814 std::vector<Scene> scenes;
815 std::vector<Light> lights;
816 ExtensionMap extensions;
817
818 int defaultScene;
819 std::vector<std::string> extensionsUsed;
820 std::vector<std::string> extensionsRequired;
821
822 Asset asset;
823
824 Value extras;
825};
826
827enum SectionCheck {
828 NO_REQUIRE = 0x00,
829 REQUIRE_SCENE = 0x01,
830 REQUIRE_SCENES = 0x02,
831 REQUIRE_NODES = 0x04,
832 REQUIRE_ACCESSORS = 0x08,
833 REQUIRE_BUFFERS = 0x10,
834 REQUIRE_BUFFER_VIEWS = 0x20,
835 REQUIRE_ALL = 0x3f
836};
837
841typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
842 std::string *, int, int,
843 const unsigned char *, int, void *);
844
848typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
849 Image *, bool, void *);
850
851#ifndef TINYGLTF_NO_STB_IMAGE
852// Declaration of default image loader callback
853bool LoadImageData(Image *image, const int image_idx, std::string *err,
854 std::string *warn, int req_width, int req_height,
855 const unsigned char *bytes, int size, void *);
856#endif
857
858#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
859// Declaration of default image writer callback
860bool WriteImageData(const std::string *basepath, const std::string *filename,
861 Image *image, bool embedImages, void *);
862#endif
863
867typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
868
872typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
873
877typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
878 std::string *, const std::string &,
879 void *);
880
884typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
885 const std::vector<unsigned char> &,
886 void *);
887
893 FileExistsFunction FileExists;
894 ExpandFilePathFunction ExpandFilePath;
895 ReadWholeFileFunction ReadWholeFile;
896 WriteWholeFileFunction WriteWholeFile;
897
898 void *user_data; // An argument that is passed to all fs callbacks
899};
900
901#ifndef TINYGLTF_NO_FS
902// Declaration of default filesystem callbacks
903
904bool FileExists(const std::string &abs_filename, void *);
905
906std::string ExpandFilePath(const std::string &filepath, void *);
907
908bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
909 const std::string &filepath, void *);
910
911bool WriteWholeFile(std::string *err, const std::string &filepath,
912 const std::vector<unsigned char> &contents, void *);
913#endif
914
915class TinyGLTF {
916 public:
917#ifdef __clang__
918#pragma clang diagnostic push
919#pragma clang diagnostic ignored "-Wc++98-compat"
920#endif
921
922 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
923
924#ifdef __clang__
925#pragma clang diagnostic pop
926#endif
927
928 ~TinyGLTF() {}
929
935 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
936 const std::string &filename,
937 unsigned int check_sections = REQUIRE_ALL);
938
945 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
946 const char *str, const unsigned int length,
947 const std::string &base_dir,
948 unsigned int check_sections = REQUIRE_ALL);
949
955 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
956 const std::string &filename,
957 unsigned int check_sections = REQUIRE_ALL);
958
965 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
966 const unsigned char *bytes,
967 const unsigned int length,
968 const std::string &base_dir = "",
969 unsigned int check_sections = REQUIRE_ALL);
970
974 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
975 bool embedImages, bool embedBuffers,
976 bool prettyPrint, bool writeBinary);
977
981 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
982
986 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
987
992
993 private:
1000 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1001 const char *str, const unsigned int length,
1002 const std::string &base_dir, unsigned int check_sections);
1003
1004 const unsigned char *bin_data_;
1005 size_t bin_size_;
1006 bool is_binary_;
1007
1008 FsCallbacks fs = {
1009#ifndef TINYGLTF_NO_FS
1010 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1011 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
1012
1013 nullptr // Fs callback user data
1014#else
1015 nullptr, nullptr, nullptr, nullptr,
1016
1017 nullptr // Fs callback user data
1018#endif
1019 };
1020
1021 LoadImageDataFunction LoadImageData =
1022#ifndef TINYGLTF_NO_STB_IMAGE
1023 &tinygltf::LoadImageData;
1024#else
1025 nullptr;
1026#endif
1027 void *load_image_user_data_ = reinterpret_cast<void *>(&fs);
1028
1029 WriteImageDataFunction WriteImageData =
1030#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1031 &tinygltf::WriteImageData;
1032#else
1033 nullptr;
1034#endif
1035 void *write_image_user_data_ = reinterpret_cast<void *>(&fs);
1036};
1037
1038#ifdef __clang__
1039#pragma clang diagnostic pop // -Wpadded
1040#endif
1041
1042} // namespace tinygltf
1043
1044#endif // TINY_GLTF_H_
1045
1046#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
1047#include <algorithm>
1048//#include <cassert>
1049#ifndef TINYGLTF_NO_FS
1050#include <fstream>
1051#endif
1052#include <sstream>
1053
1054#ifdef __clang__
1055// Disable some warnings for external files.
1056#pragma clang diagnostic push
1057#pragma clang diagnostic ignored "-Wfloat-equal"
1058#pragma clang diagnostic ignored "-Wexit-time-destructors"
1059#pragma clang diagnostic ignored "-Wconversion"
1060#pragma clang diagnostic ignored "-Wold-style-cast"
1061#pragma clang diagnostic ignored "-Wglobal-constructors"
1062#pragma clang diagnostic ignored "-Wreserved-id-macro"
1063#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1064#pragma clang diagnostic ignored "-Wpadded"
1065#pragma clang diagnostic ignored "-Wc++98-compat"
1066#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1067#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1068#pragma clang diagnostic ignored "-Wswitch-enum"
1069#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
1070#pragma clang diagnostic ignored "-Wweak-vtables"
1071#pragma clang diagnostic ignored "-Wcovered-switch-default"
1072#if __has_warning("-Wdouble-promotion")
1073#pragma clang diagnostic ignored "-Wdouble-promotion"
1074#endif
1075#if __has_warning("-Wcomma")
1076#pragma clang diagnostic ignored "-Wcomma"
1077#endif
1078#if __has_warning("-Wzero-as-null-pointer-constant")
1079#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1080#endif
1081#if __has_warning("-Wcast-qual")
1082#pragma clang diagnostic ignored "-Wcast-qual"
1083#endif
1084#if __has_warning("-Wmissing-variable-declarations")
1085#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1086#endif
1087#if __has_warning("-Wmissing-prototypes")
1088#pragma clang diagnostic ignored "-Wmissing-prototypes"
1089#endif
1090#if __has_warning("-Wcast-align")
1091#pragma clang diagnostic ignored "-Wcast-align"
1092#endif
1093#if __has_warning("-Wnewline-eof")
1094#pragma clang diagnostic ignored "-Wnewline-eof"
1095#endif
1096#if __has_warning("-Wunused-parameter")
1097#pragma clang diagnostic ignored "-Wunused-parameter"
1098#endif
1099#if __has_warning("-Wmismatched-tags")
1100#pragma clang diagnostic ignored "-Wmismatched-tags"
1101#endif
1102#endif
1103
1104// Disable GCC warnigs
1105#ifdef __GNUC__
1106#pragma GCC diagnostic push
1107#pragma GCC diagnostic ignored "-Wtype-limits"
1108#endif // __GNUC__
1109
1110#ifndef TINYGLTF_NO_INCLUDE_JSON
1111#include "json.hpp"
1112#endif
1113
1114#ifdef TINYGLTF_ENABLE_DRACO
1115#include "draco/compression/decode.h"
1116#include "draco/core/decoder_buffer.h"
1117#endif
1118
1119#ifndef TINYGLTF_NO_STB_IMAGE
1120#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
1121#include "stb_image.h"
1122#endif
1123#endif
1124
1125#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1126#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
1127#include "stb_image_write.h"
1128#endif
1129#endif
1130
1131#ifdef __clang__
1132#pragma clang diagnostic pop
1133#endif
1134
1135#ifdef __GNUC__
1136#pragma GCC diagnostic pop
1137#endif
1138
1139#ifdef _WIN32
1140
1141// issue 143.
1142// Define NOMINMAX to avoid min/max defines,
1143// but undef it after included windows.h
1144#ifndef NOMINMAX
1145#define TINYGLTF_INTERNAL_NOMINMAX
1146#define NOMINMAX
1147#endif
1148
1149#ifndef WIN32_LEAN_AND_MEAN
1150#define WIN32_LEAN_AND_MEAN
1151#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1152#endif
1153#include <windows.h> // include API for expanding a file path
1154
1155#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1156#undef WIN32_LEAN_AND_MEAN
1157#endif
1158
1159#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1160#undef NOMINMAX
1161#endif
1162
1163#elif !defined(__ANDROID__)
1164#include <wordexp.h>
1165#endif
1166
1167#if defined(__sparcv9)
1168// Big endian
1169#else
1170#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1171#define TINYGLTF_LITTLE_ENDIAN 1
1172#endif
1173#endif
1174
1175using nlohmann::json;
1176
1177#ifdef __APPLE__
1178#include "TargetConditionals.h"
1179#endif
1180
1181#ifdef __clang__
1182#pragma clang diagnostic push
1183#pragma clang diagnostic ignored "-Wc++98-compat"
1184#endif
1185
1186namespace tinygltf {
1187
1188// Equals function for Value, for recursivity
1189static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1190 if (one.Type() != other.Type()) return false;
1191
1192 switch (one.Type()) {
1193 case NULL_TYPE:
1194 return true;
1195 case BOOL_TYPE:
1196 return one.Get<bool>() == other.Get<bool>();
1197 case NUMBER_TYPE:
1198 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1199 case INT_TYPE:
1200 return one.Get<int>() == other.Get<int>();
1201 case OBJECT_TYPE: {
1202 auto oneObj = one.Get<tinygltf::Value::Object>();
1203 auto otherObj = other.Get<tinygltf::Value::Object>();
1204 if (oneObj.size() != otherObj.size()) return false;
1205 for (auto &it : oneObj) {
1206 auto otherIt = otherObj.find(it.first);
1207 if (otherIt == otherObj.end()) return false;
1208
1209 if (!Equals(it.second, otherIt->second)) return false;
1210 }
1211 return true;
1212 }
1213 case ARRAY_TYPE: {
1214 if (one.Size() != other.Size()) return false;
1215 for (int i = 0; i < int(one.Size()); ++i)
1216 if (!Equals(one.Get(i), other.Get(i))) return false;
1217 return true;
1218 }
1219 case STRING_TYPE:
1220 return one.Get<std::string>() == other.Get<std::string>();
1221 case BINARY_TYPE:
1222 return one.Get<std::vector<unsigned char> >() ==
1223 other.Get<std::vector<unsigned char> >();
1224 default: {
1225 // unhandled type
1226 return false;
1227 }
1228 }
1229}
1230
1231// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
1232static bool Equals(const std::vector<double> &one,
1233 const std::vector<double> &other) {
1234 if (one.size() != other.size()) return false;
1235 for (int i = 0; i < int(one.size()); ++i) {
1236 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1237 }
1238 return true;
1239}
1240
1241bool Accessor::operator==(const Accessor &other) const {
1242 return this->bufferView == other.bufferView &&
1243 this->byteOffset == other.byteOffset &&
1244 this->componentType == other.componentType &&
1245 this->count == other.count && this->extras == other.extras &&
1246 Equals(this->maxValues, other.maxValues) &&
1247 Equals(this->minValues, other.minValues) && this->name == other.name &&
1248 this->normalized == other.normalized && this->type == other.type;
1249}
1250bool Animation::operator==(const Animation &other) const {
1251 return this->channels == other.channels && this->extras == other.extras &&
1252 this->name == other.name && this->samplers == other.samplers;
1253}
1254bool AnimationChannel::operator==(const AnimationChannel &other) const {
1255 return this->extras == other.extras &&
1256 this->target_node == other.target_node &&
1257 this->target_path == other.target_path &&
1258 this->sampler == other.sampler;
1259}
1260bool AnimationSampler::operator==(const AnimationSampler &other) const {
1261 return this->extras == other.extras && this->input == other.input &&
1262 this->interpolation == other.interpolation &&
1263 this->output == other.output;
1264}
1265bool Asset::operator==(const Asset &other) const {
1266 return this->copyright == other.copyright &&
1267 this->extensions == other.extensions && this->extras == other.extras &&
1268 this->generator == other.generator &&
1269 this->minVersion == other.minVersion && this->version == other.version;
1270}
1271bool Buffer::operator==(const Buffer &other) const {
1272 return this->data == other.data && this->extras == other.extras &&
1273 this->name == other.name && this->uri == other.uri;
1274}
1275bool BufferView::operator==(const BufferView &other) const {
1276 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1277 this->byteOffset == other.byteOffset &&
1278 this->byteStride == other.byteStride && this->name == other.name &&
1279 this->target == other.target && this->extras == other.extras &&
1280 this->dracoDecoded == other.dracoDecoded;
1281}
1282bool Camera::operator==(const Camera &other) const {
1283 return this->name == other.name && this->extensions == other.extensions &&
1284 this->extras == other.extras &&
1285 this->orthographic == other.orthographic &&
1286 this->perspective == other.perspective && this->type == other.type;
1287}
1288bool Image::operator==(const Image &other) const {
1289 return this->bufferView == other.bufferView &&
1290 this->component == other.component && this->extras == other.extras &&
1291 this->height == other.height && this->image == other.image &&
1292 this->mimeType == other.mimeType && this->name == other.name &&
1293 this->uri == other.uri && this->width == other.width;
1294}
1295bool Light::operator==(const Light &other) const {
1296 return Equals(this->color, other.color) && this->name == other.name &&
1297 this->type == other.type;
1298}
1299bool Material::operator==(const Material &other) const {
1300 return this->additionalValues == other.additionalValues &&
1301 this->extensions == other.extensions && this->extras == other.extras &&
1302 this->name == other.name && this->values == other.values;
1303}
1304bool Mesh::operator==(const Mesh &other) const {
1305 return this->extensions == other.extensions && this->extras == other.extras &&
1306 this->name == other.name && this->primitives == other.primitives &&
1307 this->targets == other.targets && Equals(this->weights, other.weights);
1308}
1309bool Model::operator==(const Model &other) const {
1310 return this->accessors == other.accessors &&
1311 this->animations == other.animations && this->asset == other.asset &&
1312 this->buffers == other.buffers &&
1313 this->bufferViews == other.bufferViews &&
1314 this->cameras == other.cameras &&
1315 this->defaultScene == other.defaultScene &&
1316 this->extensions == other.extensions &&
1317 this->extensionsRequired == other.extensionsRequired &&
1318 this->extensionsUsed == other.extensionsUsed &&
1319 this->extras == other.extras && this->images == other.images &&
1320 this->lights == other.lights && this->materials == other.materials &&
1321 this->meshes == other.meshes && this->nodes == other.nodes &&
1322 this->samplers == other.samplers && this->scenes == other.scenes &&
1323 this->skins == other.skins && this->textures == other.textures;
1324}
1325bool Node::operator==(const Node &other) const {
1326 return this->camera == other.camera && this->children == other.children &&
1327 this->extensions == other.extensions && this->extras == other.extras &&
1328 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1329 this->name == other.name && Equals(this->rotation, other.rotation) &&
1330 Equals(this->scale, other.scale) && this->skin == other.skin &&
1331 Equals(this->translation, other.translation) &&
1332 Equals(this->weights, other.weights);
1333}
1334bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1335 return this->extensions == other.extensions && this->extras == other.extras &&
1336 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1337 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1338 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1339 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
1340}
1341bool Parameter::operator==(const Parameter &other) const {
1342 if (this->bool_value != other.bool_value ||
1343 this->has_number_value != other.has_number_value)
1344 return false;
1345
1346 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1347 return false;
1348
1349 if (this->json_double_value.size() != other.json_double_value.size())
1350 return false;
1351 for (auto &it : this->json_double_value) {
1352 auto otherIt = other.json_double_value.find(it.first);
1353 if (otherIt == other.json_double_value.end()) return false;
1354
1355 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1356 }
1357
1358 if (!Equals(this->number_array, other.number_array)) return false;
1359
1360 if (this->string_value != other.string_value) return false;
1361
1362 return true;
1363}
1364bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1365 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1366 this->extensions == other.extensions && this->extras == other.extras &&
1367 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1368 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1369 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
1370}
1371bool Primitive::operator==(const Primitive &other) const {
1372 return this->attributes == other.attributes && this->extras == other.extras &&
1373 this->indices == other.indices && this->material == other.material &&
1374 this->mode == other.mode && this->targets == other.targets;
1375}
1376bool Sampler::operator==(const Sampler &other) const {
1377 return this->extras == other.extras && this->magFilter == other.magFilter &&
1378 this->minFilter == other.minFilter && this->name == other.name &&
1379 this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
1380 this->wrapT == other.wrapT;
1381}
1382bool Scene::operator==(const Scene &other) const {
1383 return this->extensions == other.extensions && this->extras == other.extras &&
1384 this->name == other.name && this->nodes == other.nodes;
1385 ;
1386}
1387bool Skin::operator==(const Skin &other) const {
1388 return this->inverseBindMatrices == other.inverseBindMatrices &&
1389 this->joints == other.joints && this->name == other.name &&
1390 this->skeleton == other.skeleton;
1391}
1392bool Texture::operator==(const Texture &other) const {
1393 return this->extensions == other.extensions && this->extras == other.extras &&
1394 this->name == other.name && this->sampler == other.sampler &&
1395 this->source == other.source;
1396}
1397bool Value::operator==(const Value &other) const {
1398 return Equals(*this, other);
1399}
1400
1401static void swap4(unsigned int *val) {
1402#ifdef TINYGLTF_LITTLE_ENDIAN
1403 (void)val;
1404#else
1405 unsigned int tmp = *val;
1406 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
1407 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
1408
1409 dst[0] = src[3];
1410 dst[1] = src[2];
1411 dst[2] = src[1];
1412 dst[3] = src[0];
1413#endif
1414}
1415
1416static std::string JoinPath(const std::string &path0,
1417 const std::string &path1) {
1418 if (path0.empty()) {
1419 return path1;
1420 } else {
1421 // check '/'
1422 char lastChar = *path0.rbegin();
1423 if (lastChar != '/') {
1424 return path0 + std::string("/") + path1;
1425 } else {
1426 return path0 + path1;
1427 }
1428 }
1429}
1430
1431static std::string FindFile(const std::vector<std::string> &paths,
1432 const std::string &filepath, FsCallbacks *fs) {
1433 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
1434 fs->FileExists == nullptr) {
1435 // Error, fs callback[s] missing
1436 return std::string();
1437 }
1438
1439 for (size_t i = 0; i < paths.size(); i++) {
1440 std::string absPath =
1441 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
1442 if (fs->FileExists(absPath, fs->user_data)) {
1443 return absPath;
1444 }
1445 }
1446
1447 return std::string();
1448}
1449
1450static std::string GetFilePathExtension(const std::string &FileName) {
1451 if (FileName.find_last_of(".") != std::string::npos)
1452 return FileName.substr(FileName.find_last_of(".") + 1);
1453 return "";
1454}
1455
1456static std::string GetBaseDir(const std::string &filepath) {
1457 if (filepath.find_last_of("/\\") != std::string::npos)
1458 return filepath.substr(0, filepath.find_last_of("/\\"));
1459 return "";
1460}
1461
1462// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
1463static std::string GetBaseFilename(const std::string &filepath) {
1464 return filepath.substr(filepath.find_last_of("/\\") + 1);
1465}
1466
1467std::string base64_encode(unsigned char const *, unsigned int len);
1468std::string base64_decode(std::string const &s);
1469
1470/*
1471 base64.cpp and base64.h
1472
1473 Copyright (C) 2004-2008 René Nyffenegger
1474
1475 This source code is provided 'as-is', without any express or implied
1476 warranty. In no event will the author be held liable for any damages
1477 arising from the use of this software.
1478
1479 Permission is granted to anyone to use this software for any purpose,
1480 including commercial applications, and to alter it and redistribute it
1481 freely, subject to the following restrictions:
1482
1483 1. The origin of this source code must not be misrepresented; you must not
1484 claim that you wrote the original source code. If you use this source code
1485 in a product, an acknowledgment in the product documentation would be
1486 appreciated but is not required.
1487
1488 2. Altered source versions must be plainly marked as such, and must not be
1489 misrepresented as being the original source code.
1490
1491 3. This notice may not be removed or altered from any source distribution.
1492
1493 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
1494
1495*/
1496
1497#ifdef __clang__
1498#pragma clang diagnostic push
1499#pragma clang diagnostic ignored "-Wexit-time-destructors"
1500#pragma clang diagnostic ignored "-Wglobal-constructors"
1501#pragma clang diagnostic ignored "-Wsign-conversion"
1502#pragma clang diagnostic ignored "-Wconversion"
1503#endif
1504static const std::string base64_chars =
1505 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1506 "abcdefghijklmnopqrstuvwxyz"
1507 "0123456789+/";
1508
1509static inline bool is_base64(unsigned char c) {
1510 return (isalnum(c) || (c == '+') || (c == '/'));
1511}
1512
1513std::string base64_encode(unsigned char const *bytes_to_encode,
1514 unsigned int in_len) {
1515 std::string ret;
1516 int i = 0;
1517 int j = 0;
1518 unsigned char char_array_3[3];
1519 unsigned char char_array_4[4];
1520
1521 while (in_len--) {
1522 char_array_3[i++] = *(bytes_to_encode++);
1523 if (i == 3) {
1524 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1525 char_array_4[1] =
1526 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1527 char_array_4[2] =
1528 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
1529 char_array_4[3] = char_array_3[2] & 0x3f;
1530
1531 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
1532 i = 0;
1533 }
1534 }
1535
1536 if (i) {
1537 for (j = i; j < 3; j++) char_array_3[j] = '\0';
1538
1539 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
1540 char_array_4[1] =
1541 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
1542 char_array_4[2] =
1543 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
1544
1545 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
1546
1547 while ((i++ < 3)) ret += '=';
1548 }
1549
1550 return ret;
1551}
1552
1553std::string base64_decode(std::string const &encoded_string) {
1554 int in_len = static_cast<int>(encoded_string.size());
1555 int i = 0;
1556 int j = 0;
1557 int in_ = 0;
1558 unsigned char char_array_4[4], char_array_3[3];
1559 std::string ret;
1560
1561 while (in_len-- && (encoded_string[in_] != '=') &&
1562 is_base64(encoded_string[in_])) {
1563 char_array_4[i++] = encoded_string[in_];
1564 in_++;
1565 if (i == 4) {
1566 for (i = 0; i < 4; i++)
1567 char_array_4[i] =
1568 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
1569
1570 char_array_3[0] =
1571 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1572 char_array_3[1] =
1573 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1574 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1575
1576 for (i = 0; (i < 3); i++) ret += char_array_3[i];
1577 i = 0;
1578 }
1579 }
1580
1581 if (i) {
1582 for (j = i; j < 4; j++) char_array_4[j] = 0;
1583
1584 for (j = 0; j < 4; j++)
1585 char_array_4[j] =
1586 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
1587
1588 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
1589 char_array_3[1] =
1590 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
1591 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
1592
1593 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
1594 }
1595
1596 return ret;
1597}
1598#ifdef __clang__
1599#pragma clang diagnostic pop
1600#endif
1601
1602static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
1603 std::string *warn, const std::string &filename,
1604 const std::string &basedir, bool required,
1605 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
1606 if (fs == nullptr || fs->FileExists == nullptr ||
1607 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
1608 // This is a developer error, assert() ?
1609 if (err) {
1610 (*err) += "FS callback[s] not set\n";
1611 }
1612 return false;
1613 }
1614
1615 std::string *failMsgOut = required ? err : warn;
1616
1617 out->clear();
1618
1619 std::vector<std::string> paths;
1620 paths.push_back(basedir);
1621 paths.push_back(".");
1622
1623 std::string filepath = FindFile(paths, filename, fs);
1624 if (filepath.empty() || filename.empty()) {
1625 if (failMsgOut) {
1626 (*failMsgOut) += "File not found : " + filename + "\n";
1627 }
1628 return false;
1629 }
1630
1631 std::vector<unsigned char> buf;
1632 std::string fileReadErr;
1633 bool fileRead =
1634 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
1635 if (!fileRead) {
1636 if (failMsgOut) {
1637 (*failMsgOut) +=
1638 "File read error : " + filepath + " : " + fileReadErr + "\n";
1639 }
1640 return false;
1641 }
1642
1643 size_t sz = buf.size();
1644 if (sz == 0) {
1645 if (failMsgOut) {
1646 (*failMsgOut) += "File is empty : " + filepath + "\n";
1647 }
1648 return false;
1649 }
1650
1651 if (checkSize) {
1652 if (reqBytes == sz) {
1653 out->swap(buf);
1654 return true;
1655 } else {
1656 std::stringstream ss;
1657 ss << "File size mismatch : " << filepath << ", requestedBytes "
1658 << reqBytes << ", but got " << sz << std::endl;
1659 if (failMsgOut) {
1660 (*failMsgOut) += ss.str();
1661 }
1662 return false;
1663 }
1664 }
1665
1666 out->swap(buf);
1667 return true;
1668}
1669
1670void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
1671 LoadImageData = func;
1672 load_image_user_data_ = user_data;
1673}
1674
1675#ifndef TINYGLTF_NO_STB_IMAGE
1676bool LoadImageData(Image *image, const int image_idx, std::string *err,
1677 std::string *warn, int req_width, int req_height,
1678 const unsigned char *bytes, int size, void *user_data) {
1679 (void)user_data;
1680 (void)warn;
1681
1682 int w, h, comp, req_comp;
1683
1684 unsigned char *data = nullptr;
1685
1686 // force 32-bit textures for common Vulkan compatibility. It appears that
1687 // some GPU drivers do not support 24-bit images for Vulkan
1688 req_comp = 4;
1689 int bits = 8;
1690 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
1691
1692 // It is possible that the image we want to load is a 16bit per channel image
1693 // We are going to attempt to load it as 16bit per channel, and if it worked,
1694 // set the image data accodingly. We are casting the returned pointer into
1695 // unsigned char, because we are representing "bytes". But we are updating
1696 // the Image metadata to signal that this image uses 2 bytes (16bits) per
1697 // channel:
1698 if (stbi_is_16_bit_from_memory(bytes, size)) {
1699 data = (unsigned char *)stbi_load_16_from_memory(bytes, size, &w, &h, &comp,
1700 req_comp);
1701 if (data) {
1702 bits = 16;
1703 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
1704 }
1705 }
1706
1707 // at this point, if data is still NULL, it means that the image wasn't
1708 // 16bit per channel, we are going to load it as a normal 8bit per channel
1709 // mage as we used to do:
1710 // if image cannot be decoded, ignore parsing and keep it by its path
1711 // don't break in this case
1712 // FIXME we should only enter this function if the image is embedded. If
1713 // image->uri references
1714 // an image file, it should be left as it is. Image loading should not be
1715 // mandatory (to support other formats)
1716 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
1717 if (!data) {
1718 // NOTE: you can use `warn` instead of `err`
1719 if (err) {
1720 (*err) +=
1721 "Unknown image format. STB cannot decode image data for image[" +
1722 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
1723 }
1724 return false;
1725 }
1726
1727 if (w < 1 || h < 1) {
1728 stbi_image_free(data);
1729 if (err) {
1730 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
1731 "] name = \"" + image->name + "\"\n";
1732 }
1733 return false;
1734 }
1735
1736 if (req_width > 0) {
1737 if (req_width != w) {
1738 stbi_image_free(data);
1739 if (err) {
1740 (*err) += "Image width mismatch for image[" +
1741 std::to_string(image_idx) + "] name = \"" + image->name +
1742 "\"\n";
1743 }
1744 return false;
1745 }
1746 }
1747
1748 if (req_height > 0) {
1749 if (req_height != h) {
1750 stbi_image_free(data);
1751 if (err) {
1752 (*err) += "Image height mismatch. for image[" +
1753 std::to_string(image_idx) + "] name = \"" + image->name +
1754 "\"\n";
1755 }
1756 return false;
1757 }
1758 }
1759
1760 image->width = w;
1761 image->height = h;
1762 image->component = req_comp;
1763 image->bits = bits;
1764 image->pixel_type = pixel_type;
1765 image->image.resize(static_cast<size_t>(w * h * req_comp) * (bits / 8));
1766 std::copy(data, data + w * h * req_comp * (bits / 8), image->image.begin());
1767 stbi_image_free(data);
1768
1769 return true;
1770}
1771#endif
1772
1773void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
1774 WriteImageData = func;
1775 write_image_user_data_ = user_data;
1776}
1777
1778#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1779static void WriteToMemory_stbi(void *context, void *data, int size) {
1780 std::vector<unsigned char> *buffer =
1781 reinterpret_cast<std::vector<unsigned char> *>(context);
1782
1783 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
1784
1785 buffer->insert(buffer->end(), pData, pData + size);
1786}
1787
1788bool WriteImageData(const std::string *basepath, const std::string *filename,
1789 Image *image, bool embedImages, void *fsPtr) {
1790 const std::string ext = GetFilePathExtension(*filename);
1791
1792 // Write image to temporary buffer
1793 std::string header;
1794 std::vector<unsigned char> data;
1795
1796 if (ext == "png") {
1797 if ((image->bits != 8) ||
1798 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
1799 // Unsupported pixel format
1800 return false;
1801 }
1802
1803 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
1804 image->height, image->component,
1805 &image->image[0], 0)) {
1806 return false;
1807 }
1808 header = "data:image/png;base64,";
1809 } else if (ext == "jpg") {
1810 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
1811 image->height, image->component,
1812 &image->image[0], 100)) {
1813 return false;
1814 }
1815 header = "data:image/jpeg;base64,";
1816 } else if (ext == "bmp") {
1817 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
1818 image->height, image->component,
1819 &image->image[0])) {
1820 return false;
1821 }
1822 header = "data:image/bmp;base64,";
1823 } else if (!embedImages) {
1824 // Error: can't output requested format to file
1825 return false;
1826 }
1827
1828 if (embedImages) {
1829 // Embed base64-encoded image into URI
1830 if (data.size()) {
1831 image->uri =
1832 header +
1833 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
1834 } else {
1835 // Throw error?
1836 }
1837 } else {
1838 // Write image to disc
1839 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
1840 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
1841 const std::string imagefilepath = JoinPath(*basepath, *filename);
1842 std::string writeError;
1843 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
1844 fs->user_data)) {
1845 // Could not write image file to disc; Throw error ?
1846 return false;
1847 }
1848 } else {
1849 // Throw error?
1850 }
1851 image->uri = *filename;
1852 }
1853
1854 return true;
1855}
1856#endif
1857
1858void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
1859
1860#ifndef TINYGLTF_NO_FS
1861// Default implementations of filesystem functions
1862
1863bool FileExists(const std::string &abs_filename, void *) {
1864 bool ret;
1865#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
1866 if (asset_manager) {
1867 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
1868 AASSET_MODE_STREAMING);
1869 if (!asset) {
1870 return false;
1871 }
1872 AAsset_close(asset);
1873 ret = true;
1874 } else {
1875 return false;
1876 }
1877#else
1878#ifdef _WIN32
1879 FILE *fp;
1880 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
1881 if (err != 0) {
1882 return false;
1883 }
1884#else
1885 FILE *fp = fopen(abs_filename.c_str(), "rb");
1886#endif
1887 if (fp) {
1888 ret = true;
1889 fclose(fp);
1890 } else {
1891 ret = false;
1892 }
1893#endif
1894
1895 return ret;
1896}
1897
1898std::string ExpandFilePath(const std::string &filepath, void *) {
1899#ifdef _WIN32
1900 DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0);
1901 char *str = new char[len];
1902 ExpandEnvironmentStringsA(filepath.c_str(), str, len);
1903
1904 std::string s(str);
1905
1906 delete[] str;
1907
1908 return s;
1909#else
1910
1911#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
1912 defined(__ANDROID__) || defined(__EMSCRIPTEN__)
1913 // no expansion
1914 std::string s = filepath;
1915#else
1916 std::string s;
1917 wordexp_t p;
1918
1919 if (filepath.empty()) {
1920 return "";
1921 }
1922
1923 // char** w;
1924 int ret = wordexp(filepath.c_str(), &p, 0);
1925 if (ret) {
1926 // err
1927 s = filepath;
1928 return s;
1929 }
1930
1931 // Use first element only.
1932 if (p.we_wordv) {
1933 s = std::string(p.we_wordv[0]);
1934 wordfree(&p);
1935 } else {
1936 s = filepath;
1937 }
1938
1939#endif
1940
1941 return s;
1942#endif
1943}
1944
1945bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1946 const std::string &filepath, void *) {
1947#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
1948 if (asset_manager) {
1949 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
1950 AASSET_MODE_STREAMING);
1951 if (!asset) {
1952 if (err) {
1953 (*err) += "File open error : " + filepath + "\n";
1954 }
1955 return false;
1956 }
1957 size_t size = AAsset_getLength(asset);
1958 if (size <= 0) {
1959 if (err) {
1960 (*err) += "Invalid file size : " + filepath +
1961 " (does the path point to a directory?)";
1962 }
1963 }
1964 out->resize(size);
1965 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
1966 AAsset_close(asset);
1967 return true;
1968 } else {
1969 if (err) {
1970 (*err) += "No asset manager specified : " + filepath + "\n";
1971 }
1972 return false;
1973 }
1974#else
1975 std::ifstream f(filepath.c_str(), std::ifstream::binary);
1976 if (!f) {
1977 if (err) {
1978 (*err) += "File open error : " + filepath + "\n";
1979 }
1980 return false;
1981 }
1982
1983 f.seekg(0, f.end);
1984 size_t sz = static_cast<size_t>(f.tellg());
1985 f.seekg(0, f.beg);
1986
1987 if (int(sz) < 0) {
1988 if (err) {
1989 (*err) += "Invalid file size : " + filepath +
1990 " (does the path point to a directory?)";
1991 }
1992 return false;
1993 } else if (sz == 0) {
1994 if (err) {
1995 (*err) += "File is empty : " + filepath + "\n";
1996 }
1997 return false;
1998 }
1999
2000 out->resize(sz);
2001 f.read(reinterpret_cast<char *>(&out->at(0)),
2002 static_cast<std::streamsize>(sz));
2003 f.close();
2004
2005 return true;
2006#endif
2007}
2008
2009bool WriteWholeFile(std::string *err, const std::string &filepath,
2010 const std::vector<unsigned char> &contents, void *) {
2011 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2012 if (!f) {
2013 if (err) {
2014 (*err) += "File open error for writing : " + filepath + "\n";
2015 }
2016 return false;
2017 }
2018
2019 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2020 static_cast<std::streamsize>(contents.size()));
2021 if (!f) {
2022 if (err) {
2023 (*err) += "File write error: " + filepath + "\n";
2024 }
2025 return false;
2026 }
2027
2028 f.close();
2029 return true;
2030}
2031
2032#endif // TINYGLTF_NO_FS
2033
2034static std::string MimeToExt(const std::string &mimeType) {
2035 if (mimeType == "image/jpeg") {
2036 return "jpg";
2037 } else if (mimeType == "image/png") {
2038 return "png";
2039 } else if (mimeType == "image/bmp") {
2040 return "bmp";
2041 } else if (mimeType == "image/gif") {
2042 return "gif";
2043 }
2044
2045 return "";
2046}
2047
2048static void UpdateImageObject(Image &image, std::string &baseDir, int index,
2049 bool embedImages,
2050 WriteImageDataFunction *WriteImageData = nullptr,
2051 void *user_data = nullptr) {
2052 std::string filename;
2053 std::string ext;
2054
2055 // If image have uri. Use it it as a filename
2056 if (image.uri.size()) {
2057 filename = GetBaseFilename(image.uri);
2058 ext = GetFilePathExtension(filename);
2059
2060 } else if (image.name.size()) {
2061 ext = MimeToExt(image.mimeType);
2062 // Otherwise use name as filename
2063 filename = image.name + "." + ext;
2064 } else {
2065 ext = MimeToExt(image.mimeType);
2066 // Fallback to index of image as filename
2067 filename = std::to_string(index) + "." + ext;
2068 }
2069
2070 // If callback is set, modify image data object
2071 if (*WriteImageData != nullptr) {
2072 std::string uri;
2073 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2074 }
2075}
2076
2077bool IsDataURI(const std::string &in) {
2078 std::string header = "data:application/octet-stream;base64,";
2079 if (in.find(header) == 0) {
2080 return true;
2081 }
2082
2083 header = "data:image/jpeg;base64,";
2084 if (in.find(header) == 0) {
2085 return true;
2086 }
2087
2088 header = "data:image/png;base64,";
2089 if (in.find(header) == 0) {
2090 return true;
2091 }
2092
2093 header = "data:image/bmp;base64,";
2094 if (in.find(header) == 0) {
2095 return true;
2096 }
2097
2098 header = "data:image/gif;base64,";
2099 if (in.find(header) == 0) {
2100 return true;
2101 }
2102
2103 header = "data:text/plain;base64,";
2104 if (in.find(header) == 0) {
2105 return true;
2106 }
2107
2108 header = "data:application/gltf-buffer;base64,";
2109 if (in.find(header) == 0) {
2110 return true;
2111 }
2112
2113 return false;
2114}
2115
2116bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2117 const std::string &in, size_t reqBytes, bool checkSize) {
2118 std::string header = "data:application/octet-stream;base64,";
2119 std::string data;
2120 if (in.find(header) == 0) {
2121 data = base64_decode(in.substr(header.size())); // cut mime string.
2122 }
2123
2124 if (data.empty()) {
2125 header = "data:image/jpeg;base64,";
2126 if (in.find(header) == 0) {
2127 mime_type = "image/jpeg";
2128 data = base64_decode(in.substr(header.size())); // cut mime string.
2129 }
2130 }
2131
2132 if (data.empty()) {
2133 header = "data:image/png;base64,";
2134 if (in.find(header) == 0) {
2135 mime_type = "image/png";
2136 data = base64_decode(in.substr(header.size())); // cut mime string.
2137 }
2138 }
2139
2140 if (data.empty()) {
2141 header = "data:image/bmp;base64,";
2142 if (in.find(header) == 0) {
2143 mime_type = "image/bmp";
2144 data = base64_decode(in.substr(header.size())); // cut mime string.
2145 }
2146 }
2147
2148 if (data.empty()) {
2149 header = "data:image/gif;base64,";
2150 if (in.find(header) == 0) {
2151 mime_type = "image/gif";
2152 data = base64_decode(in.substr(header.size())); // cut mime string.
2153 }
2154 }
2155
2156 if (data.empty()) {
2157 header = "data:text/plain;base64,";
2158 if (in.find(header) == 0) {
2159 mime_type = "text/plain";
2160 data = base64_decode(in.substr(header.size()));
2161 }
2162 }
2163
2164 if (data.empty()) {
2165 header = "data:application/gltf-buffer;base64,";
2166 if (in.find(header) == 0) {
2167 data = base64_decode(in.substr(header.size()));
2168 }
2169 }
2170
2171 if (data.empty()) {
2172 return false;
2173 }
2174
2175 if (checkSize) {
2176 if (data.size() != reqBytes) {
2177 return false;
2178 }
2179 out->resize(reqBytes);
2180 } else {
2181 out->resize(data.size());
2182 }
2183 std::copy(data.begin(), data.end(), out->begin());
2184 return true;
2185}
2186
2187static bool ParseJsonAsValue(Value *ret, const json &o) {
2188 Value val{};
2189 switch (o.type()) {
2190 case json::value_t::object: {
2191 Value::Object value_object;
2192 for (auto it = o.begin(); it != o.end(); it++) {
2193 Value entry;
2194 ParseJsonAsValue(&entry, it.value());
2195 if (entry.Type() != NULL_TYPE) value_object[it.key()] = entry;
2196 }
2197 if (value_object.size() > 0) val = Value(value_object);
2198 } break;
2199 case json::value_t::array: {
2200 Value::Array value_array;
2201 for (auto it = o.begin(); it != o.end(); it++) {
2202 Value entry;
2203 ParseJsonAsValue(&entry, it.value());
2204 if (entry.Type() != NULL_TYPE) value_array.push_back(entry);
2205 }
2206 if (value_array.size() > 0) val = Value(value_array);
2207 } break;
2208 case json::value_t::string:
2209 val = Value(o.get<std::string>());
2210 break;
2211 case json::value_t::boolean:
2212 val = Value(o.get<bool>());
2213 break;
2214 case json::value_t::number_integer:
2215 case json::value_t::number_unsigned:
2216 val = Value(static_cast<int>(o.get<int64_t>()));
2217 break;
2218 case json::value_t::number_float:
2219 val = Value(o.get<double>());
2220 break;
2221 case json::value_t::null:
2222 case json::value_t::discarded:
2223 // default:
2224 break;
2225 }
2226 if (ret) *ret = val;
2227
2228 return val.Type() != NULL_TYPE;
2229}
2230
2231static bool ParseExtrasProperty(Value *ret, const json &o) {
2232 json::const_iterator it = o.find("extras");
2233 if (it == o.end()) {
2234 return false;
2235 }
2236
2237 return ParseJsonAsValue(ret, it.value());
2238}
2239
2240static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
2241 const std::string &property,
2242 const bool required,
2243 const std::string &parent_node = "") {
2244 json::const_iterator it = o.find(property);
2245 if (it == o.end()) {
2246 if (required) {
2247 if (err) {
2248 (*err) += "'" + property + "' property is missing";
2249 if (!parent_node.empty()) {
2250 (*err) += " in " + parent_node;
2251 }
2252 (*err) += ".\n";
2253 }
2254 }
2255 return false;
2256 }
2257
2258 if (!it.value().is_boolean()) {
2259 if (required) {
2260 if (err) {
2261 (*err) += "'" + property + "' property is not a bool type.\n";
2262 }
2263 }
2264 return false;
2265 }
2266
2267 if (ret) {
2268 (*ret) = it.value().get<bool>();
2269 }
2270
2271 return true;
2272}
2273
2274static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
2275 const std::string &property,
2276 const bool required,
2277 const std::string &parent_node = "") {
2278 json::const_iterator it = o.find(property);
2279 if (it == o.end()) {
2280 if (required) {
2281 if (err) {
2282 (*err) += "'" + property + "' property is missing";
2283 if (!parent_node.empty()) {
2284 (*err) += " in " + parent_node;
2285 }
2286 (*err) += ".\n";
2287 }
2288 }
2289 return false;
2290 }
2291
2292 if (!it.value().is_number_integer()) {
2293 if (required) {
2294 if (err) {
2295 (*err) += "'" + property + "' property is not an integer type.\n";
2296 }
2297 }
2298 return false;
2299 }
2300
2301 if (ret) {
2302 (*ret) = it.value().get<int>();
2303 }
2304
2305 return true;
2306}
2307
2308static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
2309 const std::string &property,
2310 const bool required,
2311 const std::string &parent_node = "") {
2312 json::const_iterator it = o.find(property);
2313 if (it == o.end()) {
2314 if (required) {
2315 if (err) {
2316 (*err) += "'" + property + "' property is missing";
2317 if (!parent_node.empty()) {
2318 (*err) += " in " + parent_node;
2319 }
2320 (*err) += ".\n";
2321 }
2322 }
2323 return false;
2324 }
2325
2326 if (!it.value().is_number_unsigned()) {
2327 if (required) {
2328 if (err) {
2329 (*err) += "'" + property + "' property is not a positive integer.\n";
2330 }
2331 }
2332 return false;
2333 }
2334
2335 if (ret) {
2336 (*ret) = it.value().get<size_t>();
2337 }
2338
2339 return true;
2340}
2341
2342static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
2343 const std::string &property,
2344 const bool required,
2345 const std::string &parent_node = "") {
2346 json::const_iterator it = o.find(property);
2347 if (it == o.end()) {
2348 if (required) {
2349 if (err) {
2350 (*err) += "'" + property + "' property is missing";
2351 if (!parent_node.empty()) {
2352 (*err) += " in " + parent_node;
2353 }
2354 (*err) += ".\n";
2355 }
2356 }
2357 return false;
2358 }
2359
2360 if (!it.value().is_number()) {
2361 if (required) {
2362 if (err) {
2363 (*err) += "'" + property + "' property is not a number type.\n";
2364 }
2365 }
2366 return false;
2367 }
2368
2369 if (ret) {
2370 (*ret) = it.value().get<double>();
2371 }
2372
2373 return true;
2374}
2375
2376static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
2377 const json &o, const std::string &property,
2378 bool required,
2379 const std::string &parent_node = "") {
2380 json::const_iterator it = o.find(property);
2381 if (it == o.end()) {
2382 if (required) {
2383 if (err) {
2384 (*err) += "'" + property + "' property is missing";
2385 if (!parent_node.empty()) {
2386 (*err) += " in " + parent_node;
2387 }
2388 (*err) += ".\n";
2389 }
2390 }
2391 return false;
2392 }
2393
2394 if (!it.value().is_array()) {
2395 if (required) {
2396 if (err) {
2397 (*err) += "'" + property + "' property is not an array";
2398 if (!parent_node.empty()) {
2399 (*err) += " in " + parent_node;
2400 }
2401 (*err) += ".\n";
2402 }
2403 }
2404 return false;
2405 }
2406
2407 ret->clear();
2408 for (json::const_iterator i = it.value().begin(); i != it.value().end();
2409 i++) {
2410 if (!i.value().is_number()) {
2411 if (required) {
2412 if (err) {
2413 (*err) += "'" + property + "' property is not a number.\n";
2414 if (!parent_node.empty()) {
2415 (*err) += " in " + parent_node;
2416 }
2417 (*err) += ".\n";
2418 }
2419 }
2420 return false;
2421 }
2422 ret->push_back(i.value());
2423 }
2424
2425 return true;
2426}
2427
2428static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
2429 const json &o,
2430 const std::string &property,
2431 bool required,
2432 const std::string &parent_node = "") {
2433 json::const_iterator it = o.find(property);
2434 if (it == o.end()) {
2435 if (required) {
2436 if (err) {
2437 (*err) += "'" + property + "' property is missing";
2438 if (!parent_node.empty()) {
2439 (*err) += " in " + parent_node;
2440 }
2441 (*err) += ".\n";
2442 }
2443 }
2444 return false;
2445 }
2446
2447 if (!it.value().is_array()) {
2448 if (required) {
2449 if (err) {
2450 (*err) += "'" + property + "' property is not an array";
2451 if (!parent_node.empty()) {
2452 (*err) += " in " + parent_node;
2453 }
2454 (*err) += ".\n";
2455 }
2456 }
2457 return false;
2458 }
2459
2460 ret->clear();
2461 for (json::const_iterator i = it.value().begin(); i != it.value().end();
2462 i++) {
2463 if (!i.value().is_number_integer()) {
2464 if (required) {
2465 if (err) {
2466 (*err) += "'" + property + "' property is not an integer type.\n";
2467 if (!parent_node.empty()) {
2468 (*err) += " in " + parent_node;
2469 }
2470 (*err) += ".\n";
2471 }
2472 }
2473 return false;
2474 }
2475 ret->push_back(i.value());
2476 }
2477
2478 return true;
2479}
2480
2481static bool ParseStringProperty(
2482 std::string *ret, std::string *err, const json &o,
2483 const std::string &property, bool required,
2484 const std::string &parent_node = std::string()) {
2485 json::const_iterator it = o.find(property);
2486 if (it == o.end()) {
2487 if (required) {
2488 if (err) {
2489 (*err) += "'" + property + "' property is missing";
2490 if (parent_node.empty()) {
2491 (*err) += ".\n";
2492 } else {
2493 (*err) += " in `" + parent_node + "'.\n";
2494 }
2495 }
2496 }
2497 return false;
2498 }
2499
2500 if (!it.value().is_string()) {
2501 if (required) {
2502 if (err) {
2503 (*err) += "'" + property + "' property is not a string type.\n";
2504 }
2505 }
2506 return false;
2507 }
2508
2509 if (ret) {
2510 (*ret) = it.value().get<std::string>();
2511 }
2512
2513 return true;
2514}
2515
2516static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
2517 std::string *err, const json &o,
2518 const std::string &property,
2519 bool required,
2520 const std::string &parent = "") {
2521 json::const_iterator it = o.find(property);
2522 if (it == o.end()) {
2523 if (required) {
2524 if (err) {
2525 if (!parent.empty()) {
2526 (*err) +=
2527 "'" + property + "' property is missing in " + parent + ".\n";
2528 } else {
2529 (*err) += "'" + property + "' property is missing.\n";
2530 }
2531 }
2532 }
2533 return false;
2534 }
2535
2536 // Make sure we are dealing with an object / dictionary.
2537 if (!it.value().is_object()) {
2538 if (required) {
2539 if (err) {
2540 (*err) += "'" + property + "' property is not an object.\n";
2541 }
2542 }
2543 return false;
2544 }
2545
2546 ret->clear();
2547 const json &dict = it.value();
2548
2549 json::const_iterator dictIt(dict.begin());
2550 json::const_iterator dictItEnd(dict.end());
2551
2552 for (; dictIt != dictItEnd; ++dictIt) {
2553 if (!dictIt.value().is_number_integer()) {
2554 if (required) {
2555 if (err) {
2556 (*err) += "'" + property + "' value is not an integer type.\n";
2557 }
2558 }
2559 return false;
2560 }
2561
2562 // Insert into the list.
2563 (*ret)[dictIt.key()] = dictIt.value();
2564 }
2565 return true;
2566}
2567
2568static bool ParseJSONProperty(std::map<std::string, double> *ret,
2569 std::string *err, const json &o,
2570 const std::string &property, bool required) {
2571 json::const_iterator it = o.find(property);
2572 if (it == o.end()) {
2573 if (required) {
2574 if (err) {
2575 (*err) += "'" + property + "' property is missing. \n'";
2576 }
2577 }
2578 return false;
2579 }
2580
2581 if (!it.value().is_object()) {
2582 if (required) {
2583 if (err) {
2584 (*err) += "'" + property + "' property is not a JSON object.\n";
2585 }
2586 }
2587 return false;
2588 }
2589
2590 ret->clear();
2591 const json &obj = it.value();
2592 json::const_iterator it2(obj.begin());
2593 json::const_iterator itEnd(obj.end());
2594 for (; it2 != itEnd; it2++) {
2595 if (it2.value().is_number())
2596 ret->insert(std::pair<std::string, double>(it2.key(), it2.value()));
2597 }
2598
2599 return true;
2600}
2601
2602static bool ParseParameterProperty(Parameter *param, std::string *err,
2603 const json &o, const std::string &prop,
2604 bool required) {
2605 // A parameter value can either be a string or an array of either a boolean or
2606 // a number. Booleans of any kind aren't supported here. Granted, it
2607 // complicates the Parameter structure and breaks it semantically in the sense
2608 // that the client probably works off the assumption that if the string is
2609 // empty the vector is used, etc. Would a tagged union work?
2610 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
2611 // Found string property.
2612 return true;
2613 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
2614 false)) {
2615 // Found a number array.
2616 return true;
2617 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
2618 return param->has_number_value = true;
2619 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
2620 false)) {
2621 return true;
2622 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
2623 return true;
2624 } else {
2625 if (required) {
2626 if (err) {
2627 (*err) += "parameter must be a string or number / number array.\n";
2628 }
2629 }
2630 return false;
2631 }
2632}
2633
2634static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
2635 const json &o) {
2636 (void)err;
2637
2638 json::const_iterator it = o.find("extensions");
2639 if (it == o.end()) {
2640 return false;
2641 }
2642 if (!it.value().is_object()) {
2643 return false;
2644 }
2645 ExtensionMap extensions;
2646 json::const_iterator extIt = it.value().begin();
2647 for (; extIt != it.value().end(); extIt++) {
2648 if (!extIt.value().is_object()) continue;
2649 if (!ParseJsonAsValue(&extensions[extIt.key()], extIt.value())) {
2650 if (!extIt.key().empty()) {
2651 // create empty object so that an extension object is still of type
2652 // object
2653 extensions[extIt.key()] = Value{Value::Object{}};
2654 }
2655 }
2656 }
2657 if (ret) {
2658 (*ret) = extensions;
2659 }
2660 return true;
2661}
2662
2663static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
2664 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
2665 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
2666 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
2667
2668 ParseExtensionsProperty(&asset->extensions, err, o);
2669
2670 // Unity exporter version is added as extra here
2671 ParseExtrasProperty(&(asset->extras), o);
2672
2673 return true;
2674}
2675
2676static bool ParseImage(Image *image, const int image_idx, std::string *err,
2677 std::string *warn, const json &o,
2678 const std::string &basedir, FsCallbacks *fs,
2679 LoadImageDataFunction *LoadImageData = nullptr,
2680 void *load_image_user_data = nullptr) {
2681 // A glTF image must either reference a bufferView or an image uri
2682
2683 // schema says oneOf [`bufferView`, `uri`]
2684 // TODO(syoyo): Check the type of each parameters.
2685 bool hasBufferView = (o.find("bufferView") != o.end());
2686 bool hasURI = (o.find("uri") != o.end());
2687
2688 ParseStringProperty(&image->name, err, o, "name", false);
2689
2690 if (hasBufferView && hasURI) {
2691 // Should not both defined.
2692 if (err) {
2693 (*err) +=
2694 "Only one of `bufferView` or `uri` should be defined, but both are "
2695 "defined for image[" +
2696 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
2697 }
2698 return false;
2699 }
2700
2701 if (!hasBufferView && !hasURI) {
2702 if (err) {
2703 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
2704 std::to_string(image_idx) + "] name = \"" + image->name +
2705 "\"\n";
2706 }
2707 return false;
2708 }
2709
2710 ParseExtensionsProperty(&image->extensions, err, o);
2711 ParseExtrasProperty(&image->extras, o);
2712
2713 if (hasBufferView) {
2714 int bufferView = -1;
2715 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
2716 if (err) {
2717 (*err) += "Failed to parse `bufferView` for image[" +
2718 std::to_string(image_idx) + "] name = \"" + image->name +
2719 "\"\n";
2720 }
2721 return false;
2722 }
2723
2724 std::string mime_type;
2725 ParseStringProperty(&mime_type, err, o, "mimeType", false);
2726
2727 int width = 0;
2728 ParseIntegerProperty(&width, err, o, "width", false);
2729
2730 int height = 0;
2731 ParseIntegerProperty(&height, err, o, "height", false);
2732
2733 // Just only save some information here. Loading actual image data from
2734 // bufferView is done after this `ParseImage` function.
2735 image->bufferView = bufferView;
2736 image->mimeType = mime_type;
2737 image->width = width;
2738 image->height = height;
2739
2740 return true;
2741 }
2742
2743 // Parse URI & Load image data.
2744
2745 std::string uri;
2746 std::string tmp_err;
2747 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
2748 if (err) {
2749 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
2750 "] name = \"" + image->name + "\".\n";
2751 }
2752 return false;
2753 }
2754
2755 std::vector<unsigned char> img;
2756
2757 if (IsDataURI(uri)) {
2758 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
2759 if (err) {
2760 (*err) += "Failed to decode 'uri' for image[" +
2761 std::to_string(image_idx) + "] name = [" + image->name +
2762 "]\n";
2763 }
2764 return false;
2765 }
2766 } else {
2767 // Assume external file
2768 // Keep texture path (for textures that cannot be decoded)
2769 image->uri = uri;
2770#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
2771 return true;
2772#endif
2773 if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) {
2774 if (warn) {
2775 (*warn) += "Failed to load external 'uri' for image[" +
2776 std::to_string(image_idx) + "] name = [" + image->name +
2777 "]\n";
2778 }
2779 // If the image cannot be loaded, keep uri as image->uri.
2780 return true;
2781 }
2782
2783 if (img.empty()) {
2784 if (warn) {
2785 (*warn) += "Image data is empty for image[" +
2786 std::to_string(image_idx) + "] name = [" + image->name +
2787 "] \n";
2788 }
2789 return false;
2790 }
2791 }
2792
2793 if (*LoadImageData == nullptr) {
2794 if (err) {
2795 (*err) += "No LoadImageData callback specified.\n";
2796 }
2797 return false;
2798 }
2799 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
2800 static_cast<int>(img.size()), load_image_user_data);
2801}
2802
2803static bool ParseTexture(Texture *texture, std::string *err, const json &o,
2804 const std::string &basedir) {
2805 (void)basedir;
2806 int sampler = -1;
2807 int source = -1;
2808 ParseIntegerProperty(&sampler, err, o, "sampler", false);
2809
2810 ParseIntegerProperty(&source, err, o, "source", false);
2811
2812 texture->sampler = sampler;
2813 texture->source = source;
2814
2815 ParseExtensionsProperty(&texture->extensions, err, o);
2816 ParseExtrasProperty(&texture->extras, o);
2817
2818 ParseStringProperty(&texture->name, err, o, "name", false);
2819
2820 return true;
2821}
2822
2823static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
2824 FsCallbacks *fs, const std::string &basedir,
2825 bool is_binary = false,
2826 const unsigned char *bin_data = nullptr,
2827 size_t bin_size = 0) {
2828 size_t byteLength;
2829 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
2830 "Buffer")) {
2831 return false;
2832 }
2833
2834 // In glTF 2.0, uri is not mandatory anymore
2835 buffer->uri.clear();
2836 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
2837
2838 // having an empty uri for a non embedded image should not be valid
2839 if (!is_binary && buffer->uri.empty()) {
2840 if (err) {
2841 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
2842 }
2843 }
2844
2845 json::const_iterator type = o.find("type");
2846 if (type != o.end()) {
2847 if (type.value().is_string()) {
2848 const std::string &ty = type.value();
2849 if (ty.compare("arraybuffer") == 0) {
2850 // buffer.type = "arraybuffer";
2851 }
2852 }
2853 }
2854
2855 if (is_binary) {
2856 // Still binary glTF accepts external dataURI.
2857 if (!buffer->uri.empty()) {
2858 // First try embedded data URI.
2859 if (IsDataURI(buffer->uri)) {
2860 std::string mime_type;
2861 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
2862 true)) {
2863 if (err) {
2864 (*err) +=
2865 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
2866 }
2867 return false;
2868 }
2869 } else {
2870 // External .bin file.
2871 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
2872 buffer->uri, basedir, true, byteLength, true,
2873 fs)) {
2874 return false;
2875 }
2876 }
2877 } else {
2878 // load data from (embedded) binary data
2879
2880 if ((bin_size == 0) || (bin_data == nullptr)) {
2881 if (err) {
2882 (*err) += "Invalid binary data in `Buffer'.\n";
2883 }
2884 return false;
2885 }
2886
2887 if (byteLength > bin_size) {
2888 if (err) {
2889 std::stringstream ss;
2890 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
2891 "`byteLength' = "
2892 << byteLength << ", binary size = " << bin_size << std::endl;
2893 (*err) += ss.str();
2894 }
2895 return false;
2896 }
2897
2898 // Read buffer data
2899 buffer->data.resize(static_cast<size_t>(byteLength));
2900 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
2901 }
2902
2903 } else {
2904 if (IsDataURI(buffer->uri)) {
2905 std::string mime_type;
2906 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
2907 true)) {
2908 if (err) {
2909 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
2910 }
2911 return false;
2912 }
2913 } else {
2914 // Assume external .bin file.
2915 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri,
2916 basedir, true, byteLength, true, fs)) {
2917 return false;
2918 }
2919 }
2920 }
2921
2922 ParseStringProperty(&buffer->name, err, o, "name", false);
2923
2924 return true;
2925}
2926
2927static bool ParseBufferView(BufferView *bufferView, std::string *err,
2928 const json &o) {
2929 int buffer = -1;
2930 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
2931 return false;
2932 }
2933
2934 size_t byteOffset = 0;
2935 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
2936
2937 size_t byteLength = 1;
2938 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
2939 "BufferView")) {
2940 return false;
2941 }
2942
2943 size_t byteStride = 0;
2944 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
2945 // Spec says: When byteStride of referenced bufferView is not defined, it
2946 // means that accessor elements are tightly packed, i.e., effective stride
2947 // equals the size of the element.
2948 // We cannot determine the actual byteStride until Accessor are parsed, thus
2949 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
2950 byteStride = 0;
2951 }
2952
2953 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
2954 if (err) {
2955 std::stringstream ss;
2956 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
2957 "4 : "
2958 << byteStride << std::endl;
2959
2960 (*err) += ss.str();
2961 }
2962 return false;
2963 }
2964
2965 int target = 0;
2966 ParseIntegerProperty(&target, err, o, "target", false);
2967 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
2968 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
2969 // OK
2970 } else {
2971 target = 0;
2972 }
2973 bufferView->target = target;
2974
2975 ParseStringProperty(&bufferView->name, err, o, "name", false);
2976
2977 bufferView->buffer = buffer;
2978 bufferView->byteOffset = byteOffset;
2979 bufferView->byteLength = byteLength;
2980 bufferView->byteStride = byteStride;
2981 return true;
2982}
2983
2984static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
2985 const json &o) {
2986 accessor->sparse.isSparse = true;
2987
2988 int count = 0;
2989 ParseIntegerProperty(&count, err, o, "count", true);
2990
2991 const auto indices_iterator = o.find("indices");
2992 const auto values_iterator = o.find("values");
2993 if (indices_iterator == o.end()) {
2994 (*err) = "the sparse object of this accessor doesn't have indices";
2995 return false;
2996 }
2997
2998 if (values_iterator == o.end()) {
2999 (*err) = "the sparse object ob ths accessor doesn't have values";
3000 return false;
3001 }
3002
3003 const json &indices_obj = *indices_iterator;
3004 const json &values_obj = *values_iterator;
3005
3006 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
3007 ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
3008 true);
3009 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
3010 true);
3011 ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
3012 true);
3013
3014 int values_buffer_view = 0, values_byte_offset = 0;
3015 ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
3016 true);
3017 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
3018 true);
3019
3020 accessor->sparse.count = count;
3021 accessor->sparse.indices.bufferView = indices_buffer_view;
3022 accessor->sparse.indices.byteOffset = indices_byte_offset;
3023 accessor->sparse.indices.componentType = component_type;
3024 accessor->sparse.values.bufferView = values_buffer_view;
3025 accessor->sparse.values.byteOffset = values_byte_offset;
3026
3027 // todo check theses values
3028
3029 return true;
3030}
3031
3032static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
3033 int bufferView = -1;
3034 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
3035
3036 size_t byteOffset = 0;
3037 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
3038
3039 bool normalized = false;
3040 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
3041
3042 size_t componentType = 0;
3043 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
3044 "Accessor")) {
3045 return false;
3046 }
3047
3048 size_t count = 0;
3049 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
3050 return false;
3051 }
3052
3053 std::string type;
3054 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
3055 return false;
3056 }
3057
3058 if (type.compare("SCALAR") == 0) {
3059 accessor->type = TINYGLTF_TYPE_SCALAR;
3060 } else if (type.compare("VEC2") == 0) {
3061 accessor->type = TINYGLTF_TYPE_VEC2;
3062 } else if (type.compare("VEC3") == 0) {
3063 accessor->type = TINYGLTF_TYPE_VEC3;
3064 } else if (type.compare("VEC4") == 0) {
3065 accessor->type = TINYGLTF_TYPE_VEC4;
3066 } else if (type.compare("MAT2") == 0) {
3067 accessor->type = TINYGLTF_TYPE_MAT2;
3068 } else if (type.compare("MAT3") == 0) {
3069 accessor->type = TINYGLTF_TYPE_MAT3;
3070 } else if (type.compare("MAT4") == 0) {
3071 accessor->type = TINYGLTF_TYPE_MAT4;
3072 } else {
3073 std::stringstream ss;
3074 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
3075 if (err) {
3076 (*err) += ss.str();
3077 }
3078 return false;
3079 }
3080
3081 ParseStringProperty(&accessor->name, err, o, "name", false);
3082
3083 accessor->minValues.clear();
3084 accessor->maxValues.clear();
3085 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
3086 "Accessor");
3087
3088 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
3089 "Accessor");
3090
3091 accessor->count = count;
3092 accessor->bufferView = bufferView;
3093 accessor->byteOffset = byteOffset;
3094 accessor->normalized = normalized;
3095 {
3096 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
3097 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
3098 // OK
3099 accessor->componentType = componentType;
3100 } else {
3101 std::stringstream ss;
3102 ss << "Invalid `componentType` in accessor. Got " << componentType
3103 << "\n";
3104 if (err) {
3105 (*err) += ss.str();
3106 }
3107 return false;
3108 }
3109 }
3110
3111 ParseExtrasProperty(&(accessor->extras), o);
3112
3113 // check if accessor has a "sparse" object:
3114 const auto iterator = o.find("sparse");
3115 if (iterator != o.end()) {
3116 // here this accessor has a "sparse" subobject
3117 return ParseSparseAccessor(accessor, err, *iterator);
3118 }
3119
3120 return true;
3121}
3122
3123#ifdef TINYGLTF_ENABLE_DRACO
3124
3125static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
3126 std::vector<uint8_t> &outBuffer) {
3127 if (componentSize == 4) {
3128 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
3129 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
3130 outBuffer.size());
3131 } else {
3132 size_t faceStride = componentSize * 3;
3133 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
3134 const draco::Mesh::Face &face = mesh->face(f);
3135 if (componentSize == 2) {
3136 uint16_t indices[3] = {(uint16_t)face[0].value(),
3137 (uint16_t)face[1].value(),
3138 (uint16_t)face[2].value()};
3139 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
3140 faceStride);
3141 } else {
3142 uint8_t indices[3] = {(uint8_t)face[0].value(),
3143 (uint8_t)face[1].value(),
3144 (uint8_t)face[2].value()};
3145 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
3146 faceStride);
3147 }
3148 }
3149 }
3150}
3151
3152template <typename T>
3153static bool GetAttributeForAllPoints(draco::Mesh *mesh,
3154 const draco::PointAttribute *pAttribute,
3155 std::vector<uint8_t> &outBuffer) {
3156 size_t byteOffset = 0;
3157 T values[4] = {0, 0, 0, 0};
3158 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
3159 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
3160 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
3161 values))
3162 return false;
3163
3164 memcpy(outBuffer.data() + byteOffset, &values[0],
3165 sizeof(T) * pAttribute->num_components());
3166 byteOffset += sizeof(T) * pAttribute->num_components();
3167 }
3168
3169 return true;
3170}
3171
3172static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
3173 const draco::PointAttribute *pAttribute,
3174 std::vector<uint8_t> &outBuffer) {
3175 bool decodeResult = false;
3176 switch (componentType) {
3177 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
3178 decodeResult =
3179 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
3180 break;
3181 case TINYGLTF_COMPONENT_TYPE_BYTE:
3182 decodeResult =
3183 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
3184 break;
3185 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
3186 decodeResult =
3187 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
3188 break;
3189 case TINYGLTF_COMPONENT_TYPE_SHORT:
3190 decodeResult =
3191 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
3192 break;
3193 case TINYGLTF_COMPONENT_TYPE_INT:
3194 decodeResult =
3195 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
3196 break;
3197 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
3198 decodeResult =
3199 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
3200 break;
3201 case TINYGLTF_COMPONENT_TYPE_FLOAT:
3202 decodeResult =
3203 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
3204 break;
3205 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
3206 decodeResult =
3207 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
3208 break;
3209 default:
3210 return false;
3211 }
3212
3213 return decodeResult;
3214}
3215
3216static bool ParseDracoExtension(Primitive *primitive, Model *model,
3217 std::string *err,
3218 const Value &dracoExtensionValue) {
3219 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
3220 if (!bufferViewValue.IsInt()) return false;
3221 auto attributesValue = dracoExtensionValue.Get("attributes");
3222 if (!attributesValue.IsObject()) return false;
3223
3224 auto attributesObject = attributesValue.Get<Value::Object>();
3225 int bufferView = bufferViewValue.Get<int>();
3226
3227 BufferView &view = model->bufferViews[bufferView];
3228 Buffer &buffer = model->buffers[view.buffer];
3229 // BufferView has already been decoded
3230 if (view.dracoDecoded) return true;
3231 view.dracoDecoded = true;
3232
3233 const char *bufferViewData =
3234 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
3235 size_t bufferViewSize = view.byteLength;
3236
3237 // decode draco
3238 draco::DecoderBuffer decoderBuffer;
3239 decoderBuffer.Init(bufferViewData, bufferViewSize);
3240 draco::Decoder decoder;
3241 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
3242 if (!decodeResult.ok()) {
3243 return false;
3244 }
3245 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
3246
3247 // create new bufferView for indices
3248 if (primitive->indices >= 0) {
3249 int32_t componentSize = GetComponentSizeInBytes(
3250 model->accessors[primitive->indices].componentType);
3251 Buffer decodedIndexBuffer;
3252 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
3253
3254 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
3255
3256 model->buffers.emplace_back(std::move(decodedIndexBuffer));
3257
3258 BufferView decodedIndexBufferView;
3259 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
3260 decodedIndexBufferView.byteLength =
3261 int(mesh->num_faces() * 3 * componentSize);
3262 decodedIndexBufferView.byteOffset = 0;
3263 decodedIndexBufferView.byteStride = 0;
3264 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
3265 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
3266
3267 model->accessors[primitive->indices].bufferView =
3268 int(model->bufferViews.size() - 1);
3269 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
3270 }
3271
3272 for (const auto &attribute : attributesObject) {
3273 if (!attribute.second.IsInt()) return false;
3274 auto primitiveAttribute = primitive->attributes.find(attribute.first);
3275 if (primitiveAttribute == primitive->attributes.end()) return false;
3276
3277 int dracoAttributeIndex = attribute.second.Get<int>();
3278 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
3279 const auto pBuffer = pAttribute->buffer();
3280 const auto componentType =
3281 model->accessors[primitiveAttribute->second].componentType;
3282
3283 // Create a new buffer for this decoded buffer
3284 Buffer decodedBuffer;
3285 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
3286 GetComponentSizeInBytes(componentType);
3287 decodedBuffer.data.resize(bufferSize);
3288
3289 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
3290 decodedBuffer.data))
3291 return false;
3292
3293 model->buffers.emplace_back(std::move(decodedBuffer));
3294
3295 BufferView decodedBufferView;
3296 decodedBufferView.buffer = int(model->buffers.size() - 1);
3297 decodedBufferView.byteLength = bufferSize;
3298 decodedBufferView.byteOffset = pAttribute->byte_offset();
3299 decodedBufferView.byteStride = pAttribute->byte_stride();
3300 decodedBufferView.target = primitive->indices >= 0
3301 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
3302 : TINYGLTF_TARGET_ARRAY_BUFFER;
3303 model->bufferViews.emplace_back(std::move(decodedBufferView));
3304
3305 model->accessors[primitiveAttribute->second].bufferView =
3306 int(model->bufferViews.size() - 1);
3307 model->accessors[primitiveAttribute->second].count =
3308 int(mesh->num_points());
3309 }
3310
3311 return true;
3312}
3313#endif
3314
3315static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
3316 const json &o) {
3317 int material = -1;
3318 ParseIntegerProperty(&material, err, o, "material", false);
3319 primitive->material = material;
3320
3321 int mode = TINYGLTF_MODE_TRIANGLES;
3322 ParseIntegerProperty(&mode, err, o, "mode", false);
3323 primitive->mode = mode; // Why only triangled were supported ?
3324
3325 int indices = -1;
3326 ParseIntegerProperty(&indices, err, o, "indices", false);
3327 primitive->indices = indices;
3328 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
3329 true, "Primitive")) {
3330 return false;
3331 }
3332
3333 // Look for morph targets
3334 json::const_iterator targetsObject = o.find("targets");
3335 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
3336 for (json::const_iterator i = targetsObject.value().begin();
3337 i != targetsObject.value().end(); i++) {
3338 std::map<std::string, int> targetAttribues;
3339
3340 const json &dict = i.value();
3341 json::const_iterator dictIt(dict.begin());
3342 json::const_iterator dictItEnd(dict.end());
3343
3344 for (; dictIt != dictItEnd; ++dictIt) {
3345 targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
3346 }
3347 primitive->targets.push_back(targetAttribues);
3348 }
3349 }
3350
3351 ParseExtrasProperty(&(primitive->extras), o);
3352
3353 ParseExtensionsProperty(&primitive->extensions, err, o);
3354
3355#ifdef TINYGLTF_ENABLE_DRACO
3356 auto dracoExtension =
3357 primitive->extensions.find("KHR_draco_mesh_compression");
3358 if (dracoExtension != primitive->extensions.end()) {
3359 ParseDracoExtension(primitive, model, err, dracoExtension->second);
3360 }
3361#else
3362 (void)model;
3363#endif
3364
3365 return true;
3366}
3367
3368static bool ParseMesh(Mesh *mesh, Model *model, std::string *err,
3369 const json &o) {
3370 ParseStringProperty(&mesh->name, err, o, "name", false);
3371
3372 mesh->primitives.clear();
3373 json::const_iterator primObject = o.find("primitives");
3374 if ((primObject != o.end()) && primObject.value().is_array()) {
3375 for (json::const_iterator i = primObject.value().begin();
3376 i != primObject.value().end(); i++) {
3377 Primitive primitive;
3378 if (ParsePrimitive(&primitive, model, err, i.value())) {
3379 // Only add the primitive if the parsing succeeds.
3380 mesh->primitives.push_back(primitive);
3381 }
3382 }
3383 }
3384
3385 // Look for morph targets
3386 json::const_iterator targetsObject = o.find("targets");
3387 if ((targetsObject != o.end()) && targetsObject.value().is_array()) {
3388 for (json::const_iterator i = targetsObject.value().begin();
3389 i != targetsObject.value().end(); i++) {
3390 std::map<std::string, int> targetAttribues;
3391
3392 const json &dict = i.value();
3393 json::const_iterator dictIt(dict.begin());
3394 json::const_iterator dictItEnd(dict.end());
3395
3396 for (; dictIt != dictItEnd; ++dictIt) {
3397 targetAttribues[dictIt.key()] = static_cast<int>(dictIt.value());
3398 }
3399 mesh->targets.push_back(targetAttribues);
3400 }
3401 }
3402
3403 // Should probably check if has targets and if dimensions fit
3404 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
3405
3406 ParseExtensionsProperty(&mesh->extensions, err, o);
3407 ParseExtrasProperty(&(mesh->extras), o);
3408
3409 return true;
3410}
3411
3412static bool ParseLight(Light *light, std::string *err, const json &o) {
3413 ParseStringProperty(&light->name, err, o, "name", false);
3414 ParseNumberArrayProperty(&light->color, err, o, "color", false);
3415 ParseStringProperty(&light->type, err, o, "type", false);
3416 return true;
3417}
3418
3419static bool ParseNode(Node *node, std::string *err, const json &o) {
3420 ParseStringProperty(&node->name, err, o, "name", false);
3421
3422 int skin = -1;
3423 ParseIntegerProperty(&skin, err, o, "skin", false);
3424 node->skin = skin;
3425
3426 // Matrix and T/R/S are exclusive
3427 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
3428 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
3429 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
3430 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
3431 }
3432
3433 int camera = -1;
3434 ParseIntegerProperty(&camera, err, o, "camera", false);
3435 node->camera = camera;
3436
3437 int mesh = -1;
3438 ParseIntegerProperty(&mesh, err, o, "mesh", false);
3439 node->mesh = mesh;
3440
3441 node->children.clear();
3442 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
3443
3444 ParseExtensionsProperty(&node->extensions, err, o);
3445 ParseExtrasProperty(&(node->extras), o);
3446
3447 return true;
3448}
3449
3450static bool ParseMaterial(Material *material, std::string *err, const json &o) {
3451 material->values.clear();
3452 material->extensions.clear();
3453 material->additionalValues.clear();
3454
3455 json::const_iterator it(o.begin());
3456 json::const_iterator itEnd(o.end());
3457
3458 for (; it != itEnd; it++) {
3459 if (it.key() == "pbrMetallicRoughness") {
3460 if (it.value().is_object()) {
3461 const json &values_object = it.value();
3462
3463 json::const_iterator itVal(values_object.begin());
3464 json::const_iterator itValEnd(values_object.end());
3465
3466 for (; itVal != itValEnd; itVal++) {
3467 Parameter param;
3468 if (ParseParameterProperty(&param, err, values_object, itVal.key(),
3469 false)) {
3470 material->values[itVal.key()] = param;
3471 }
3472 }
3473 }
3474 } else if (it.key() == "extensions" || it.key() == "extras") {
3475 // done later, skip, otherwise poorly parsed contents will be saved in the
3476 // parametermap and serialized again later
3477 } else {
3478 Parameter param;
3479 if (ParseParameterProperty(&param, err, o, it.key(), false)) {
3480 material->additionalValues[it.key()] = param;
3481 }
3482 }
3483 }
3484
3485 ParseExtensionsProperty(&material->extensions, err, o);
3486 ParseExtrasProperty(&(material->extras), o);
3487
3488 return true;
3489}
3490
3491static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
3492 const json &o) {
3493 int samplerIndex = -1;
3494 int targetIndex = -1;
3495 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
3496 "AnimationChannel")) {
3497 if (err) {
3498 (*err) += "`sampler` field is missing in animation channels\n";
3499 }
3500 return false;
3501 }
3502
3503 json::const_iterator targetIt = o.find("target");
3504 if ((targetIt != o.end()) && targetIt.value().is_object()) {
3505 const json &target_object = targetIt.value();
3506
3507 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
3508 if (err) {
3509 (*err) += "`node` field is missing in animation.channels.target\n";
3510 }
3511 return false;
3512 }
3513
3514 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
3515 true)) {
3516 if (err) {
3517 (*err) += "`path` field is missing in animation.channels.target\n";
3518 }
3519 return false;
3520 }
3521 }
3522
3523 channel->sampler = samplerIndex;
3524 channel->target_node = targetIndex;
3525
3526 ParseExtrasProperty(&(channel->extras), o);
3527
3528 return true;
3529}
3530
3531static bool ParseAnimation(Animation *animation, std::string *err,
3532 const json &o) {
3533 {
3534 json::const_iterator channelsIt = o.find("channels");
3535 if ((channelsIt != o.end()) && channelsIt.value().is_array()) {
3536 for (json::const_iterator i = channelsIt.value().begin();
3537 i != channelsIt.value().end(); i++) {
3538 AnimationChannel channel;
3539 if (ParseAnimationChannel(&channel, err, i.value())) {
3540 // Only add the channel if the parsing succeeds.
3541 animation->channels.push_back(channel);
3542 }
3543 }
3544 }
3545 }
3546
3547 {
3548 json::const_iterator samplerIt = o.find("samplers");
3549 if ((samplerIt != o.end()) && samplerIt.value().is_array()) {
3550 const json &sampler_array = samplerIt.value();
3551
3552 json::const_iterator it = sampler_array.begin();
3553 json::const_iterator itEnd = sampler_array.end();
3554
3555 for (; it != itEnd; it++) {
3556 const json &s = it->get<json>();
3557
3558 AnimationSampler sampler;
3559 int inputIndex = -1;
3560 int outputIndex = -1;
3561 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
3562 if (err) {
3563 (*err) += "`input` field is missing in animation.sampler\n";
3564 }
3565 return false;
3566 }
3567 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
3568 false);
3569 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
3570 if (err) {
3571 (*err) += "`output` field is missing in animation.sampler\n";
3572 }
3573 return false;
3574 }
3575 sampler.input = inputIndex;
3576 sampler.output = outputIndex;
3577 ParseExtrasProperty(&(sampler.extras), s);
3578 animation->samplers.push_back(sampler);
3579 }
3580 }
3581 }
3582
3583 ParseStringProperty(&animation->name, err, o, "name", false);
3584
3585 ParseExtrasProperty(&(animation->extras), o);
3586
3587 return true;
3588}
3589
3590static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) {
3591 ParseStringProperty(&sampler->name, err, o, "name", false);
3592
3593 int minFilter = TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR;
3594 int magFilter = TINYGLTF_TEXTURE_FILTER_LINEAR;
3595 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
3596 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
3597 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
3598 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
3599 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
3600 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
3601
3602 sampler->minFilter = minFilter;
3603 sampler->magFilter = magFilter;
3604 sampler->wrapS = wrapS;
3605 sampler->wrapT = wrapT;
3606
3607 ParseExtrasProperty(&(sampler->extras), o);
3608
3609 return true;
3610}
3611
3612static bool ParseSkin(Skin *skin, std::string *err, const json &o) {
3613 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
3614
3615 std::vector<int> joints;
3616 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
3617 return false;
3618 }
3619 skin->joints = std::move(joints);
3620
3621 int skeleton = -1;
3622 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
3623 skin->skeleton = skeleton;
3624
3625 int invBind = -1;
3626 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
3627 skin->inverseBindMatrices = invBind;
3628
3629 return true;
3630}
3631
3632static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
3633 const json &o) {
3634 double yfov = 0.0;
3635 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
3636 return false;
3637 }
3638
3639 double znear = 0.0;
3640 if (!ParseNumberProperty(&znear, err, o, "znear", true,
3641 "PerspectiveCamera")) {
3642 return false;
3643 }
3644
3645 double aspectRatio = 0.0; // = invalid
3646 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
3647 "PerspectiveCamera");
3648
3649 double zfar = 0.0; // = invalid
3650 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
3651
3652 camera->aspectRatio = aspectRatio;
3653 camera->zfar = zfar;
3654 camera->yfov = yfov;
3655 camera->znear = znear;
3656
3657 ParseExtensionsProperty(&camera->extensions, err, o);
3658 ParseExtrasProperty(&(camera->extras), o);
3659
3660 // TODO(syoyo): Validate parameter values.
3661
3662 return true;
3663}
3664
3665static bool ParseOrthographicCamera(OrthographicCamera *camera,
3666 std::string *err, const json &o) {
3667 double xmag = 0.0;
3668 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
3669 return false;
3670 }
3671
3672 double ymag = 0.0;
3673 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
3674 return false;
3675 }
3676
3677 double zfar = 0.0;
3678 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
3679 return false;
3680 }
3681
3682 double znear = 0.0;
3683 if (!ParseNumberProperty(&znear, err, o, "znear", true,
3684 "OrthographicCamera")) {
3685 return false;
3686 }
3687
3688 ParseExtensionsProperty(&camera->extensions, err, o);
3689 ParseExtrasProperty(&(camera->extras), o);
3690
3691 camera->xmag = xmag;
3692 camera->ymag = ymag;
3693 camera->zfar = zfar;
3694 camera->znear = znear;
3695
3696 // TODO(syoyo): Validate parameter values.
3697
3698 return true;
3699}
3700
3701static bool ParseCamera(Camera *camera, std::string *err, const json &o) {
3702 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
3703 return false;
3704 }
3705
3706 if (camera->type.compare("orthographic") == 0) {
3707 if (o.find("orthographic") == o.end()) {
3708 if (err) {
3709 std::stringstream ss;
3710 ss << "Orhographic camera description not found." << std::endl;
3711 (*err) += ss.str();
3712 }
3713 return false;
3714 }
3715
3716 const json &v = o.find("orthographic").value();
3717 if (!v.is_object()) {
3718 if (err) {
3719 std::stringstream ss;
3720 ss << "\"orthographic\" is not a JSON object." << std::endl;
3721 (*err) += ss.str();
3722 }
3723 return false;
3724 }
3725
3726 if (!ParseOrthographicCamera(&camera->orthographic, err, v.get<json>())) {
3727 return false;
3728 }
3729 } else if (camera->type.compare("perspective") == 0) {
3730 if (o.find("perspective") == o.end()) {
3731 if (err) {
3732 std::stringstream ss;
3733 ss << "Perspective camera description not found." << std::endl;
3734 (*err) += ss.str();
3735 }
3736 return false;
3737 }
3738
3739 const json &v = o.find("perspective").value();
3740 if (!v.is_object()) {
3741 if (err) {
3742 std::stringstream ss;
3743 ss << "\"perspective\" is not a JSON object." << std::endl;
3744 (*err) += ss.str();
3745 }
3746 return false;
3747 }
3748
3749 if (!ParsePerspectiveCamera(&camera->perspective, err, v.get<json>())) {
3750 return false;
3751 }
3752 } else {
3753 if (err) {
3754 std::stringstream ss;
3755 ss << "Invalid camera type: \"" << camera->type
3756 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
3757 (*err) += ss.str();
3758 }
3759 return false;
3760 }
3761
3762 ParseStringProperty(&camera->name, err, o, "name", false);
3763
3764 ParseExtensionsProperty(&camera->extensions, err, o);
3765 ParseExtrasProperty(&(camera->extras), o);
3766
3767 return true;
3768}
3769
3770bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
3771 const char *str, unsigned int length,
3772 const std::string &base_dir,
3773 unsigned int check_sections) {
3774 if (length < 4) {
3775 if (err) {
3776 (*err) = "JSON string too short.\n";
3777 }
3778 return false;
3779 }
3780
3781 json v;
3782
3783#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
3784 defined(_CPPUNWIND)) && \
3785 not defined(TINYGLTF_NOEXCEPTION)
3786 try {
3787 v = json::parse(str, str + length);
3788
3789 } catch (const std::exception &e) {
3790 if (err) {
3791 (*err) = e.what();
3792 }
3793 return false;
3794 }
3795#else
3796 {
3797 v = json::parse(str, str + length, nullptr, /* exception */ false);
3798
3799 if (!v.is_object()) {
3800 // Assume parsing was failed.
3801 if (err) {
3802 (*err) = "Failed to parse JSON object\n";
3803 }
3804 return false;
3805 }
3806 }
3807#endif
3808
3809 if (!v.is_object()) {
3810 // root is not an object.
3811 if (err) {
3812 (*err) = "Root element is not a JSON object\n";
3813 }
3814 return false;
3815 }
3816
3817 // scene is not mandatory.
3818 // FIXME Maybe a better way to handle it than removing the code
3819
3820 {
3821 json::const_iterator it = v.find("scenes");
3822 if ((it != v.end()) && it.value().is_array()) {
3823 // OK
3824 } else if (check_sections & REQUIRE_SCENES) {
3825 if (err) {
3826 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
3827 }
3828 return false;
3829 }
3830 }
3831
3832 {
3833 json::const_iterator it = v.find("nodes");
3834 if ((it != v.end()) && it.value().is_array()) {
3835 // OK
3836 } else if (check_sections & REQUIRE_NODES) {
3837 if (err) {
3838 (*err) += "\"nodes\" object not found in .gltf\n";
3839 }
3840 return false;
3841 }
3842 }
3843
3844 {
3845 json::const_iterator it = v.find("accessors");
3846 if ((it != v.end()) && it.value().is_array()) {
3847 // OK
3848 } else if (check_sections & REQUIRE_ACCESSORS) {
3849 if (err) {
3850 (*err) += "\"accessors\" object not found in .gltf\n";
3851 }
3852 return false;
3853 }
3854 }
3855
3856 {
3857 json::const_iterator it = v.find("buffers");
3858 if ((it != v.end()) && it.value().is_array()) {
3859 // OK
3860 } else if (check_sections & REQUIRE_BUFFERS) {
3861 if (err) {
3862 (*err) += "\"buffers\" object not found in .gltf\n";
3863 }
3864 return false;
3865 }
3866 }
3867
3868 {
3869 json::const_iterator it = v.find("bufferViews");
3870 if ((it != v.end()) && it.value().is_array()) {
3871 // OK
3872 } else if (check_sections & REQUIRE_BUFFER_VIEWS) {
3873 if (err) {
3874 (*err) += "\"bufferViews\" object not found in .gltf\n";
3875 }
3876 return false;
3877 }
3878 }
3879
3880 model->buffers.clear();
3881 model->bufferViews.clear();
3882 model->accessors.clear();
3883 model->meshes.clear();
3884 model->cameras.clear();
3885 model->nodes.clear();
3886 model->extensionsUsed.clear();
3887 model->extensionsRequired.clear();
3888 model->extensions.clear();
3889 model->defaultScene = -1;
3890
3891 // 1. Parse Asset
3892 {
3893 json::const_iterator it = v.find("asset");
3894 if ((it != v.end()) && it.value().is_object()) {
3895 const json &root = it.value();
3896
3897 ParseAsset(&model->asset, err, root);
3898 }
3899 }
3900
3901 // 2. Parse extensionUsed
3902 {
3903 json::const_iterator it = v.find("extensionsUsed");
3904 if ((it != v.end()) && it.value().is_array()) {
3905 const json &root = it.value();
3906 for (unsigned int i = 0; i < root.size(); ++i) {
3907 model->extensionsUsed.push_back(root[i].get<std::string>());
3908 }
3909 }
3910 }
3911
3912 {
3913 json::const_iterator it = v.find("extensionsRequired");
3914 if ((it != v.end()) && it.value().is_array()) {
3915 const json &root = it.value();
3916 for (unsigned int i = 0; i < root.size(); ++i) {
3917 model->extensionsRequired.push_back(root[i].get<std::string>());
3918 }
3919 }
3920 }
3921
3922 // 3. Parse Buffer
3923 {
3924 json::const_iterator rootIt = v.find("buffers");
3925 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3926 const json &root = rootIt.value();
3927
3928 json::const_iterator it(root.begin());
3929 json::const_iterator itEnd(root.end());
3930 for (; it != itEnd; it++) {
3931 if (!it.value().is_object()) {
3932 if (err) {
3933 (*err) += "`buffers' does not contain an JSON object.";
3934 }
3935 return false;
3936 }
3937 Buffer buffer;
3938 if (!ParseBuffer(&buffer, err, it->get<json>(), &fs, base_dir,
3939 is_binary_, bin_data_, bin_size_)) {
3940 return false;
3941 }
3942
3943 model->buffers.push_back(buffer);
3944 }
3945 }
3946 }
3947
3948 // 4. Parse BufferView
3949 {
3950 json::const_iterator rootIt = v.find("bufferViews");
3951 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3952 const json &root = rootIt.value();
3953
3954 json::const_iterator it(root.begin());
3955 json::const_iterator itEnd(root.end());
3956 for (; it != itEnd; it++) {
3957 if (!it.value().is_object()) {
3958 if (err) {
3959 (*err) += "`bufferViews' does not contain an JSON object.";
3960 }
3961 return false;
3962 }
3963 BufferView bufferView;
3964 if (!ParseBufferView(&bufferView, err, it->get<json>())) {
3965 return false;
3966 }
3967
3968 model->bufferViews.push_back(bufferView);
3969 }
3970 }
3971 }
3972
3973 // 5. Parse Accessor
3974 {
3975 json::const_iterator rootIt = v.find("accessors");
3976 if ((rootIt != v.end()) && rootIt.value().is_array()) {
3977 const json &root = rootIt.value();
3978
3979 json::const_iterator it(root.begin());
3980 json::const_iterator itEnd(root.end());
3981 for (; it != itEnd; it++) {
3982 if (!it.value().is_object()) {
3983 if (err) {
3984 (*err) += "`accessors' does not contain an JSON object.";
3985 }
3986 return false;
3987 }
3988 Accessor accessor;
3989 if (!ParseAccessor(&accessor, err, it->get<json>())) {
3990 return false;
3991 }
3992
3993 model->accessors.push_back(accessor);
3994 }
3995 }
3996 }
3997
3998 // 6. Parse Mesh
3999 {
4000 json::const_iterator rootIt = v.find("meshes");
4001 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4002 const json &root = rootIt.value();
4003
4004 json::const_iterator it(root.begin());
4005 json::const_iterator itEnd(root.end());
4006 for (; it != itEnd; it++) {
4007 if (!it.value().is_object()) {
4008 if (err) {
4009 (*err) += "`meshes' does not contain an JSON object.";
4010 }
4011 return false;
4012 }
4013 Mesh mesh;
4014 if (!ParseMesh(&mesh, model, err, it->get<json>())) {
4015 return false;
4016 }
4017
4018 model->meshes.push_back(mesh);
4019 }
4020 }
4021 }
4022
4023 // Assign missing bufferView target types
4024 // - Look for missing Mesh indices
4025 // - Look for missing bufferView targets
4026 for (auto &mesh : model->meshes) {
4027 for (auto &primitive : mesh.primitives) {
4028 if (primitive.indices >
4029 -1) // has indices from parsing step, must be Element Array Buffer
4030 {
4031 if (size_t(primitive.indices) >= model->accessors.size()) {
4032 if (err) {
4033 (*err) += "primitive indices accessor out of bounds";
4034 }
4035 return false;
4036 }
4037
4038 auto bufferView = model->accessors[primitive.indices].bufferView;
4039 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
4040 if (err) {
4041 (*err) += "accessor[" + std::to_string(primitive.indices) +
4042 "] invalid bufferView";
4043 }
4044 return false;
4045 }
4046
4047 model->bufferViews[bufferView].target =
4048 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
4049 // we could optionally check if acessors' bufferView type is Scalar, as
4050 // it should be
4051 }
4052 }
4053 }
4054 // find any missing targets, must be an array buffer type if not fulfilled
4055 // from previous check
4056 for (auto &bufferView : model->bufferViews) {
4057 if (bufferView.target == 0) // missing target type
4058 {
4059 bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4060 }
4061 }
4062
4063 // 7. Parse Node
4064 {
4065 json::const_iterator rootIt = v.find("nodes");
4066 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4067 const json &root = rootIt.value();
4068
4069 json::const_iterator it(root.begin());
4070 json::const_iterator itEnd(root.end());
4071 for (; it != itEnd; it++) {
4072 if (!it.value().is_object()) {
4073 if (err) {
4074 (*err) += "`nodes' does not contain an JSON object.";
4075 }
4076 return false;
4077 }
4078 Node node;
4079 if (!ParseNode(&node, err, it->get<json>())) {
4080 return false;
4081 }
4082
4083 model->nodes.push_back(node);
4084 }
4085 }
4086 }
4087
4088 // 8. Parse scenes.
4089 {
4090 json::const_iterator rootIt = v.find("scenes");
4091 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4092 const json &root = rootIt.value();
4093
4094 json::const_iterator it(root.begin());
4095 json::const_iterator itEnd(root.end());
4096 for (; it != itEnd; it++) {
4097 if (!(it.value().is_object())) {
4098 if (err) {
4099 (*err) += "`scenes' does not contain an JSON object.";
4100 }
4101 return false;
4102 }
4103 const json &o = it->get<json>();
4104 std::vector<int> nodes;
4105 if (!ParseIntegerArrayProperty(&nodes, err, o, "nodes", false)) {
4106 return false;
4107 }
4108
4109 Scene scene;
4110 scene.nodes = std::move(nodes);
4111
4112 ParseStringProperty(&scene.name, err, o, "name", false);
4113
4114 ParseExtensionsProperty(&scene.extensions, err, o);
4115 ParseExtrasProperty(&scene.extras, o);
4116
4117 model->scenes.push_back(scene);
4118 }
4119 }
4120 }
4121
4122 // 9. Parse default scenes.
4123 {
4124 json::const_iterator rootIt = v.find("scene");
4125 if ((rootIt != v.end()) && rootIt.value().is_number_integer()) {
4126 const int defaultScene = rootIt.value();
4127
4128 model->defaultScene = defaultScene;
4129 }
4130 }
4131
4132 // 10. Parse Material
4133 {
4134 json::const_iterator rootIt = v.find("materials");
4135 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4136 const json &root = rootIt.value();
4137
4138 json::const_iterator it(root.begin());
4139 json::const_iterator itEnd(root.end());
4140 for (; it != itEnd; it++) {
4141 if (!it.value().is_object()) {
4142 if (err) {
4143 (*err) += "`materials' does not contain an JSON object.";
4144 }
4145 return false;
4146 }
4147 json jsonMaterial = it->get<json>();
4148
4149 Material material;
4150 ParseStringProperty(&material.name, err, jsonMaterial, "name", false);
4151
4152 if (!ParseMaterial(&material, err, jsonMaterial)) {
4153 return false;
4154 }
4155
4156 model->materials.push_back(material);
4157 }
4158 }
4159 }
4160
4161 // 11. Parse Image
4162 {
4163 json::const_iterator rootIt = v.find("images");
4164 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4165 const json &root = rootIt.value();
4166
4167 json::const_iterator it(root.begin());
4168 json::const_iterator itEnd(root.end());
4169 int idx = 0;
4170 for (; it != itEnd; it++, idx++) {
4171 if (!it.value().is_object()) {
4172 if (err) {
4173 (*err) +=
4174 "image[" + std::to_string(idx) + "] is not a JSON object.";
4175 }
4176 return false;
4177 }
4178 Image image;
4179 if (!ParseImage(&image, idx, err, warn, it.value(), base_dir, &fs,
4180 &this->LoadImageData, load_image_user_data_)) {
4181 return false;
4182 }
4183
4184 if (image.bufferView != -1) {
4185 // Load image from the buffer view.
4186 if (size_t(image.bufferView) >= model->bufferViews.size()) {
4187 if (err) {
4188 std::stringstream ss;
4189 ss << "image[" << idx << "] bufferView \"" << image.bufferView
4190 << "\" not found in the scene." << std::endl;
4191 (*err) += ss.str();
4192 }
4193 return false;
4194 }
4195
4196 const BufferView &bufferView =
4197 model->bufferViews[size_t(image.bufferView)];
4198 if (size_t(bufferView.buffer) >= model->buffers.size()) {
4199 if (err) {
4200 std::stringstream ss;
4201 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
4202 << "\" not found in the scene." << std::endl;
4203 (*err) += ss.str();
4204 }
4205 return false;
4206 }
4207 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
4208
4209 if (*LoadImageData == nullptr) {
4210 if (err) {
4211 (*err) += "No LoadImageData callback specified.\n";
4212 }
4213 return false;
4214 }
4215 bool ret = LoadImageData(
4216 &image, idx, err, warn, image.width, image.height,
4217 &buffer.data[bufferView.byteOffset],
4218 static_cast<int>(bufferView.byteLength), load_image_user_data_);
4219 if (!ret) {
4220 return false;
4221 }
4222 }
4223
4224 model->images.push_back(image);
4225 }
4226 }
4227 }
4228
4229 // 12. Parse Texture
4230 {
4231 json::const_iterator rootIt = v.find("textures");
4232 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4233 const json &root = rootIt.value();
4234
4235 json::const_iterator it(root.begin());
4236 json::const_iterator itEnd(root.end());
4237 for (; it != itEnd; it++) {
4238 if (!it.value().is_object()) {
4239 if (err) {
4240 (*err) += "`textures' does not contain an JSON object.";
4241 }
4242 return false;
4243 }
4244 Texture texture;
4245 if (!ParseTexture(&texture, err, it->get<json>(), base_dir)) {
4246 return false;
4247 }
4248
4249 model->textures.push_back(texture);
4250 }
4251 }
4252 }
4253
4254 // 13. Parse Animation
4255 {
4256 json::const_iterator rootIt = v.find("animations");
4257 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4258 const json &root = rootIt.value();
4259
4260 json::const_iterator it(root.begin());
4261 json::const_iterator itEnd(root.end());
4262 for (; it != itEnd; ++it) {
4263 if (!it.value().is_object()) {
4264 if (err) {
4265 (*err) += "`animations' does not contain an JSON object.";
4266 }
4267 return false;
4268 }
4269 Animation animation;
4270 if (!ParseAnimation(&animation, err, it->get<json>())) {
4271 return false;
4272 }
4273
4274 model->animations.push_back(animation);
4275 }
4276 }
4277 }
4278
4279 // 14. Parse Skin
4280 {
4281 json::const_iterator rootIt = v.find("skins");
4282 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4283 const json &root = rootIt.value();
4284
4285 json::const_iterator it(root.begin());
4286 json::const_iterator itEnd(root.end());
4287 for (; it != itEnd; ++it) {
4288 if (!it.value().is_object()) {
4289 if (err) {
4290 (*err) += "`skins' does not contain an JSON object.";
4291 }
4292 return false;
4293 }
4294 Skin skin;
4295 if (!ParseSkin(&skin, err, it->get<json>())) {
4296 return false;
4297 }
4298
4299 model->skins.push_back(skin);
4300 }
4301 }
4302 }
4303
4304 // 15. Parse Sampler
4305 {
4306 json::const_iterator rootIt = v.find("samplers");
4307 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4308 const json &root = rootIt.value();
4309
4310 json::const_iterator it(root.begin());
4311 json::const_iterator itEnd(root.end());
4312 for (; it != itEnd; ++it) {
4313 if (!it.value().is_object()) {
4314 if (err) {
4315 (*err) += "`samplers' does not contain an JSON object.";
4316 }
4317 return false;
4318 }
4319 Sampler sampler;
4320 if (!ParseSampler(&sampler, err, it->get<json>())) {
4321 return false;
4322 }
4323
4324 model->samplers.push_back(sampler);
4325 }
4326 }
4327 }
4328
4329 // 16. Parse Camera
4330 {
4331 json::const_iterator rootIt = v.find("cameras");
4332 if ((rootIt != v.end()) && rootIt.value().is_array()) {
4333 const json &root = rootIt.value();
4334
4335 json::const_iterator it(root.begin());
4336 json::const_iterator itEnd(root.end());
4337 for (; it != itEnd; ++it) {
4338 if (!it.value().is_object()) {
4339 if (err) {
4340 (*err) += "`cameras' does not contain an JSON object.";
4341 }
4342 return false;
4343 }
4344 Camera camera;
4345 if (!ParseCamera(&camera, err, it->get<json>())) {
4346 return false;
4347 }
4348
4349 model->cameras.push_back(camera);
4350 }
4351 }
4352 }
4353
4354 // 17. Parse Extensions
4355 ParseExtensionsProperty(&model->extensions, err, v);
4356
4357 // 18. Specific extension implementations
4358 {
4359 json::const_iterator rootIt = v.find("extensions");
4360 if ((rootIt != v.end()) && rootIt.value().is_object()) {
4361 const json &root = rootIt.value();
4362
4363 json::const_iterator it(root.begin());
4364 json::const_iterator itEnd(root.end());
4365 for (; it != itEnd; ++it) {
4366 // parse KHR_lights_cmn extension
4367 if ((it.key().compare("KHR_lights_cmn") == 0) &&
4368 it.value().is_object()) {
4369 const json &object = it.value();
4370 json::const_iterator itLight(object.find("lights"));
4371 json::const_iterator itLightEnd(object.end());
4372 if (itLight == itLightEnd) {
4373 continue;
4374 }
4375
4376 if (!itLight.value().is_array()) {
4377 continue;
4378 }
4379
4380 const json &lights = itLight.value();
4381 json::const_iterator arrayIt(lights.begin());
4382 json::const_iterator arrayItEnd(lights.end());
4383 for (; arrayIt != arrayItEnd; ++arrayIt) {
4384 Light light;
4385 if (!ParseLight(&light, err, arrayIt.value())) {
4386 return false;
4387 }
4388 model->lights.push_back(light);
4389 }
4390 }
4391 }
4392 }
4393 }
4394
4395 // 19. Parse Extras
4396 ParseExtrasProperty(&model->extras, v);
4397
4398 return true;
4399}
4400
4401bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
4402 std::string *warn, const char *str,
4403 unsigned int length,
4404 const std::string &base_dir,
4405 unsigned int check_sections) {
4406 is_binary_ = false;
4407 bin_data_ = nullptr;
4408 bin_size_ = 0;
4409
4410 return LoadFromString(model, err, warn, str, length, base_dir,
4411 check_sections);
4412}
4413
4414bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
4415 std::string *warn, const std::string &filename,
4416 unsigned int check_sections) {
4417 std::stringstream ss;
4418
4419 if (fs.ReadWholeFile == nullptr) {
4420 // Programmer error, assert() ?
4421 ss << "Failed to read file: " << filename
4422 << ": one or more FS callback not set" << std::endl;
4423 if (err) {
4424 (*err) = ss.str();
4425 }
4426 return false;
4427 }
4428
4429 std::vector<unsigned char> data;
4430 std::string fileerr;
4431 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
4432 if (!fileread) {
4433 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
4434 if (err) {
4435 (*err) = ss.str();
4436 }
4437 return false;
4438 }
4439
4440 size_t sz = data.size();
4441 if (sz == 0) {
4442 if (err) {
4443 (*err) = "Empty file.";
4444 }
4445 return false;
4446 }
4447
4448 std::string basedir = GetBaseDir(filename);
4449
4450 bool ret = LoadASCIIFromString(
4451 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
4452 static_cast<unsigned int>(data.size()), basedir, check_sections);
4453
4454 return ret;
4455}
4456
4457bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
4458 std::string *warn,
4459 const unsigned char *bytes,
4460 unsigned int size,
4461 const std::string &base_dir,
4462 unsigned int check_sections) {
4463 if (size < 20) {
4464 if (err) {
4465 (*err) = "Too short data size for glTF Binary.";
4466 }
4467 return false;
4468 }
4469
4470 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
4471 bytes[3] == 'F') {
4472 // ok
4473 } else {
4474 if (err) {
4475 (*err) = "Invalid magic.";
4476 }
4477 return false;
4478 }
4479
4480 unsigned int version; // 4 bytes
4481 unsigned int length; // 4 bytes
4482 unsigned int model_length; // 4 bytes
4483 unsigned int model_format; // 4 bytes;
4484
4485 // @todo { Endian swap for big endian machine. }
4486 memcpy(&version, bytes + 4, 4);
4487 swap4(&version);
4488 memcpy(&length, bytes + 8, 4);
4489 swap4(&length);
4490 memcpy(&model_length, bytes + 12, 4);
4491 swap4(&model_length);
4492 memcpy(&model_format, bytes + 16, 4);
4493 swap4(&model_format);
4494
4495 // In case the Bin buffer is not present, the size is exactly 20 + size of
4496 // JSON contents,
4497 // so use "greater than" operator.
4498 if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
4499 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
4500 if (err) {
4501 (*err) = "Invalid glTF binary.";
4502 }
4503 return false;
4504 }
4505
4506 // Extract JSON string.
4507 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
4508 model_length);
4509
4510 is_binary_ = true;
4511 bin_data_ = bytes + 20 + model_length +
4512 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
4513 bin_size_ =
4514 length - (20 + model_length); // extract header + JSON scene data.
4515
4516 bool ret = LoadFromString(model, err, warn,
4517 reinterpret_cast<const char *>(&bytes[20]),
4518 model_length, base_dir, check_sections);
4519 if (!ret) {
4520 return ret;
4521 }
4522
4523 return true;
4524}
4525
4526bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
4527 std::string *warn,
4528 const std::string &filename,
4529 unsigned int check_sections) {
4530 std::stringstream ss;
4531
4532 if (fs.ReadWholeFile == nullptr) {
4533 // Programmer error, assert() ?
4534 ss << "Failed to read file: " << filename
4535 << ": one or more FS callback not set" << std::endl;
4536 if (err) {
4537 (*err) = ss.str();
4538 }
4539 return false;
4540 }
4541
4542 std::vector<unsigned char> data;
4543 std::string fileerr;
4544 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
4545 if (!fileread) {
4546 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
4547 if (err) {
4548 (*err) = ss.str();
4549 }
4550 return false;
4551 }
4552
4553 std::string basedir = GetBaseDir(filename);
4554
4555 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
4556 static_cast<unsigned int>(data.size()),
4557 basedir, check_sections);
4558
4559 return ret;
4560}
4561
4563// GLTF Serialization
4565
4566// typedef std::pair<std::string, json> json_object_pair;
4567
4568template <typename T>
4569static void SerializeNumberProperty(const std::string &key, T number,
4570 json &obj) {
4571 // obj.insert(
4572 // json_object_pair(key, json(static_cast<double>(number))));
4573 // obj[key] = static_cast<double>(number);
4574 obj[key] = number;
4575}
4576
4577template <typename T>
4578static void SerializeNumberArrayProperty(const std::string &key,
4579 const std::vector<T> &value,
4580 json &obj) {
4581 json o;
4582 json vals;
4583
4584 for (unsigned int i = 0; i < value.size(); ++i) {
4585 vals.push_back(static_cast<T>(value[i]));
4586 }
4587 if (!vals.is_null()) {
4588 obj[key] = vals;
4589 }
4590}
4591
4592static void SerializeStringProperty(const std::string &key,
4593 const std::string &value, json &obj) {
4594 obj[key] = value;
4595}
4596
4597static void SerializeStringArrayProperty(const std::string &key,
4598 const std::vector<std::string> &value,
4599 json &obj) {
4600 json o;
4601 json vals;
4602
4603 for (unsigned int i = 0; i < value.size(); ++i) {
4604 vals.push_back(value[i]);
4605 }
4606
4607 obj[key] = vals;
4608}
4609
4610static bool ValueToJson(const Value &value, json *ret) {
4611 json obj;
4612 switch (value.Type()) {
4613 case NUMBER_TYPE:
4614 obj = json(value.Get<double>());
4615 break;
4616 case INT_TYPE:
4617 obj = json(value.Get<int>());
4618 break;
4619 case BOOL_TYPE:
4620 obj = json(value.Get<bool>());
4621 break;
4622 case STRING_TYPE:
4623 obj = json(value.Get<std::string>());
4624 break;
4625 case ARRAY_TYPE: {
4626 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
4627 Value elementValue = value.Get(int(i));
4628 json elementJson;
4629 if (ValueToJson(value.Get(int(i)), &elementJson))
4630 obj.push_back(elementJson);
4631 }
4632 break;
4633 }
4634 case BINARY_TYPE:
4635 // TODO
4636 // obj = json(value.Get<std::vector<unsigned char>>());
4637 return false;
4638 break;
4639 case OBJECT_TYPE: {
4640 Value::Object objMap = value.Get<Value::Object>();
4641 for (auto &it : objMap) {
4642 json elementJson;
4643 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
4644 }
4645 break;
4646 }
4647 case NULL_TYPE:
4648 default:
4649 return false;
4650 }
4651 if (ret) *ret = obj;
4652 return true;
4653}
4654
4655static void SerializeValue(const std::string &key, const Value &value,
4656 json &obj) {
4657 json ret;
4658 if (ValueToJson(value, &ret)) obj[key] = ret;
4659}
4660
4661static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
4662 json &o) {
4663 std::string header = "data:application/octet-stream;base64,";
4664 std::string encodedData =
4665 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
4666 SerializeStringProperty("uri", header + encodedData, o);
4667}
4668
4669static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
4670 const std::string &binFilename) {
4671 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
4672 if (!output.is_open()) return false;
4673 output.write(reinterpret_cast<const char *>(&data[0]),
4674 std::streamsize(data.size()));
4675 output.close();
4676 return true;
4677}
4678
4679static void SerializeParameterMap(ParameterMap &param, json &o) {
4680 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
4681 ++paramIt) {
4682 if (paramIt->second.number_array.size()) {
4683 SerializeNumberArrayProperty<double>(paramIt->first,
4684 paramIt->second.number_array, o);
4685 } else if (paramIt->second.json_double_value.size()) {
4686 json json_double_value;
4687 for (std::map<std::string, double>::iterator it =
4688 paramIt->second.json_double_value.begin();
4689 it != paramIt->second.json_double_value.end(); ++it) {
4690 if (it->first == "index") {
4691 json_double_value[it->first] = paramIt->second.TextureIndex();
4692 } else {
4693 json_double_value[it->first] = it->second;
4694 }
4695 }
4696
4697 o[paramIt->first] = json_double_value;
4698 } else if (!paramIt->second.string_value.empty()) {
4699 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
4700 } else if (paramIt->second.has_number_value) {
4701 o[paramIt->first] = paramIt->second.number_value;
4702 } else {
4703 o[paramIt->first] = paramIt->second.bool_value;
4704 }
4705 }
4706}
4707
4708static void SerializeExtensionMap(ExtensionMap &extensions, json &o) {
4709 if (!extensions.size()) return;
4710
4711 json extMap;
4712 for (ExtensionMap::iterator extIt = extensions.begin();
4713 extIt != extensions.end(); ++extIt) {
4714 json extension_values;
4715
4716 // Allow an empty object for extension(#97)
4717 json ret;
4718 if (ValueToJson(extIt->second, &ret)) {
4719 extMap[extIt->first] = ret;
4720 }
4721 if (ret.is_null()) {
4722 if (!(extIt->first.empty())) { // name should not be empty, but for sure
4723 // create empty object so that an extension name is still included in
4724 // json.
4725 extMap[extIt->first] = json({});
4726 }
4727 }
4728 }
4729 o["extensions"] = extMap;
4730}
4731
4732static void SerializeGltfAccessor(Accessor &accessor, json &o) {
4733 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
4734
4735 if (accessor.byteOffset != 0.0)
4736 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
4737
4738 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
4739 SerializeNumberProperty<size_t>("count", accessor.count, o);
4740 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
4741 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
4742 SerializeValue("normalized", Value(accessor.normalized), o);
4743 std::string type;
4744 switch (accessor.type) {
4745 case TINYGLTF_TYPE_SCALAR:
4746 type = "SCALAR";
4747 break;
4748 case TINYGLTF_TYPE_VEC2:
4749 type = "VEC2";
4750 break;
4751 case TINYGLTF_TYPE_VEC3:
4752 type = "VEC3";
4753 break;
4754 case TINYGLTF_TYPE_VEC4:
4755 type = "VEC4";
4756 break;
4757 case TINYGLTF_TYPE_MAT2:
4758 type = "MAT2";
4759 break;
4760 case TINYGLTF_TYPE_MAT3:
4761 type = "MAT3";
4762 break;
4763 case TINYGLTF_TYPE_MAT4:
4764 type = "MAT4";
4765 break;
4766 }
4767
4768 SerializeStringProperty("type", type, o);
4769 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
4770
4771 if (accessor.extras.Type() != NULL_TYPE) {
4772 SerializeValue("extras", accessor.extras, o);
4773 }
4774}
4775
4776static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
4777 SerializeNumberProperty("sampler", channel.sampler, o);
4778 json target;
4779 SerializeNumberProperty("node", channel.target_node, target);
4780 SerializeStringProperty("path", channel.target_path, target);
4781
4782 o["target"] = target;
4783
4784 if (channel.extras.Type() != NULL_TYPE) {
4785 SerializeValue("extras", channel.extras, o);
4786 }
4787}
4788
4789static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
4790 SerializeNumberProperty("input", sampler.input, o);
4791 SerializeNumberProperty("output", sampler.output, o);
4792 SerializeStringProperty("interpolation", sampler.interpolation, o);
4793
4794 if (sampler.extras.Type() != NULL_TYPE) {
4795 SerializeValue("extras", sampler.extras, o);
4796 }
4797}
4798
4799static void SerializeGltfAnimation(Animation &animation, json &o) {
4800 if (!animation.name.empty())
4801 SerializeStringProperty("name", animation.name, o);
4802 json channels;
4803 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
4804 json channel;
4805 AnimationChannel gltfChannel = animation.channels[i];
4806 SerializeGltfAnimationChannel(gltfChannel, channel);
4807 channels.push_back(channel);
4808 }
4809 o["channels"] = channels;
4810
4811 json samplers;
4812 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
4813 json sampler;
4814 AnimationSampler gltfSampler = animation.samplers[i];
4815 SerializeGltfAnimationSampler(gltfSampler, sampler);
4816 samplers.push_back(sampler);
4817 }
4818
4819 o["samplers"] = samplers;
4820
4821 if (animation.extras.Type() != NULL_TYPE) {
4822 SerializeValue("extras", animation.extras, o);
4823 }
4824}
4825
4826static void SerializeGltfAsset(Asset &asset, json &o) {
4827 if (!asset.generator.empty()) {
4828 SerializeStringProperty("generator", asset.generator, o);
4829 }
4830
4831 if (!asset.version.empty()) {
4832 SerializeStringProperty("version", asset.version, o);
4833 }
4834
4835 if (asset.extras.Keys().size()) {
4836 SerializeValue("extras", asset.extras, o);
4837 }
4838
4839 SerializeExtensionMap(asset.extensions, o);
4840}
4841
4842static void SerializeGltfBuffer(Buffer &buffer, json &o) {
4843 SerializeNumberProperty("byteLength", buffer.data.size(), o);
4844 SerializeGltfBufferData(buffer.data, o);
4845
4846 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
4847
4848 if (buffer.extras.Type() != NULL_TYPE) {
4849 SerializeValue("extras", buffer.extras, o);
4850 }
4851}
4852
4853static bool SerializeGltfBuffer(Buffer &buffer, json &o,
4854 const std::string &binFilename,
4855 const std::string &binBaseFilename) {
4856 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
4857 SerializeNumberProperty("byteLength", buffer.data.size(), o);
4858 SerializeStringProperty("uri", binBaseFilename, o);
4859
4860 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
4861
4862 if (buffer.extras.Type() != NULL_TYPE) {
4863 SerializeValue("extras", buffer.extras, o);
4864 }
4865 return true;
4866}
4867
4868static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
4869 SerializeNumberProperty("buffer", bufferView.buffer, o);
4870 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
4871
4872 // byteStride is optional, minimum allowed is 4
4873 if (bufferView.byteStride >= 4) {
4874 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
4875 }
4876 // byteOffset is optional, default is 0
4877 if (bufferView.byteOffset > 0) {
4878 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
4879 }
4880 // Target is optional, check if it contains a valid value
4881 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
4882 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
4883 SerializeNumberProperty("target", bufferView.target, o);
4884 }
4885 if (bufferView.name.size()) {
4886 SerializeStringProperty("name", bufferView.name, o);
4887 }
4888
4889 if (bufferView.extras.Type() != NULL_TYPE) {
4890 SerializeValue("extras", bufferView.extras, o);
4891 }
4892}
4893
4894static void SerializeGltfImage(Image &image, json &o) {
4895 SerializeStringProperty("uri", image.uri, o);
4896
4897 if (image.name.size()) {
4898 SerializeStringProperty("name", image.name, o);
4899 }
4900
4901 if (image.extras.Type() != NULL_TYPE) {
4902 SerializeValue("extras", image.extras, o);
4903 }
4904
4905 SerializeExtensionMap(image.extensions, o);
4906}
4907
4908static void SerializeGltfMaterial(Material &material, json &o) {
4909 if (material.extras.Size()) SerializeValue("extras", material.extras, o);
4910 SerializeExtensionMap(material.extensions, o);
4911
4912 if (material.values.size()) {
4913 json pbrMetallicRoughness;
4914 SerializeParameterMap(material.values, pbrMetallicRoughness);
4915 o["pbrMetallicRoughness"] = pbrMetallicRoughness;
4916 }
4917
4918 SerializeParameterMap(material.additionalValues, o);
4919
4920 if (material.name.size()) {
4921 SerializeStringProperty("name", material.name, o);
4922 }
4923
4924 if (material.extras.Type() != NULL_TYPE) {
4925 SerializeValue("extras", material.extras, o);
4926 }
4927}
4928
4929static void SerializeGltfMesh(Mesh &mesh, json &o) {
4930 json primitives;
4931 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
4932 json primitive;
4933 json attributes;
4934 Primitive gltfPrimitive = mesh.primitives[i];
4935 for (std::map<std::string, int>::iterator attrIt =
4936 gltfPrimitive.attributes.begin();
4937 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
4938 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
4939 }
4940
4941 primitive["attributes"] = attributes;
4942
4943 // Indicies is optional
4944 if (gltfPrimitive.indices > -1) {
4945 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
4946 }
4947 // Material is optional
4948 if (gltfPrimitive.material > -1) {
4949 SerializeNumberProperty<int>("material", gltfPrimitive.material,
4950 primitive);
4951 }
4952 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
4953
4954 // Morph targets
4955 if (gltfPrimitive.targets.size()) {
4956 json targets;
4957 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
4958 json targetAttributes;
4959 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
4960 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
4961 attrIt != targetData.end(); ++attrIt) {
4962 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
4963 targetAttributes);
4964 }
4965
4966 targets.push_back(targetAttributes);
4967 }
4968 primitive["targets"] = targets;
4969 }
4970
4971 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
4972 SerializeValue("extras", gltfPrimitive.extras, primitive);
4973 }
4974
4975 primitives.push_back(primitive);
4976 }
4977
4978 o["primitives"] = primitives;
4979 if (mesh.weights.size()) {
4980 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
4981 }
4982
4983 if (mesh.name.size()) {
4984 SerializeStringProperty("name", mesh.name, o);
4985 }
4986
4987 if (mesh.extras.Type() != NULL_TYPE) {
4988 SerializeValue("extras", mesh.extras, o);
4989 }
4990}
4991
4992static void SerializeGltfLight(Light &light, json &o) {
4993 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
4994 SerializeNumberArrayProperty("color", light.color, o);
4995 SerializeStringProperty("type", light.type, o);
4996}
4997
4998static void SerializeGltfNode(Node &node, json &o) {
4999 if (node.translation.size() > 0) {
5000 SerializeNumberArrayProperty<double>("translation", node.translation, o);
5001 }
5002 if (node.rotation.size() > 0) {
5003 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
5004 }
5005 if (node.scale.size() > 0) {
5006 SerializeNumberArrayProperty<double>("scale", node.scale, o);
5007 }
5008 if (node.matrix.size() > 0) {
5009 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
5010 }
5011 if (node.mesh != -1) {
5012 SerializeNumberProperty<int>("mesh", node.mesh, o);
5013 }
5014
5015 if (node.skin != -1) {
5016 SerializeNumberProperty<int>("skin", node.skin, o);
5017 }
5018
5019 if (node.camera != -1) {
5020 SerializeNumberProperty<int>("camera", node.camera, o);
5021 }
5022
5023 if (node.extras.Type() != NULL_TYPE) {
5024 SerializeValue("extras", node.extras, o);
5025 }
5026
5027 SerializeExtensionMap(node.extensions, o);
5028 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
5029 SerializeNumberArrayProperty<int>("children", node.children, o);
5030}
5031
5032static void SerializeGltfSampler(Sampler &sampler, json &o) {
5033 SerializeNumberProperty("magFilter", sampler.magFilter, o);
5034 SerializeNumberProperty("minFilter", sampler.minFilter, o);
5035 SerializeNumberProperty("wrapR", sampler.wrapR, o);
5036 SerializeNumberProperty("wrapS", sampler.wrapS, o);
5037 SerializeNumberProperty("wrapT", sampler.wrapT, o);
5038
5039 if (sampler.extras.Type() != NULL_TYPE) {
5040 SerializeValue("extras", sampler.extras, o);
5041 }
5042}
5043
5044static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
5045 json &o) {
5046 SerializeNumberProperty("zfar", camera.zfar, o);
5047 SerializeNumberProperty("znear", camera.znear, o);
5048 SerializeNumberProperty("xmag", camera.xmag, o);
5049 SerializeNumberProperty("ymag", camera.ymag, o);
5050
5051 if (camera.extras.Type() != NULL_TYPE) {
5052 SerializeValue("extras", camera.extras, o);
5053 }
5054}
5055
5056static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
5057 json &o) {
5058 SerializeNumberProperty("zfar", camera.zfar, o);
5059 SerializeNumberProperty("znear", camera.znear, o);
5060 if (camera.aspectRatio > 0) {
5061 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
5062 }
5063
5064 if (camera.yfov > 0) {
5065 SerializeNumberProperty("yfov", camera.yfov, o);
5066 }
5067
5068 if (camera.extras.Type() != NULL_TYPE) {
5069 SerializeValue("extras", camera.extras, o);
5070 }
5071}
5072
5073static void SerializeGltfCamera(const Camera &camera, json &o) {
5074 SerializeStringProperty("type", camera.type, o);
5075 if (!camera.name.empty()) {
5076 SerializeStringProperty("name", camera.name, o);
5077 }
5078
5079 if (camera.type.compare("orthographic") == 0) {
5080 json orthographic;
5081 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
5082 o["orthographic"] = orthographic;
5083 } else if (camera.type.compare("perspective") == 0) {
5085 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
5086 o["perspective"] = perspective;
5087 } else {
5088 // ???
5089 }
5090}
5091
5092static void SerializeGltfScene(Scene &scene, json &o) {
5093 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
5094
5095 if (scene.name.size()) {
5096 SerializeStringProperty("name", scene.name, o);
5097 }
5098 if (scene.extras.Type() != NULL_TYPE) {
5099 SerializeValue("extras", scene.extras, o);
5100 }
5101 SerializeExtensionMap(scene.extensions, o);
5102}
5103
5104static void SerializeGltfSkin(Skin &skin, json &o) {
5105 if (skin.inverseBindMatrices != -1)
5106 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
5107
5108 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
5109 SerializeNumberProperty("skeleton", skin.skeleton, o);
5110 if (skin.name.size()) {
5111 SerializeStringProperty("name", skin.name, o);
5112 }
5113}
5114
5115static void SerializeGltfTexture(Texture &texture, json &o) {
5116 if (texture.sampler > -1) {
5117 SerializeNumberProperty("sampler", texture.sampler, o);
5118 }
5119 if (texture.source > -1) {
5120 SerializeNumberProperty("source", texture.source, o);
5121 }
5122 if (texture.extras.Type() != NULL_TYPE) {
5123 SerializeValue("extras", texture.extras, o);
5124 }
5125 SerializeExtensionMap(texture.extensions, o);
5126}
5127
5128static bool WriteGltfFile(const std::string &output,
5129 const std::string &content) {
5130 std::ofstream gltfFile(output.c_str());
5131 if (!gltfFile.is_open()) return false;
5132 gltfFile << content << std::endl;
5133 return true;
5134}
5135
5136static void WriteBinaryGltfFile(const std::string &output,
5137 const std::string &content) {
5138 std::ofstream gltfFile(output.c_str(), std::ios::binary);
5139
5140 const std::string header = "glTF";
5141 const int version = 2;
5142 const int padding_size = content.size() % 4;
5143
5144 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info,
5145 // padding
5146 const int length = 12 + 8 + int(content.size()) + padding_size;
5147
5148 gltfFile.write(header.c_str(), header.size());
5149 gltfFile.write(reinterpret_cast<const char *>(&version), sizeof(version));
5150 gltfFile.write(reinterpret_cast<const char *>(&length), sizeof(length));
5151
5152 // JSON chunk info, then JSON data
5153 const int model_length = int(content.size()) + padding_size;
5154 const int model_format = 0x4E4F534A;
5155 gltfFile.write(reinterpret_cast<const char *>(&model_length),
5156 sizeof(model_length));
5157 gltfFile.write(reinterpret_cast<const char *>(&model_format),
5158 sizeof(model_format));
5159 gltfFile.write(content.c_str(), content.size());
5160
5161 // Chunk must be multiplies of 4, so pad with spaces
5162 if (padding_size > 0) {
5163 const std::string padding = std::string(padding_size, ' ');
5164 gltfFile.write(padding.c_str(), padding.size());
5165 }
5166}
5167
5168bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
5169 bool embedImages = false,
5170 bool embedBuffers = false,
5171 bool prettyPrint = true,
5172 bool writeBinary = false) {
5173 json output;
5174
5175 // ACCESSORS
5176 json accessors;
5177 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
5178 json accessor;
5179 SerializeGltfAccessor(model->accessors[i], accessor);
5180 accessors.push_back(accessor);
5181 }
5182 output["accessors"] = accessors;
5183
5184 // ANIMATIONS
5185 if (model->animations.size()) {
5186 json animations;
5187 for (unsigned int i = 0; i < model->animations.size(); ++i) {
5188 if (model->animations[i].channels.size()) {
5189 json animation;
5190 SerializeGltfAnimation(model->animations[i], animation);
5191 animations.push_back(animation);
5192 }
5193 }
5194 output["animations"] = animations;
5195 }
5196
5197 // ASSET
5198 json asset;
5199 SerializeGltfAsset(model->asset, asset);
5200 output["asset"] = asset;
5201
5202 std::string defaultBinFilename = GetBaseFilename(filename);
5203 std::string defaultBinFileExt = ".bin";
5204 std::string::size_type pos =
5205 defaultBinFilename.rfind('.', defaultBinFilename.length());
5206
5207 if (pos != std::string::npos) {
5208 defaultBinFilename = defaultBinFilename.substr(0, pos);
5209 }
5210 std::string baseDir = GetBaseDir(filename);
5211 if (baseDir.empty()) {
5212 baseDir = "./";
5213 }
5214
5215 // BUFFERS
5216 std::vector<std::string> usedUris;
5217 json buffers;
5218 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
5219 json buffer;
5220 if (embedBuffers) {
5221 SerializeGltfBuffer(model->buffers[i], buffer);
5222 } else {
5223 std::string binSavePath;
5224 std::string binUri;
5225 if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
5226 binUri = model->buffers[i].uri;
5227 } else {
5228 binUri = defaultBinFilename + defaultBinFileExt;
5229 bool inUse = true;
5230 int numUsed = 0;
5231 while (inUse) {
5232 inUse = false;
5233 for (const std::string &usedName : usedUris) {
5234 if (binUri.compare(usedName) != 0) continue;
5235 inUse = true;
5236 binUri = defaultBinFilename + std::to_string(numUsed++) +
5237 defaultBinFileExt;
5238 break;
5239 }
5240 }
5241 }
5242 usedUris.push_back(binUri);
5243 binSavePath = JoinPath(baseDir, binUri);
5244 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
5245 binUri)) {
5246 return false;
5247 }
5248 }
5249 buffers.push_back(buffer);
5250 }
5251 output["buffers"] = buffers;
5252
5253 // BUFFERVIEWS
5254 json bufferViews;
5255 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
5256 json bufferView;
5257 SerializeGltfBufferView(model->bufferViews[i], bufferView);
5258 bufferViews.push_back(bufferView);
5259 }
5260 output["bufferViews"] = bufferViews;
5261
5262 // Extensions used
5263 if (model->extensionsUsed.size()) {
5264 SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed,
5265 output);
5266 }
5267
5268 // Extensions required
5269 if (model->extensionsRequired.size()) {
5270 SerializeStringArrayProperty("extensionsRequired",
5271 model->extensionsRequired, output);
5272 }
5273
5274 // IMAGES
5275 if (model->images.size()) {
5276 json images;
5277 for (unsigned int i = 0; i < model->images.size(); ++i) {
5278 json image;
5279
5280 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
5281 &this->WriteImageData, this->write_image_user_data_);
5282 SerializeGltfImage(model->images[i], image);
5283 images.push_back(image);
5284 }
5285 output["images"] = images;
5286 }
5287
5288 // MATERIALS
5289 if (model->materials.size()) {
5290 json materials;
5291 for (unsigned int i = 0; i < model->materials.size(); ++i) {
5292 json material;
5293 SerializeGltfMaterial(model->materials[i], material);
5294 materials.push_back(material);
5295 }
5296 output["materials"] = materials;
5297 }
5298
5299 // MESHES
5300 if (model->meshes.size()) {
5301 json meshes;
5302 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
5303 json mesh;
5304 SerializeGltfMesh(model->meshes[i], mesh);
5305 meshes.push_back(mesh);
5306 }
5307 output["meshes"] = meshes;
5308 }
5309
5310 // NODES
5311 if (model->nodes.size()) {
5312 json nodes;
5313 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
5314 json node;
5315 SerializeGltfNode(model->nodes[i], node);
5316 nodes.push_back(node);
5317 }
5318 output["nodes"] = nodes;
5319 }
5320
5321 // SCENE
5322 if (model->defaultScene > -1) {
5323 SerializeNumberProperty<int>("scene", model->defaultScene, output);
5324 }
5325
5326 // SCENES
5327 if (model->scenes.size()) {
5328 json scenes;
5329 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
5330 json currentScene;
5331 SerializeGltfScene(model->scenes[i], currentScene);
5332 scenes.push_back(currentScene);
5333 }
5334 output["scenes"] = scenes;
5335 }
5336
5337 // SKINS
5338 if (model->skins.size()) {
5339 json skins;
5340 for (unsigned int i = 0; i < model->skins.size(); ++i) {
5341 json skin;
5342 SerializeGltfSkin(model->skins[i], skin);
5343 skins.push_back(skin);
5344 }
5345 output["skins"] = skins;
5346 }
5347
5348 // TEXTURES
5349 if (model->textures.size()) {
5350 json textures;
5351 for (unsigned int i = 0; i < model->textures.size(); ++i) {
5352 json texture;
5353 SerializeGltfTexture(model->textures[i], texture);
5354 textures.push_back(texture);
5355 }
5356 output["textures"] = textures;
5357 }
5358
5359 // SAMPLERS
5360 if (model->samplers.size()) {
5361 json samplers;
5362 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
5363 json sampler;
5364 SerializeGltfSampler(model->samplers[i], sampler);
5365 samplers.push_back(sampler);
5366 }
5367 output["samplers"] = samplers;
5368 }
5369
5370 // CAMERAS
5371 if (model->cameras.size()) {
5372 json cameras;
5373 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
5374 json camera;
5375 SerializeGltfCamera(model->cameras[i], camera);
5376 cameras.push_back(camera);
5377 }
5378 output["cameras"] = cameras;
5379 }
5380
5381 // EXTENSIONS
5382 SerializeExtensionMap(model->extensions, output);
5383
5384 // LIGHTS as KHR_lights_cmn
5385 if (model->lights.size()) {
5386 json lights;
5387 for (unsigned int i = 0; i < model->lights.size(); ++i) {
5388 json light;
5389 SerializeGltfLight(model->lights[i], light);
5390 lights.push_back(light);
5391 }
5392 json khr_lights_cmn;
5393 khr_lights_cmn["lights"] = lights;
5394 json ext_j;
5395
5396 if (output.find("extensions") != output.end()) {
5397 ext_j = output["extensions"];
5398 }
5399
5400 ext_j["KHR_lights_cmn"] = khr_lights_cmn;
5401
5402 output["extensions"] = ext_j;
5403 }
5404
5405 // EXTRAS
5406 if (model->extras.Type() != NULL_TYPE) {
5407 SerializeValue("extras", model->extras, output);
5408 }
5409
5410 if (writeBinary) {
5411 WriteBinaryGltfFile(filename, output.dump());
5412 } else {
5413 WriteGltfFile(filename, output.dump(prettyPrint ? 2 : -1));
5414 }
5415
5416 return true;
5417}
5418
5419} // namespace tinygltf
5420
5421#ifdef __clang__
5422#pragma clang diagnostic pop
5423#endif
5424
5425#endif // TINYGLTF_IMPLEMENTATION
Definition camera.h:37
Definition format.h:4067
Definition base.h:1940
a class to store JSON values
Definition json.hpp:12544
Definition tiny_gltf.h:796
Definition tiny_gltf.h:718
Definition tiny_gltf.h:915
bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, const std::string &filename, unsigned int check_sections=REQUIRE_ALL)
void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data)
bool WriteGltfSceneToFile(Model *model, const std::string &filename, bool embedImages, bool embedBuffers, bool prettyPrint, bool writeBinary)
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_ALL)
void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data)
bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, const std::string &filename, unsigned int check_sections=REQUIRE_ALL)
void SetFsCallbacks(FsCallbacks callbacks)
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_ALL)
Definition tiny_gltf.h:226
GLM_FUNC_DECL mat< 4, 4, T, defaultp > perspective(T fovy, T aspect, T near, T far)
Definition matrix_clip_space.inl:338
uint8 uint8_t
Definition fwd.hpp:103
int32 int32_t
Definition fwd.hpp:71
uint16 uint16_t
Definition fwd.hpp:117
basic_json<> json
default JSON class
Definition json.hpp:110
Definition tiny_gltf.h:567
int ByteStride(const BufferView &bufferViewObject) const
Definition tiny_gltf.h:599
Definition tiny_gltf.h:426
Definition tiny_gltf.h:437
Definition tiny_gltf.h:448
Definition tiny_gltf.h:767
Definition tiny_gltf.h:552
Definition tiny_gltf.h:757
Definition tiny_gltf.h:670
Definition tiny_gltf.h:892
Definition tiny_gltf.h:492
Definition tiny_gltf.h:788
Definition tiny_gltf.h:540
Definition tiny_gltf.h:707
Definition tiny_gltf.h:657
Definition tiny_gltf.h:362
int TextureIndex() const
Definition tiny_gltf.h:377
ColorValue ColorFactor() const
Definition tiny_gltf.h:404
int TextureTexCoord() const
Definition tiny_gltf.h:388
double Factor() const
Definition tiny_gltf.h:399
Definition tiny_gltf.h:639
Definition tiny_gltf.h:684
Definition tiny_gltf.h:470
Definition tiny_gltf.h:778
Definition tiny_gltf.h:457
Definition tiny_gltf.h:525