1 /// TinyGLTF is a library for loading JSON serialized (embedded) GLTF models.
2 module tinygltf;
3 
4 import std.stdio;
5 import std.string;
6 import std.file;
7 import std.json;
8 import core.stdcpp.array;
9 import std.conv;
10 import std.algorithm.iteration;
11 import std.base64;
12 
13 /// OpenGL rendering mode POINTS.
14 enum TINYGLTF_MODE_POINTS = (0);
15 /// OpenGL rendering mode LINE.
16 enum TINYGLTF_MODE_LINE = (1);
17 /// OpenGL rendering mode LINE LOOP.
18 enum TINYGLTF_MODE_LINE_LOOP = (2);
19 /// OpenGL rendering mode LINE STRIP.
20 enum TINYGLTF_MODE_LINE_STRIP = (3);
21 /// OpenGL rendering mode TRIANGLES.
22 enum TINYGLTF_MODE_TRIANGLES = (4);
23 /// OpenGL rendering mode TRIANGLE STRIP.
24 enum TINYGLTF_MODE_TRIANGLE_STRIP = (5);
25 /// OpenGL rendering mode TRIANGLE FAN.
26 enum TINYGLTF_MODE_TRIANGLE_FAN = (6);
27 
28 /// GLTF data component type BYTE.
29 enum TINYGLTF_COMPONENT_TYPE_BYTE = (5120);
30 /// GLTF data component type UNSIGNED BYTE.
31 enum TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE = (5121);
32 /// GLTF data component type SHORT.
33 enum TINYGLTF_COMPONENT_TYPE_SHORT = (5122);
34 /// GLTF data component type UNSIGNED SHORT.
35 enum TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT = (5123);
36 /// GLTF data component type INTEGER.
37 enum TINYGLTF_COMPONENT_TYPE_INT = (5124);
38 /// GLTF data component type UNSIGNED INTEGER.
39 enum TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT = (5125);
40 /// GLTF data component type FLOAT.
41 enum TINYGLTF_COMPONENT_TYPE_FLOAT = (5126);
42 /**
43     OpenGL double type. Note that some of glTF 2.0 validator does not;
44     support double type even the schema seems allow any value of
45     integer:
46     https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
47 */
48 enum TINYGLTF_COMPONENT_TYPE_DOUBLE = (5130);
49 
50 /// OpenGL texture filtering mode NEAREST.
51 enum TINYGLTF_TEXTURE_FILTER_NEAREST = (9728);
52 /// OpenGL texture filtering mode LINEAR.
53 enum TINYGLTF_TEXTURE_FILTER_LINEAR = (9729);
54 /// OpenGL texture filtering mode NEAREST MIPMAP NEAREST.
55 enum TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST = (9984);
56 /// OpenGL texture filtering mode LINEAR MIPMAP NEAREST.
57 enum TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST = (9985);
58 /// OpenGL texture filtering mode NEAREST MIPMAP LINEAR.
59 enum TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR = (9986);
60 /// OpenGL texture filtering mode LINEAR MIPMAP LINEAR.
61 enum TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR = (9987);
62 
63 /// OpenGL texture wrap mode REPEAT.
64 enum TINYGLTF_TEXTURE_WRAP_REPEAT = (10497);
65 /// OpenGL texture wrap mode CLAMP TO EDGE.
66 enum TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE = (33071);
67 /// OpenGL texture wrap mode MIRRORED REPEAT.
68 enum TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT = (33648);
69 
70 // Redeclarations of the above for technique.parameters.
71 /// GLTF data component type BYTE.
72 enum TINYGLTF_PARAMETER_TYPE_BYTE = (5120);
73 /// GLTF data component type UNSIGNED BYTE.
74 enum TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE = (5121);
75 /// GLTF data component type SHORT.
76 enum TINYGLTF_PARAMETER_TYPE_SHORT = (5122);
77 /// GLTF data component type UNSIGNED SHORT.
78 enum TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT = (5123);
79 /// GLTF data component type INTEGER.
80 enum TINYGLTF_PARAMETER_TYPE_INT = (5124);
81 /// GLTF data component type UNSIGNED INTEGER.
82 enum TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT = (5125);
83 /// GLTF data component type FLOAT.
84 enum TINYGLTF_PARAMETER_TYPE_FLOAT = (5126);
85 
86 /// GLTF float Vector 2 parameter type.
87 enum TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 = (35664);
88 /// GLTF float Vector 3 parameter type.
89 enum TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 = (35665);
90 /// GLTF float Vector 4 parameter type.
91 enum TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 = (35666);
92 
93 /// GLTF integer Vector 2 parameter type.
94 enum TINYGLTF_PARAMETER_TYPE_INT_VEC2 = (35667);
95 /// GLTF integer Vector 3 parameter type.
96 enum TINYGLTF_PARAMETER_TYPE_INT_VEC3 = (35668);
97 /// GLTF integer Vector 4 parameter type.
98 enum TINYGLTF_PARAMETER_TYPE_INT_VEC4 = (35669);
99 
100 /// GLTF Boolean parameter type.
101 enum TINYGLTF_PARAMETER_TYPE_BOOL = (35670);
102 /// GLTF Boolean Vector 2 parameter type.
103 enum TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 = (35671);
104 /// GLTF Boolean Vector 3 parameter type.
105 enum TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 = (35672);
106 /// GLTF Boolean Vector 4 parameter type.
107 enum TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 = (35673);
108 
109 /// GLTF float Matrix 2x2 parameter type.
110 enum TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 = (35674);
111 /// GLTF float Matrix 3x3 parameter type.
112 enum TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 = (35675);
113 /// GLTF float Matrix 4x4 parameter type.
114 enum TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 = (35676);
115 
116 // End parameter types
117 
118 /// GLTF Vector 2 data type.
119 enum TINYGLTF_TYPE_VEC2 = (2);
120 /// GLTF Vector 3 data type.
121 enum TINYGLTF_TYPE_VEC3 = (3);
122 /// GLTF Vector 4 data type.
123 enum TINYGLTF_TYPE_VEC4 = (4);
124 
125 /// GLTF Matrix 2x2 data type.
126 enum TINYGLTF_TYPE_MAT2 = (32 + 2);
127 /// GLTF Matrix 3x3 data type.
128 enum TINYGLTF_TYPE_MAT3 = (32 + 3);
129 /// GLTF Matrix 4x4 data type.
130 enum TINYGLTF_TYPE_MAT4 = (32 + 4);
131 
132 /// GLTF Scalar data type.
133 enum TINYGLTF_TYPE_SCALAR = (64 + 1);
134 /// GLTF Vector data type.
135 enum TINYGLTF_TYPE_VECTOR = (64 + 4);
136 /// GLTF Matrix data type.
137 enum TINYGLTF_TYPE_MATRIX = (64 + 16);
138 
139 /// Enumerators for Values to define which type they are.
140 enum Type {
141     NULL_TYPE,
142     REAL_TYPE,
143     INT_TYPE,
144     BOOL_TYPE,
145     STRING_TYPE,
146     ARRAY_TYPE,
147     BINARY_TYPE,
148     OBJECT_TYPE
149 }
150 
151 /// Null type enumerator. D type nothing/null.
152 alias NULL_TYPE   = Type.NULL_TYPE;
153 /// Double type enumerator. D type double.
154 alias REAL_TYPE   = Type.REAL_TYPE;
155 /// Integer type enumerator. D type int.
156 alias INT_TYPE    = Type.INT_TYPE;
157 /// Boolean type enumerator. D type bool.
158 alias BOOL_TYPE   = Type.BOOL_TYPE;
159 /// String type enumerator. D type string.
160 alias STRING_TYPE = Type.STRING_TYPE;
161 /// Array type enumerator. D type Value[].
162 alias ARRAY_TYPE  = Type.ARRAY_TYPE;
163 /// Binary type enumerator. D type ubyte[].
164 alias BINARY_TYPE = Type.BINARY_TYPE;
165 /// Object type enumerator. D type Value[[str][ing]].
166 alias OBJECT_TYPE = Type.OBJECT_TYPE;
167 
168 /// Gets the component size in byte size. (Integer)
169 pragma(inline, true) private int getComponentSizeInBytes(uint componentType) {
170     if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
171         return 1;
172     } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
173         return 1;
174     } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
175         return 2;
176     } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
177         return 2;
178     } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
179         return 4;
180     } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
181         return 4;
182     } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
183         return 4;
184     } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
185         return 8;
186     } else {
187         // Unknown component type
188         return -1;
189     }
190 }
191 
192 /// Gets the number of components in a type. (For example vec3 has 3 components.)
193 pragma(inline, true) private int getNumComponentsInType(uint ty) {
194     if (ty == TINYGLTF_TYPE_SCALAR) {
195         return 1;
196     } else if (ty == TINYGLTF_TYPE_VEC2) {
197         return 2;
198     } else if (ty == TINYGLTF_TYPE_VEC3) {
199         return 3;
200     } else if (ty == TINYGLTF_TYPE_VEC4) {
201         return 4;
202     } else if (ty == TINYGLTF_TYPE_MAT2) {
203         return 4;
204     } else if (ty == TINYGLTF_TYPE_MAT3) {
205         return 9;
206     } else if (ty == TINYGLTF_TYPE_MAT4) {
207         return 16;
208     } else {
209         // Unknown component type
210         return -1;
211     }
212 }
213 
214 //* Translation Note: This whole thing is duck typed
215 /// Simple class to represent a JSON object.
216 class Value {
217 
218 public:
219     // The value becomes whatever it is constructed with
220 
221     /// Construct as a NULL type
222     this() {
223         this.type_ = NULL_TYPE;
224         this.int_value_ = 0;
225         this.real_value_ = 0.0;
226         this.boolean_value_ = false;
227     }
228     /// Construct as a BOOL (boolean) type.
229     this(bool b) {
230         this.boolean_value_ = b;
231         this.type_ = BOOL_TYPE;
232     }
233     /// Construct as a INT (integer) type.
234     this(int i) {
235         this.int_value_ = i;
236         this.real_value_ = i;
237         this.type_ = INT_TYPE;
238     }
239     /// Construct as a REAL (double) type.
240     this(double n) {
241         this.real_value_ = n;
242         this.type_ = REAL_TYPE;
243     }
244     /// Construct as a STRING (string) type.
245     this(string s) {
246         this.string_value_ = s;
247         this.type_ = STRING_TYPE;
248     }
249     /// Construct as a BINARY (ubyte[]) type.
250     this(ubyte[] v) {
251         this.binary_value_ = v;
252         this.type_ = BINARY_TYPE;
253     }
254     /// Construct as an ARRAY (Value[]) type.
255     this(Value[] a) {
256         this.array_value_ = a;
257         this.type_ = ARRAY_TYPE;
258     }
259     /// Construct as an OBJECT (Value[string]) type.
260     this(Value[string] o) {
261         this.object_value_ = o;
262         this.type_ = OBJECT_TYPE;
263     }
264     /// Get what Type this Value is.
265     Type type(){
266         return this.type_;
267     }
268     /// Gets if it's a bool.
269     bool isBool() {
270         return (this.type_ == BOOL_TYPE);
271     }
272     /// Gets if it's an integer.
273     bool isInt() {
274         return (this.type_ == INT_TYPE);
275     }
276     /// Gets if it's a number.
277     bool isNumber() {
278         return (this.type_ == REAL_TYPE) || (this.type_ == INT_TYPE);
279     }
280     /// Gets if it's a double.
281     bool isReal() {
282         return (this.type_ == REAL_TYPE);
283     }
284     /// Gets if it's a string.
285     bool isString() {
286         return (this.type_ == STRING_TYPE);
287     }
288     /// Gets if it's a binary.
289     bool isBinary() {
290         return (this.type_ == BINARY_TYPE);
291     }
292     /// Gets if it's an array.
293     bool isArray() {
294         return (this.type_ == ARRAY_TYPE);
295     }
296     /// Gets if it's an object.
297     bool isObject() {
298         return (this.type_ == OBJECT_TYPE);
299     }
300 
301     /// Use this function if you want to have number value as double.
302     double getNumberAsDouble() {
303         if (this.type_ == INT_TYPE) {
304             return cast(double)this.int_value_;
305         } else {
306             return this.real_value_;
307         }
308     }
309 
310     // TODO(syoyo): Support int value larger than 32 bits
311     /// Use this function if you want to have number value as int.
312     int getNumberAsInt() {
313         if (this.type_ == REAL_TYPE) {
314             return cast(int)this.real_value_;
315         } else {
316             return this.int_value_;
317         }
318     }
319 
320     /// Lookup value from an array.
321     Value get(int idx) {
322         static Value null_value;
323         assert(this.isArray());
324         assert(idx >= 0);
325         return (idx < this.array_value_.length) ? array_value_[idx] : null_value;
326     }
327 
328     /// Lookup value from a key-value pair.
329     Value get(const string key) {
330         static Value null_value;
331         assert(this.isArray());
332         assert(this.isObject());
333         return object_value_.get(key, null_value);
334     }
335 
336     /// Get the length of the array if this is an array.
337     size_t arrayLen() {
338         if (!this.isArray())
339             return 0;
340         return this.array_value_.length;
341     }
342 
343     /// Valid only for object type.
344     bool has(const string key) {
345         if (!this.isObject())
346             return false;
347         return (key in this.object_value_) !is null;
348     }
349 
350     /// List keys
351     string[] keys() {
352         /// Clone in memory
353         string[] tempKeys;
354         foreach (k,v; this.object_value_) {
355             tempKeys ~= k;
356         }
357         return tempKeys;
358     }
359 
360     size_t size() {
361         return (this.isArray() ? this.arrayLen() : keys().length);
362     }
363 
364     //* Translation note: This is the more D way to do this than the weird mixin in C
365     mixin(TINYGLTF_VALUE_GET("bool", "boolean_value_"));
366     mixin(TINYGLTF_VALUE_GET("double", "real_value_"));
367     mixin(TINYGLTF_VALUE_GET("int", "int_value_"));
368     mixin(TINYGLTF_VALUE_GET("string", "string_value_"));
369     mixin(TINYGLTF_VALUE_GET("ubyteArray", "binary_value_", "ubyte[]"));
370     mixin(TINYGLTF_VALUE_GET("Array", "array_value_", "Value[]"));
371     mixin(TINYGLTF_VALUE_GET("Object", "object_value_", "Value[string]"));
372 
373 protected:
374 
375     Type type_ = NULL_TYPE;
376 
377     int int_value_ = 0;
378     double real_value_ = 0.0;
379     string string_value_;
380     ubyte[] binary_value_;
381     Value[] array_value_;
382     Value[string] object_value_;
383     bool boolean_value_ = false;
384     
385 }
386 
387 // Generate mixins so I don't have to type a bunch of things.
388 private string TINYGLTF_VALUE_GET(string ctype, string var, string returnType = "") {
389     if (returnType == "") {
390         returnType = ctype;
391     }
392     const string fancyCType = capitalize(ctype);
393     return "\n" ~
394     returnType ~ " Get" ~ fancyCType ~ "() {\n" ~
395          "return this." ~ var ~ ";\n" ~
396     "}";
397 }
398 
399 /**
400     Holds animation channels. Animations channels are the root of the animation in the model.
401     An animation channel combines an animation sampler with a target property being animated.
402 */
403 class AnimationChannel {
404     /// Required. Points to the index of the AnimationSampler.
405     int sampler = -1;
406     /**
407         REQUIRED. The index of the node to target (alternative target should be provided by extension).
408         Extensions are not supported in this translation so this is required.
409         This is the joint if you're wondering.
410     */
411     int target_node = -1;
412 
413     /**
414         REQUIRED with standard values of ["translation", "rotation", "scale", "weights"]
415     */
416     string target_path;
417 
418     this(int sampler = -1, int target_node = -1) {
419         this.sampler = sampler;
420         this.target_node = target_node;
421     }
422 }
423 
424 /**
425     An animation sampler combines timestamps with a sequence of output values and defines an interpolation algorithm.
426 */
427 class AnimationSampler {
428     /// REQUIRED. The index of an accessor containing keyframe timestamps.
429     int input = -1;
430     /// REQUIRED. The index of an accessor, containing keyframe output values.
431     int output = -1;
432     /**
433         Interpolation algorithm.
434         "LINEAR", "STEP","CUBICSPLINE" or user defined string.
435         Default: "LINEAR".
436     */
437     string interpolation = "LINEAR";
438     
439     this(int input = -1, int output = -1, string interpolation = "LINEAR"){
440         this.input = input;
441         this.output = output;
442         this.interpolation = interpolation;
443     }
444 }
445 
446 /**
447     A keyframe animation.
448 */
449 class Animation {
450     /// The animation name.
451     string name;
452     /**
453         An array of animation channels. An animation channel combines an animation sampler with a target property being animated.
454         Different channels of the same animation MUST NOT have the same targets.
455     */
456     AnimationChannel[] channels;
457     /**
458         An array of animation samplers.
459         An animation sampler combines timestamps with a sequence of output values and defines an interpolation algorithm.
460     */
461     AnimationSampler[] samplers;
462     
463     this() {}
464 }
465 
466 /**
467     Joints and matrices defining a skin.
468 */
469 class Skin {
470     /// Name of the skin.
471     string name;
472     /// REQUIRED. The index of the accessor containing the floating-point 4x4 inverse-bind matrices.
473     int inverseBindMatrices = -1;
474     /// The index of the node used as a skeleton root.
475     int skeleton = -1;
476     /// Indices of skeleton nodes, used as joints in this skin.
477     int[] joints;
478 
479     this() {}
480 }
481 
482 /**
483     Texture sampler properties for filtering and wrapping modes.
484 */
485 class Sampler {
486     /// The name of this sampler.
487     string name;
488     // glTF 2.0 spec does not define default value for `minFilter` and
489     // `magFilter`. Set -1 in TinyGLTF(issue #186)
490 
491     /**
492         OPTIONAL. -1 is no filter defined. ["NEAREST", "LINEAR",
493         "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
494         "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
495     */
496     int minFilter = -1;
497 
498     /// OPTIONAL. -1 is no filter defined. ["NEAREST", "LINEAR"]
499     int magFilter = -1;
500     
501     /// ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default "REPEAT"
502     int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
503 
504     /// ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", "REPEAT"], default "REPEAT"
505     int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
506 
507     this(int minFilter = -1, int magFilter = -1, int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT, int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT) {
508         this.minFilter = minFilter;
509         this.magFilter = magFilter;
510         this.wrapS = wrapS;
511         this.wrapT = wrapT;
512     }
513 }
514 
515 /**
516     A view into a buffer generally representing a subset of the buffer.
517 */
518 class BufferView {
519     /// The name of the bufferView.
520     string name;
521     /// REQUIRED. The index of the buffer.
522     int buffer = -1;
523     /**
524         The offset into the buffer in bytes.
525         Minimum 0, default 0.
526     */
527     size_t byteOffset = 0;
528     /**
529         The length of the bufferView in bytes.
530         REQUIRED. Minimum 1. 0 is invalid.
531     */
532     size_t byteLength = 0;
533     /**
534         The stride, in bytes.
535         Minimum 4. Maximum 252 (multiple of 4). Default 0 is understood to be tightly packed.
536     */
537     size_t byteStride = 0;
538     /**
539         The hint representing the intended GPU buffer type to use with this buffer view.
540         ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices or attribs. Could be 0 for other data.
541     */
542     int target = 0;
543 
544     this(int buffer = -1, int byteOffset = 0, int byteLength = 0, int byteStride = 0, int target = 0) {
545         this.buffer = buffer;
546         this.byteOffset = byteOffset;
547         this.byteLength = byteLength;
548         this.byteStride = byteStride;
549         this.target = target;
550     }
551 }
552 
553 /**
554     A typed view into a buffer view that contains raw binary data.
555 */
556 class Accessor {
557     /**
558         The index of the bufferView.
559         REQUIRED here since sparse accessor are not supported.
560     */
561     int bufferView = -1;
562     /// The name of the Accessor.
563     string name;
564     /** 
565         The offset relative to the start of the buffer view in bytes.
566     */
567     size_t byteOffset = 0;
568     /**
569         Specifies whether integer data values are normalized before usage.
570         OPTIONAL.
571     */
572     bool normalized = false;
573     /**
574         The datatype of the accessor’s components.
575         REQUIRED. One of TINYGLTF_COMPONENT_TYPE_***
576     */
577     int componentType = -1;
578     /**
579         REQUIRED. The number of elements referenced by this accessor.
580     */
581     int count = 0;
582     /**
583         Specifies if the accessor’s elements are scalars, vectors, or matrices.
584         REQUIRED. One of TINYGLTF_TYPE_***
585     */
586     int type = -1;
587     /**
588         Minimum value of each component in this accessor.
589         OPTIONAL. Integer value is promoted to double.
590     */
591     double[] minValues;
592     /**
593         Maximum value of each component in this accessor.
594         OPTIONAL. Integer value is promoted to double.
595     */
596     double[] maxValues;
597 
598     /**
599         Utility function to compute byteStride for a given bufferView object.
600         Returns -1 upon invalid glTF value or parameter configuration.
601     */
602     int byteStride(const BufferView bufferViewObject) const {
603         if (bufferViewObject.byteStride == 0) {
604             // Assume data is tightly packed.
605             int componentSizeInBytes = getComponentSizeInBytes(componentType);
606 
607             if (componentSizeInBytes <= 0) {
608                 return -1;
609             }
610             
611             int numComponents = getNumComponentsInType(type);
612             
613             if (numComponents <= 0) {
614                 return -1;
615             }
616 
617             return componentSizeInBytes * numComponents;
618 
619         } else {
620             // Check if byteStride is a multiple of the size of the accessor's component
621             // type.
622             int componentSizeInBytes = getComponentSizeInBytes(componentType);
623 
624             if (componentSizeInBytes <= 0) {
625                 return -1;
626             }
627 
628             if ((bufferViewObject.byteStride % componentSizeInBytes) != 0) {
629                 return -1;
630             }
631             return cast(int)bufferViewObject.byteStride;
632         }
633 
634         // unreachable return 0;
635         // return 0;
636     }
637 
638     this(int bufferView = -1, int byteOffset = 0, bool normalized = false, int componentType = -1, int count = 0, int type = -1) {
639         this.bufferView = bufferView;
640         this.byteOffset = byteOffset;
641         this.normalized = normalized;
642         this.componentType = componentType;
643         this.count = count;
644         this.type = type;
645     }
646 }
647 
648 /**
649     Geometry to be rendered with the given material.
650 */
651 class Primitive {
652     /**
653         REQUIRED. A plain JSON object, where each key corresponds to a mesh attribute semantic and each
654         value is the index of the accessor containing attribute’s data.
655     */
656     int[string] attributes;
657 
658     /// The index of the material to apply to this primitive when rendering.
659     int material = -1;
660     /// The index of the accessor that contains the vertex indices.
661     int indices = -1;
662     /**
663         The topology type of primitives to render.
664         One of TINYGLTF_MODE_***
665     */
666     int mode = -1;
667 
668     /**
669         An array of morph targets. Each target is an associative array with attributes in
670         ["POSITION, "NORMAL", "TANGENT"] pointing to their corresponding accessors.
671     */
672     int[string][] targets;
673                             
674 
675     this(int material = -1, int indices = -1, int mode = -1) {
676         this.material = material;
677         this.indices = indices;
678         this.mode = mode;
679     }
680 }
681 
682 /**
683     A set of primitives to be rendered.
684     Its global transform is defined by a node that references it.
685 */
686 class Mesh {
687     /// The name of the mesh.
688     string name;
689     /// An array of primitives, each defining geometry to be rendered.
690     Primitive[] primitives;
691     /**
692         Array of weights to be applied to the morph targets.
693         The number of array elements MUST match the number of morph targets.
694     */
695     double[] weights;
696 
697     this() {}
698 }
699 /**
700     A node in the node hierarchy.
701     When the node contains skin, all mesh.primitives MUST contain JOINTS_0 and WEIGHTS_0 attributes.
702     A node MAY have either a matrix or any combination of translation/rotation/scale (TRS) properties.
703     TRS properties are converted to matrices and postmultiplied in the T * R * S order to compose the transformation matrix.
704     First the scale is applied to the vertices, then the rotation, and then the translation.
705     If none are provided, the transform is the identity.
706     When a node is targeted for animation (referenced by an animation.channel.target), matrix MUST NOT be present.
707 */
708 class Node {
709 
710 public:
711     /// The index of the camera referenced by this node.
712     int camera = -1;
713     /// The name of this node.
714     string name;
715     /// The index of the skin referenced by this node.
716     int skin = -1;
717     /// The index of the mesh in this node.
718     int mesh = -1;
719     /// The indices of this node’s children.
720     int[] children;
721     /**
722         The node’s unit quaternion rotation in the order (x, y, z, w), where w is the scalar.
723         Length must be 0 or 4.
724     */
725     double[] rotation;
726     /**
727         The node’s non-uniform scale, given as the scaling factors along the x, y, and z axes.
728         Length must be 0 or 3.
729     */
730     double[] scale;
731     /**
732         The node’s translation along the x, y, and z axes.
733         Length must be 0 or 3.
734     */
735     double[] translation;
736     /**
737         A floating-point 4x4 transformation matrix stored in column-major order.
738         Length must be 0 or 16.
739     */
740     double[] matrix;
741     /**
742         The weights of the instantiated morph target.
743         The number of array elements MUST match the number of morph targets of the referenced mesh.
744         When defined, mesh MUST also be defined.
745     */
746     double[] weights;
747 
748     this(int camera = -1, int skin = -1, int mesh = -1) {
749         this.camera = camera;
750         this.skin = skin;
751         this.mesh = mesh;
752     }
753 }
754 
755 /**
756     A buffer points to binary geometry, animation, or skins.
757 */
758 class Buffer {
759     /// The name of the buffer
760     string name;
761     /**
762        The raw data in ubytes.
763        This is decoded from the URI.
764     */
765     ubyte[] data;
766     /// The length of the buffer in bytes.
767     int byteLength = -1;
768 
769     this() {}
770 }
771 
772 /**
773     Metadata about the glTF asset.
774 */
775 class Asset {
776     /// REQUIRED. The glTF version in the form of <major>.<minor> that this asset targets.
777     string version_ = "2.0";
778     /// Tool that generated this glTF model. Useful for debugging.
779     string generator;
780     /**
781         The minimum glTF version in the form of <major>.<minor> that this asset targets.
782         This property MUST NOT be greater than the asset version.
783     */
784     string minVersion;
785     /// A copyright message suitable for display to credit the content creator.
786     string copyright;
787 
788     this() {}
789 }
790 
791 /**
792     Model is the container used to store all the decoded JSON data.
793     It loads all the data automatically through it's methods.
794 */
795 class Model {
796     /// Accessors in the model.
797     Accessor[] accessors;
798     /// Animations in the model.
799     Animation[] animations;
800     /// Buffers in the model.
801     Buffer[] buffers;
802     /// BufferViews in the model.
803     BufferView[] bufferViews;
804     /// Meshes in the model.
805     Mesh[] meshes;
806     /// Nodes in the model.
807     Node[] nodes;
808     /// Skins in the model.
809     Skin[] skins;
810     /// Samplers in the model.
811     Sampler[] samplers;
812 
813     /// The asset info of the model.
814     Asset asset;
815 
816     // Takes in a raw string so you can do whatever they want with your file location.
817     this(string fileLocation, bool debugInfo = true) {
818         //* Model can work with it's internal fields so we don't have to chain them
819         this.debugInfo = debugInfo;
820         this.fileLocation = fileLocation;
821         this.asset = new Asset();
822         if (debugInfo) {
823             writeln("\nMODEL " ~ fileLocation ~ " INITIALIZED\n");
824         }
825     }
826     
827     /**
828         Automatically loads, decodes, and stores all the implemented JSON data into the model's arrays.
829         Returns loading success.
830     */
831     bool loadFile(){
832         if (!this.fileExists()) {
833             writeDebug(
834                 "I'm very sorry, but the file:\n" ~
835                 this.fileLocation ~ "\n" ~
836                 "does not exist on the drive. Perhaps you are polling the wrong directory?\n"
837             );
838             return false;
839         }
840         
841         // Turn the raw disk data into a usable JSON object with the std.json library.
842         // Can throw an exception, which we catch and return as false.
843         // Going to be extra nice and throw in a link to the khronos verifier straight in the terminal.
844         if (!this.loadJson()) {
845             writeDebug(
846                 "I'm very sorry, but the file:\n"~
847                 this.fileLocation ~ "\n" ~
848                 "appears to be corrupted, please double-check this model with the Khronos GLTF validator.\n" ~
849                 "Link: https://github.khronos.org/glTF-Validator/\n"
850             );
851             return false;
852         }
853 
854         // Now it has to iterate the JSON object and store the data.
855         this.collectJSONInfo();
856 
857         return true;
858     }
859 
860 private:
861 
862     string fileLocation;
863     bool debugInfo = false;
864     JSONValue jsonData;
865 
866     void collectJSONInfo() {
867         // This might look a bit complicated, but we're just iterating the tree of data
868         // We start off with a set of keys and values, accessor, bufferViews, etc
869         // Then we need to go down them because they're packed pretty tight
870         foreach (key,value; this.jsonData.objectNoRef) {
871 
872             //! Don't remove this until everything is accounted for
873             // writeln(key);
874 
875             //TODO: surround this with try catch, return false on failure along with debug info on which one failed
876 
877             // Key's could be corrupted, so we need a default catch all
878             //* key is a string, value is a JSONValue object
879             switch (key) {
880                 case "accessors": {
881                     this.grabAccessorsData(value);
882                     break;
883                 }
884                 case "bufferViews": {
885                     this.grabBufferViewsData(value);
886                     break;
887                 }
888                 case "buffers": {
889                     this.grabBuffersData(value);
890                     break;
891                 }
892                 case "meshes": {
893                     this.grabMeshesData(value);
894                     break;
895                 }
896                 case "nodes": {
897                     this.grabNodesData(value);
898                     break;
899                 }
900                 case "asset": {
901                     this.grabAssetData(value);
902                     break;
903                 }
904                 case "skins": {
905                     this.grabSkinsData(value);
906                     break;
907                 }
908                 case "animations": {
909                     this.grabAnimationsData(value);
910                     break;
911                 } 
912                 default: // Unknown
913             }
914         }
915     }
916     
917     void grabAnimationsData(JSONValue jsonObject) {
918 
919         //* Implementation Note: We are accessing into each animation, like "run", "jump", etc in blender.
920         //* This is why each pass creates a new animation object in the array.
921 
922         //* This is explicit to help code-d and to be more readable for control flow
923         //* Key is integer(size_t), value is JSON value
924         foreach (size_t key, JSONValue value; jsonObject.array) {
925 
926             // We are assembling this animation
927             Animation animationObject = new Animation();
928 
929             // Now parse the string
930             
931             //* Key is string, value is JSON value
932             foreach (string arrayKey, JSONValue arrayValue; value.object) {
933                 switch (arrayKey) {
934                     // AnimationSampler[]
935                     case "samplers": {
936                         assert(arrayValue.type == JSONType.array);
937                         this.grabAnimationSamplers(animationObject, arrayValue);
938                         break;
939                     }
940                     // AnimationChannel[]
941                     case "channels": {
942                         assert(arrayValue.type == JSONType.array);
943                         this.grabAnimationChannels(animationObject, arrayValue);
944                         break;
945                     }
946                     // String
947                     case "name": {
948                         assert(arrayValue.type == JSONType..string);
949                         animationObject.name = arrayValue.str;
950                         break;
951                     }
952                     default: // Unknown
953                 }
954             }
955 
956             this.animations ~= animationObject;
957         }
958     }
959 
960     void grabAnimationChannels(Animation animationObject, JSONValue jsonObject) {
961 
962         //* This is explicit to help code-d and to be more readable for control flow
963         //* Key is integer(size_t), value is JSON value
964         foreach (size_t key, JSONValue value; jsonObject.array) {
965             
966             // We are assembling this animation channel
967             AnimationChannel animationChannelObject = new AnimationChannel();
968 
969             // Now parse the string
970 
971             //* Key is string, value is JSON value
972             foreach (string arrayKey, JSONValue arrayValue; value.object) {
973                 switch (arrayKey) {
974                     // Integer
975                     case "sampler": {
976                         assert(arrayValue.type == JSONType.integer);
977                         animationChannelObject.sampler = cast(int)arrayValue.integer;
978                         break;
979                     }
980                     // JSON Value
981                     case "target": {
982                         assert(arrayValue.type == JSONType.object);
983                         this.grabAnimationChannelTarget(animationChannelObject, arrayValue);
984                         break;
985                     }
986                     default:
987                 }
988             }
989 
990             animationObject.channels ~= animationChannelObject;
991         }
992     }
993 
994     void grabAnimationChannelTarget(AnimationChannel animationChannelObject, JSONValue jsonObject) {
995         //* This is explicit to help code-d and to be more readable for control flow
996         //* Key is string, value is JSON value
997         foreach (string key, JSONValue value; jsonObject.object) {
998             switch (key) {
999                 // Integer
1000                 case "node": {
1001                     assert(value.type == JSONType.integer);
1002                     animationChannelObject.target_node = cast(int)value.integer;
1003                     break;
1004                 }
1005                 // String
1006                 case "path": {
1007                     assert(value.type == JSONType..string);
1008                     animationChannelObject.target_path = value.str;
1009                     break;
1010                 }
1011                 default: // Unknown
1012             }
1013         }
1014     }
1015 
1016     void grabAnimationSamplers(Animation animationObject, JSONValue jsonObject) {
1017 
1018         //* This is explicit to help code-d and to be more readable for control flow
1019         //* Key is integer(size_t), value is JSON value
1020         foreach (size_t key, JSONValue value; jsonObject.array) {
1021 
1022             // We are assembling this animation sampler
1023             AnimationSampler animationSamplerObject = new AnimationSampler();
1024 
1025             // Now parse the string
1026 
1027             //* Key is string, value is JSON value
1028             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1029                 switch (arrayKey) {
1030                     // Integer
1031                     case "input": {
1032                         assert(arrayValue.type == JSONType.integer);
1033                         animationSamplerObject.input = cast(int)arrayValue.integer;
1034                         break;
1035                     }
1036                     // String
1037                     case "interpolation": {
1038                         assert(arrayValue.type == JSONType..string);
1039                         animationSamplerObject.interpolation = arrayValue.str;
1040                         break;                        
1041                     }
1042                     // Integer
1043                     case "output": {
1044                         assert(arrayValue.type == JSONType.integer);
1045                         animationSamplerObject.output = cast(int)arrayValue.integer;
1046                         break;
1047                     }
1048                     default: // Unknown
1049                 }
1050             }
1051 
1052             animationObject.samplers ~= animationSamplerObject;
1053         }
1054     }
1055 
1056     void grabSkinsData(JSONValue jsonObject) {
1057         
1058         //* This is explicit to help code-d and to be more readable for control flow
1059         //* Key is integer(size_t), value is JSON value
1060         foreach (size_t key, JSONValue value; jsonObject.array) {
1061 
1062             // We are assembling this skin
1063             Skin skinObject = new Skin();
1064 
1065             // Now parse the string
1066 
1067             //* Key is string, value is JSON value
1068             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1069                 switch (arrayKey) {
1070                     // String
1071                     case "name": {
1072                         assert(arrayValue.type == JSONType..string);
1073                         skinObject.name = arrayValue.str;
1074                         break;
1075                     }
1076                     // Integer[]
1077                     case "joints": {
1078                         assert(arrayValue.type == JSONType.array);
1079                         foreach(size_t k, JSONValue v; arrayValue.array){
1080                             assert(v.type == JSONType.integer);
1081                             skinObject.joints ~= cast(int)v.integer;
1082                         }
1083                         break;
1084                     }
1085                     // Integer - Points to Accessor
1086                     case "inverseBindMatrices": {
1087                         assert(arrayValue.type == JSONType.integer);
1088                         skinObject.inverseBindMatrices = cast(int)arrayValue.integer;
1089                         break;
1090                     }
1091                     // Integer - Points to root node (root joint)
1092                     case "skeleton": {
1093                         assert(arrayValue.type == JSONType.integer);
1094                         skinObject.skeleton = cast(int)arrayValue.integer;
1095                         break;
1096                     }
1097                     default: // Unknown
1098                 }
1099             }
1100 
1101             this.skins ~= skinObject;
1102         }
1103     }
1104 
1105     void grabNodesData(JSONValue jsonObject) {
1106 
1107         //* This is explicit to help code-d and to be more readable for control flow
1108         //* Key is integer(size_t), value is JSON value
1109         foreach (size_t key, JSONValue value; jsonObject.array) {
1110 
1111             // We are assembling this node
1112             Node nodeObject = new Node();
1113 
1114             // Now parse the string
1115 
1116             //* Key is string, value is JSON value
1117             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1118                 switch (arrayKey) {
1119                     // Integer
1120                     case "camera": {
1121                         assert(arrayValue.type == JSONType.integer);
1122                         nodeObject.camera = cast(int)arrayValue.integer;
1123                         break;
1124                     }
1125                     // Integer[]
1126                     case "children": {
1127                         assert(arrayValue.type == JSONType.array);
1128                         foreach(size_t k, JSONValue v; arrayValue.array){
1129                             assert(v.type == JSONType.integer);
1130                             nodeObject.children ~= cast(int)v.integer;
1131                         }
1132                         break;
1133                     }
1134                     // Integer
1135                     case "skin": {
1136                         assert(arrayValue.type == JSONType.integer);
1137                         nodeObject.skin = cast(int)arrayValue.integer;
1138                         break;
1139                     }
1140                     // Double[16] (matrix4)
1141                     case "matrix": {
1142                         assert(arrayValue.type == JSONType.array);
1143                         foreach(size_t k, JSONValue v; arrayValue.array){
1144                             nodeObject.matrix ~= grabDouble(v);
1145                         }
1146                         break;
1147                     }
1148                     // Integer
1149                     case "mesh": {
1150                         assert(arrayValue.type == JSONType.integer);
1151                         nodeObject.mesh = cast(int)arrayValue.integer;
1152                         break;
1153                     }
1154                     // Double[4] (quaternion)
1155                     case "rotation": {
1156                         assert(arrayValue.type == JSONType.array);
1157                         foreach(size_t k, JSONValue v; arrayValue.array){
1158                             nodeObject.rotation ~= this.grabDouble(v);
1159                         }
1160                         break;
1161                     }
1162                     // Double[3] (vector3)
1163                     case "scale": {
1164                         assert(arrayValue.type == JSONType.array);
1165                         foreach(size_t k, JSONValue v; arrayValue.array){
1166                             nodeObject.scale ~= this.grabDouble(v);
1167                         }
1168                         break;
1169                     }
1170                     // Double[3] (vector3)
1171                     case "translation": {
1172                         assert(arrayValue.type == JSONType.array);
1173                         foreach(size_t k, JSONValue v; arrayValue.array){
1174                             nodeObject.translation ~= this.grabDouble(v);
1175                         }
1176                         break;
1177                     }
1178                     // Double[]
1179                     case "weights": {
1180                         assert(arrayValue.type == JSONType.array);
1181                         foreach(size_t k, JSONValue v; arrayValue.array){
1182                             nodeObject.weights ~= this.grabDouble(v);
1183                         }
1184                         break;
1185                     }
1186                     // String
1187                     case "name": {
1188                         assert(arrayValue.type == JSONType..string);
1189                         nodeObject.name = arrayValue.str;
1190                         break;
1191                     }
1192                     default: // Unknown
1193 
1194                 }
1195             }
1196 
1197             this.nodes ~= nodeObject;
1198         }
1199     }
1200 
1201     void grabMeshesData(JSONValue jsonObject) {
1202 
1203         //* This is explicit to help code-d and to be more readable for control flow
1204         //* Key is integer(size_t), value is JSON value
1205         foreach (size_t key, JSONValue value; jsonObject.array) {
1206 
1207             //* Implementation note: Meshes are a special type.
1208             //* They contain a vector of Primitive objects.
1209  
1210             // We are assembling this mesh
1211             Mesh meshObject = new Mesh();
1212 
1213             // Now parse the string
1214 
1215             //* Key is string, value is JSON value
1216             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1217                 switch (arrayKey) {
1218                     // Json Object
1219                     case "primitives": {
1220                         assert(arrayValue.type == JSONType.array);
1221                         // Goes to a primitive assembler because it's complex.
1222                         // This returns an array of primitives, automatically assigns it.
1223                         meshObject.primitives = this.grabPrimitiveData(arrayValue);
1224                         break;
1225                     }
1226                     // Double[]
1227                     case "weights": {
1228                         assert(arrayValue.type == JSONType.array);
1229                         foreach(size_t k, JSONValue v; arrayValue.array){
1230                             meshObject.weights ~= this.grabDouble(v);
1231                         }
1232                         break;
1233                     }
1234                     // String
1235                     case "name": {
1236                         assert(arrayValue.type == JSONType..string);
1237                         meshObject.name = arrayValue.str;
1238                         break;
1239                     }
1240                     default:
1241                 }
1242             }
1243             this.meshes ~= meshObject;
1244         }
1245     }
1246     
1247     Primitive[] grabPrimitiveData(JSONValue jsonObject) {
1248 
1249         // This is assembling an array of primitives
1250         Primitive[] returningPrimitives;
1251 
1252         //* This is explicit to help code-d and to be more readable for control flow
1253         //* Key is integer(size_t), value is JSON value
1254         foreach (size_t key, JSONValue value; jsonObject.array) {
1255 
1256             // We are assembling this primitive
1257             Primitive primitiveObject = new Primitive();
1258 
1259             // Now parse the string
1260 
1261             //* Key is string, value is JSON value
1262             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1263                 switch (arrayKey) {
1264                     // Integer[String] Associative Array
1265                     case "attributes": {
1266                         assert(arrayValue.type == JSONType.object);
1267                         foreach (string attributeKey, JSONValue attributeValue; arrayValue) {
1268                             assert(attributeValue.type == JSONType.integer);
1269                             primitiveObject.attributes[attributeKey] = cast(int)attributeValue.integer;
1270                         }
1271                         break;
1272                     }
1273                     // Integer
1274                     case "indices": {
1275                         assert(arrayValue.type == JSONType.integer);
1276                         primitiveObject.indices = cast(int)arrayValue.integer;
1277                         break;
1278                     }
1279                     // Integer
1280                     case "material": {
1281                         assert(arrayValue.type == JSONType.integer);
1282                         primitiveObject.material = cast(int)arrayValue.integer;
1283                         break;
1284                     }
1285                     // Integer
1286                     case "mode": {
1287                         assert(arrayValue.type == JSONType.integer);
1288                         primitiveObject.mode = cast(int)arrayValue.integer;
1289                         break;
1290                     }
1291                     // Integer[]
1292                     case "targets": {
1293                         // TODO
1294                         break;
1295                     }
1296                     default: // Unknown
1297                 }
1298             }
1299             returningPrimitives ~= primitiveObject;
1300         }
1301         return returningPrimitives;
1302     }
1303 
1304     void grabBuffersData(JSONValue jsonObject) {
1305 
1306         //* This is explicit to help code-d and to be more readable for control flow
1307         //* Key is integer(size_t), value is JSON value
1308         foreach (size_t key, JSONValue value; jsonObject.array) {
1309             
1310             // We are assembling this buffer
1311             Buffer bufferObject = new Buffer();
1312 
1313             // Now parse the string
1314 
1315             //* Key is string, value is JSON value
1316             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1317                 switch (arrayKey) {
1318                     // String - REQUIRED to be a string of data
1319                     case "uri": {
1320                         assert(arrayValue.type == JSONType..string);
1321                         // Needs to strip out this header info
1322                         string data = arrayValue.str.replace("data:application/octet-stream;base64,", "");
1323                         // If it's a bin, fail state
1324                         assert(data.length != arrayValue.str.length);
1325                         // Now decode it
1326                         bufferObject.data = Base64.decode(data);
1327                         break;
1328                     }
1329                     // Integer
1330                     case "byteLength": {
1331                         assert(arrayValue.type == JSONType.integer);
1332                         bufferObject.byteLength = cast(int)arrayValue.integer;
1333                         break;
1334                     }
1335                     // String
1336                     case "name": {
1337                         assert(arrayValue.type == JSONType..string);
1338                         bufferObject.name = arrayValue.str;
1339                         break;
1340                     }
1341                     default: // Unknown
1342                 }
1343             }
1344             this.buffers ~= bufferObject;
1345         }
1346     }
1347 
1348     void grabBufferViewsData(JSONValue jsonObject) {
1349 
1350         //* This is explicit to help code-d and to be more readable for control flow
1351         //* Key is integer(size_t), value is JSON value
1352         foreach (size_t key, JSONValue value; jsonObject.array) {
1353 
1354             // We are assembling this bufferView
1355             BufferView bufferViewObject = new BufferView();
1356 
1357             // Now parse the string
1358 
1359             //* Key is string, value is JSON value
1360             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1361                 switch (arrayKey) {
1362                     // Integer
1363                     case "byteOffset": {
1364                         assert(arrayValue.type == JSONType.integer);
1365                         bufferViewObject.byteOffset = cast(int)arrayValue.integer;
1366                         break;
1367                     }
1368                     // Integer, alias to TINYGLTF_TARGET_
1369                     case "target": {
1370                         assert(arrayValue.type == JSONType.integer);
1371                         bufferViewObject.target = cast(int)arrayValue.integer;
1372                         break;
1373                     }
1374                     // Integer
1375                     case "buffer": {
1376                         assert(arrayValue.type == JSONType.integer);
1377                         bufferViewObject.buffer = cast(int)arrayValue.integer;
1378                         break;
1379                     }
1380                     // Integer
1381                     case "byteLength": {
1382                         assert(arrayValue.type == JSONType.integer);
1383                         bufferViewObject.byteLength = cast(int)arrayValue.integer;
1384                         break;
1385                     }
1386                     // String
1387                     case "name": {
1388                         assert(arrayValue.type == JSONType..string);
1389                         bufferViewObject.name = arrayValue.str;
1390                         break;
1391                     }
1392                     default: // Unknown
1393                 }
1394             }
1395             this.bufferViews ~= bufferViewObject;
1396         }
1397     }
1398     
1399     void grabAccessorsData(JSONValue jsonObject) {
1400         
1401         //* This is explicit to help code-d and to be more readable for control flow
1402         //* Key is integer(size_t), value is JSON value
1403         foreach (size_t key, JSONValue value; jsonObject.array) {
1404 
1405             // We are assembling this accessor
1406             Accessor accessorObject = new Accessor();
1407             
1408             // Now parse the string
1409 
1410             //* Key is string, value is JSON value
1411             foreach (string arrayKey, JSONValue arrayValue; value.object) {
1412                 
1413                 switch (arrayKey) {
1414                     // Integer
1415                     case "bufferView": {
1416                         assert(arrayValue.type() == JSONType.integer);
1417                         accessorObject.bufferView = cast(int)arrayValue.integer;
1418                         break;
1419                     }
1420                     // Integer
1421                     case "byteOffset": {
1422                         assert(arrayValue.type() == JSONType.integer);
1423                         accessorObject.byteOffset = cast(int)arrayValue.integer;
1424                         break;
1425 
1426                     }
1427                     // Integer, alias to TINYGLTF_COMPONENT_TYPE_
1428                     case "componentType": {
1429                         assert(arrayValue.type() == JSONType.integer);
1430                         accessorObject.componentType = cast(int)arrayValue.integer;
1431                         break;
1432                     }
1433                     // Integer
1434                     case "count": {
1435                         assert(arrayValue.type() == JSONType.integer);
1436                         accessorObject.count = cast(int)arrayValue.integer;
1437                         break;
1438                     }
1439                     // Double[]
1440                     case "min": {
1441                         assert(arrayValue.type() == JSONType.array);
1442                         foreach (k,JSONValue v; arrayValue.array) {
1443                             accessorObject.minValues ~= this.grabDouble(v);
1444                         }
1445                         break;
1446                     }
1447                     // Double[]
1448                     case "max": {
1449                         assert(arrayValue.type() == JSONType.array);
1450                         foreach (k,JSONValue v; arrayValue.array) {
1451                             accessorObject.maxValues ~= this.grabDouble(v);
1452                         }
1453                         break;
1454                     }
1455                     // String
1456                     case "type": {
1457                         assert(arrayValue.type == JSONType..string);
1458                         // Assign the integral value of the enum
1459                         switch (arrayValue.str) {
1460                             case "VEC2": {
1461                                 accessorObject.type = TINYGLTF_TYPE_VEC2;
1462                                 break;
1463                             }
1464                             case "VEC3": {
1465                                 accessorObject.type = TINYGLTF_TYPE_VEC3;
1466                                 break;
1467                             }
1468                             case "VEC4": {
1469                                 accessorObject.type = TINYGLTF_TYPE_VEC4;
1470                                 break;
1471                             }
1472                             case "MAT2": {
1473                                 accessorObject.type = TINYGLTF_TYPE_MAT2;
1474                                 break;
1475                             }
1476                             case "MAT3": {
1477                                 accessorObject.type = TINYGLTF_TYPE_MAT3;
1478                                 break;
1479                             }
1480                             case "MAT4": {
1481                                 accessorObject.type = TINYGLTF_TYPE_MAT4;
1482                                 break;
1483                             }
1484                             case "SCALAR": {
1485                                 accessorObject.type = TINYGLTF_TYPE_SCALAR;
1486                                 break;
1487                             }
1488                             case "VECTOR": {
1489                                 accessorObject.type = TINYGLTF_TYPE_VECTOR;
1490                                 break;
1491                             }
1492                             case "MATRIX": {
1493                                 accessorObject.type = TINYGLTF_TYPE_MATRIX;
1494                                 break;
1495                             }
1496                             default: // Unknown
1497                         }
1498                         break;
1499                     }
1500                     case "name": {
1501                         assert(arrayValue.type == JSONType..string);
1502                         accessorObject.name = arrayValue.str;
1503                         break;
1504                     }
1505                     default: // UNKNOWN
1506                 }
1507             }
1508 
1509             // Finally dump the accessor in
1510             this.accessors ~= accessorObject;
1511         }
1512     }
1513 
1514     void grabAssetData(JSONValue jsonObject) {
1515 
1516         //* This is explicit to help code-d and to be more readable for control flow
1517 
1518         //* Implementation note: There is only one asset so this looks a bit different    
1519 
1520         // We are assembling this asset
1521         Asset assetObject = new Asset();
1522 
1523         // Now parse the string
1524 
1525         //* Key is string, value is JSON value
1526         foreach (string arrayKey, JSONValue arrayValue; jsonObject.object) {
1527             switch (arrayKey) {
1528                 case "copyright": {
1529                     assert(arrayValue.type == JSONType..string);
1530                     assetObject.copyright = arrayValue.str;
1531                     break;
1532                 }
1533                 case "generator": {
1534                     assert(arrayValue.type == JSONType..string);
1535                     assetObject.generator = arrayValue.str;
1536                     break;
1537                 }
1538                 case "version": {
1539                     assert(arrayValue.type == JSONType..string);
1540                     assetObject.version_ = arrayValue.str;
1541                     break;
1542                 }
1543                 case "minVersion": {
1544                     assert(arrayValue.type == JSONType..string);
1545                     assetObject.minVersion = arrayValue.str;
1546                     break;
1547                 }
1548                 default: // Unknown
1549             }
1550         }
1551         this.asset = assetObject;    
1552     }
1553 
1554     //* This is just a passthrough to keep it looking neat :)
1555     bool fileExists() {
1556         return exists(this.fileLocation);
1557     }
1558 
1559     // Returns parsing the JSON success;
1560     bool loadJson() {
1561         void[] rawData;
1562         string jsonString;
1563         
1564         try {
1565             rawData = read(this.fileLocation);
1566         } catch (Exception e) {
1567             return false;
1568         }
1569 
1570         try {
1571             jsonString = cast(string)rawData;
1572         } catch (Exception e) {
1573             return false;
1574         }
1575 
1576         try {
1577             this.jsonData = parseJSON(jsonString);
1578         } catch (Exception e) {
1579             return false;
1580         }
1581 
1582         return true;
1583     }
1584 
1585     // std.json thinks that 1.0 and 0.0 is integer so we have to work with it
1586     static double grabDouble(JSONValue input) {
1587         if (input.type == JSONType.float_) {
1588             return input.floating;
1589         } else if (input.type == JSONType.integer) {
1590             return cast(double)input.integer;
1591         }
1592         // Something went HORRIBLY wrong with the model.
1593         throw new Exception("THIS MODEL HAS A DIFFERENT TYPE THAT'S NOT INTEGRAL AS A DOUBLE!");
1594     }
1595 
1596 
1597     //*===================== DEBUGGING TOOLS ============================
1598     void writeDebugHeader() {
1599         writeln(
1600         "=========================\n" ~
1601         "DEBUG INFO\n" ~
1602         "=========================\n"
1603         );
1604     }
1605     void writeDebug(string input) {
1606         if (!this.debugInfo) {
1607             return;
1608         }
1609         writeDebugHeader();
1610         writeln(input);
1611         writeDebugFooter();
1612     }
1613     void writeDebugFooter() {
1614         writeln("=========================\n");
1615     }
1616 }
1617 
1618 
1619 unittest {
1620     // Test fail state and disabling debug info
1621     Model failedModel = new Model("This is a failure test.", false);
1622     assert(failedModel !is null);
1623     assert(failedModel.loadFile() == false);
1624 
1625     writeln("\nFAILURE PASS!\n");
1626 
1627     // Now test loading state again with a known model
1628     Model successModel = new Model("models/cube_embedded/cube.gltf");
1629     assert(successModel !is null);
1630     assert(successModel.loadFile() == true);
1631     
1632     writeln("\nSUCCESS PASS\n");
1633 
1634     // Now test a corrupted model.
1635     Model corruptedModel = new Model("models/missing_brace/json_missing_brace.gltf");
1636     assert(corruptedModel.loadFile() == false);
1637 
1638     writeln("\nCORRUPTED PASS\n");
1639 
1640     // Now bring Minetest Sam onboard.
1641     Model sam = new Model("models/sam/sam.gltf");
1642     assert(sam.loadFile() == true);
1643 
1644     writeln("\nSAM PASSED :)\n");
1645     
1646 }