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(¶m->string_value, err, o, prop, false)) {
2033 // Found string property.
2034 return true;
2035 } else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop,
2036 false)) {
2037 // Found a number array.
2038 return true;
2039 } else if (ParseNumberProperty(¶m->number_value, err, o, prop, false)) {
2040 return param->has_number_value = true;
2041 } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop,
2042 false)) {
2043 return true;
2044 } else if (ParseBooleanProperty(¶m->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(¶m, 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(¶m, 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 ¶m, 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