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