1 module glTF2Loader;
2 
3 import std.stdio: writeln;
4 import std.conv: to;
5 import std.exception: enforce;
6 import std.file: read;
7 import std.path: dirName;
8 import std.json;
9 import std.base64: Base64;
10 import std.bitmanip: peek, Endian;
11 import std.algorithm.searching: findSplit, findSplitAfter;
12 
13 alias Attribute = int[string];
14 
15 const string[3] attributeNames = ["POSITION", "NORMAL", "TANGENT"];
16 const string[7] typeNames = ["SCALAR", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", "MAT4"];
17 const string[3] alphaModes = ["OPAQUE", "MASK", "BLEND"];
18 const string[4] interpolationModes = ["LINEAR", "STEP", "CUBICSPLINE"];
19 const string[4] targetTypes = ["translation", "rotation", "scale", "weights"];
20 
21 private float getFloatValue (JSONValue value) {
22 	float result;
23 	if (value.type == JSONType.float_)
24 		result = value.floating;
25 	else
26 		result = cast (float) value.integer;
27 	
28 	return result;
29 }
30 
31 struct glTF2Metadata {
32     string copyright;
33     string generator;
34     string currVersion;
35     string minVersion;
36 }
37 
38 struct glTF2Scene {
39     string name;
40     uint[] nodes;
41 };
42 
43 struct glTF2Primitive {
44     int indices = -1;
45     int material = -1;
46 
47     enum Mode : ubyte {
48         Points = 0,
49         Lines = 1,
50         LineLoop = 2,
51         LineStrip = 3,
52         Triangles = 4,
53         TriangleStrip = 5,
54         TriangleFan = 6
55     }
56 	Mode mode = Mode.Triangles;
57 
58     Attribute attributes;
59     Attribute[] targets;
60 };
61 
62 struct glTF2Mesh {
63     string name;
64 
65     float[] weights;
66     glTF2Primitive[] primitives;
67 };
68 
69 struct glTF2Buffer {
70     string name;
71 
72     string uri;
73     ulong byteLength = 0;
74 
75     void[] data = null;
76 };
77 
78 struct glTF2BufferView {
79     string name;
80 
81     uint buffer; 
82     ulong byteOffset = 0;
83     ulong byteLength = 0;
84     uint byteStride = 0;
85 
86     enum TargetType : ushort {
87         None = 0,
88         ArrayBuffer = 34962,
89         ElementArrayBuffer = 34963
90     } 
91 	TargetType target;
92 };
93 
94 struct glTF2Texture {
95     string name;
96 
97     int sampler = -1;
98     int source = -1;
99 };
100 
101 struct glTF2Sampler {
102     string name;
103 
104     enum MagFilter : ushort {
105         None = 0,
106         Nearest = 9728,
107         Linear = 9729
108     }
109 	MagFilter magFilter = MagFilter.None;
110 
111     enum MinFilter : ushort {
112         None = 0,
113         Nearest = 9728,
114         Linear = 9729,
115         NearestMipMapNearest = 9984,
116         LinearMipMapNearest = 9985,
117         NearestMipMapLinear = 9986,
118         LinearMipMapLinear = 9987
119     } 
120 	MinFilter minFilter = MinFilter.None;
121 
122     enum WrappingMode : ushort {
123         ClampToEdge = 33071,
124         MirroredRepeat = 33648,
125         Repeat = 10497
126     };
127 
128     WrappingMode wrapS = WrappingMode.Repeat;
129     WrappingMode wrapT = WrappingMode.Repeat;
130 };
131 
132 struct glTF2Image {
133     string name;
134     string uri;
135     string mimeType;
136 	
137     int bufferView = -1;
138 
139 	void[] data = null;
140 };
141 
142 struct glTF2Material {
143     string name;
144 
145     struct Texture {
146         int index = -1;
147         int texCoord = 0;
148     };
149 
150     struct PbrMetallicRoughness {
151         float[4] baseColorFactor = [1.0f, 1.0f, 1.0f, 1.0f];
152         Texture baseColorTexture;
153 
154         float metallicFactor = 1.0f;
155         float roughnessFactor = 1.0f;
156         Texture metallicRoughnessTexture;
157     } 
158 	PbrMetallicRoughness pbrMetallicRoughness;
159 
160     struct NormalTexture {
161         int index = -1;
162         ulong texCoord = 0;
163         float scale = 1.0f;
164     } 
165 	NormalTexture normalTexture;
166 
167     struct OcclusionTexture {
168         int index = -1;
169         ulong texCoord = 0;
170         float strength = 1.0f;
171     } 
172 	OcclusionTexture occlusionTexture;
173 
174     Texture emissiveTexture;
175 
176     float[3] emissiveFactor = [0.0f, 0.0f, 0.0f];
177 
178     enum AlphaMode : ubyte {
179         Opaque,
180         Mask,
181         Blend
182     } 
183 	AlphaMode alphaMode = AlphaMode.Opaque;
184 
185     float alphaCutoff = 0.5f;
186     bool doubleSided = false;
187 };
188 
189 struct glTF2Camera {
190 	enum CameraType : ubyte {
191 		Perspective,
192 		Orthographic
193 	}
194 	CameraType cameraType;
195 	
196 	float znear;
197 	float zfar = -1;
198 	
199 	float aspectRatio;
200 	float yfov;
201 	
202 	float xmag;
203 	float ymag;
204 }
205 
206 struct glTF2Node {
207     string name;
208 
209     int camera = -1;
210     int mesh = -1;
211     int skin = -1;
212 
213     int[] children;
214 
215     float[16] matrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
216     float[4] rotation = [0, 0, 0, 1];
217     float[3] scale = [1, 1, 1];
218     float[3] translation = [0, 0, 0];
219 
220     float[] weights;
221 };
222 
223 struct glTF2Accessor {
224     string name;
225 
226     int bufferView = -1;
227     ulong byteOffset = 0;
228     ulong count;
229     bool normalized = false;
230 
231     enum ComponentType : ushort {
232         Byte = 5120,
233         UnsignedByte = 5121,
234         Short = 5122,
235         UnsignedShort = 5123,
236         UnsignedInt = 5125,
237         Float = 5126
238     } 
239 	ComponentType componentType;
240 
241     enum Type : ubyte {
242         Scalar,
243         Vec2,
244         Vec3,
245         Vec4,
246         Mat2,
247         Mat3,
248         Mat4
249     } 
250 	Type type;
251 	
252 	union minMaxArray {
253 		int[] i;
254 		float[] f;
255 	}
256 	minMaxArray min;
257 	minMaxArray max;
258 	
259 	struct Indices {
260 		uint bufferView;
261 		ComponentType componentType;
262 		uint byteOffset = 0;
263 	}
264 	
265 	struct Values {
266 		uint bufferView;
267 		uint byteOffset = 0;
268 	}
269 	
270 	struct Sparse {
271 		uint count;
272 		Indices indices;
273 		Values values;
274 	}
275 	Sparse sparse;
276 };
277 
278 struct glTF2Skin {
279 	uint inverseBindMatrices;
280 	uint[] joints;
281 	uint skeleton;
282 }
283 
284 struct glTF2Animation {
285 	string name;
286 
287 	enum Interpolation : ubyte {
288 		Linear,
289 		Step,
290 		CubicSpline
291 	}
292 	struct Sampler {
293 		uint input;
294 		uint output;
295 		Interpolation interpolation = Interpolation.Linear;
296 	};
297 	Sampler[] samplers;
298 	
299 	enum Path : ubyte {
300 		Translation,
301 		Rotation,
302 		Scale,
303 		Weights
304 	}
305 	struct Target {
306 		uint node;
307 		Path path;
308 	}
309 	struct Channel {
310 		uint sampler;
311 		Target target;
312 	}
313 	Channel[] channels;
314 }
315 
316 struct glTF2Asset {
317 	glTF2Metadata metadata;
318 
319     glTF2Accessor[] accessors;
320     glTF2Animation[] animations;
321     glTF2Buffer[] buffers;
322     glTF2BufferView[] bufferViews;
323     glTF2Camera[] cameras;
324     glTF2Image[] images;
325     glTF2Material[] materials;
326     glTF2Mesh[] meshes;
327     glTF2Node[] nodes;
328     glTF2Sampler[] samplers;
329     int scene = -1;
330     glTF2Scene[] scenes;
331     glTF2Skin[] skins;
332     glTF2Texture[] textures;
333 
334 	JSONValue json;
335 
336 	string fileName, directoryName;
337 
338 	private void loadMetadata () {
339 		metadata.currVersion = json["asset"]["version"].str;
340 		if ("minVersion" in json["asset"])
341 			metadata.minVersion = json["asset"]["minVersion"].str;
342 		if ("generator" in json["asset"])
343 			metadata.generator = json["asset"]["generator"].str;
344 		if ("copyright" in json["asset"])
345 			metadata.copyright = json["asset"]["copyright"].str;
346 	}
347 
348 	private void loadScenes () {
349 		if ("scenes" !in json)
350 			return;
351 		scenes.length = json["scenes"].array.length;
352 		
353 		if ("scene" in json)
354 			scene = cast (int) json["scene"].integer;
355 		foreach (i, scene; json["scenes"].array) {
356 			if ("name" in scene)
357 				scenes[i].name = scene["name"].str;
358 			if ("nodes" in scene) {
359 				scenes[i].nodes.length = scene["nodes"].array.length;
360 				foreach (j, node; scene["nodes"].array) {
361 					scenes[i].nodes[j] = cast (int) node.integer;
362 				}
363 			}
364 		}
365 	}
366 
367 	private void loadMeshes () {
368 		if ("meshes" !in json)
369 			return;
370 		meshes.length = json["meshes"].array.length;
371 		
372 		foreach (i, mesh; json["meshes"].array) {
373 			if ("name" in mesh)
374 				meshes[i].name = mesh["name"].str;
375 			meshes[i].primitives.length = mesh["primitives"].array.length;
376 			foreach (j, primitive; mesh["primitives"].array) {
377 				if ("indices" in primitive)
378 					meshes[i].primitives[j].indices = cast (int) primitive["indices"].integer;
379 				if ("material" in primitive)
380 					meshes[i].primitives[j].material = cast (int) primitive["material"].integer;
381 				if ("mode" in primitive)
382 					meshes[i].primitives[j].mode = cast (glTF2Primitive.Mode) primitive["mode"].integer;
383 				if ("targets" in primitive) {
384 					meshes[i].primitives[j].targets.length = primitive["targets"].array.length;
385 					foreach (k, target; primitive["targets"].array) {
386 						foreach (l, attributeName; attributeNames) {
387 							if (attributeName in target)
388 								meshes[i].primitives[j].targets[k][attributeName] = cast (int) target[attributeName].integer;
389 						}
390 					}
391 				}
392 				foreach (attributeName, attributeIndex; primitive["attributes"].object)
393 					meshes[i].primitives[j].attributes[attributeName] = cast (uint) attributeIndex.integer;
394 			}
395 			if ("weights" in mesh) {
396 				meshes[i].weights.length = mesh["weights"].array.length;
397 				foreach (j, weight; mesh["weights"].array)
398 					meshes[i].weights[j] = getFloatValue (weight);
399 			}
400 		}
401 	}
402 
403 	private void loadNodes () {
404 		if ("nodes" !in json)
405 			return;
406 		nodes.length = json["nodes"].array.length;
407 		
408 		foreach (i, node; json["nodes"].array) {
409 			if ("name" in node)
410 				nodes[i].name = node["name"].str;
411 			if ("camera" in node)
412 				nodes[i].camera = cast (int) node["camera"].integer;
413 			if ("children" in node) {
414 				nodes[i].children.length = node["children"].array.length;
415 				foreach (j, child; node["children"].array) {
416 					nodes[i].children[j] = cast (int) child.integer;
417 				}
418 			}
419 			if ("skin" in node)
420 				nodes[i].skin = cast (int) node["skin"].integer;
421 			if ("mesh" in node)
422 				nodes[i].mesh = cast (int) node["mesh"].integer;
423 			
424 			if ("translation" in node) {
425 				enforce (node["translation"].array.length = nodes[i].translation.length, "Node " ~ to!string (i) ~ " translation array length must be " ~ nodes[i].translation.length);
426 				foreach (j, translationElement; node["translation"].array) {
427 					nodes[i].translation[j] = getFloatValue (translationElement);
428 				}
429 			}
430 			if ("rotation" in node) {
431 				enforce (node["rotation"].array.length = nodes[i].rotation.length, "Node " ~ to!string (i) ~ " rotation array length must be " ~ nodes[i].rotation.length);
432 				foreach (j, rotationElement; node["rotation"].array) {
433 					nodes[i].rotation[j] = getFloatValue (rotationElement);
434 				}
435 			}
436 			if ("scale" in node) {
437 				enforce (node["scale"].array.length = nodes[i].scale.length, "Node " ~ to!string (i) ~ " scale array length must be " ~ nodes[i].scale.length);
438 				foreach (j, scaleElement; node["scale"].array) {
439 					nodes[i].scale[j] = getFloatValue (scaleElement);
440 				}
441 			}
442 			if ("matrix" in node) {
443 				enforce (node["matrix"].array.length = nodes[i].matrix.length, "Node " ~ to!string (i) ~ " matrix array length must be " ~ nodes[i].matrix.length);
444 				foreach (j, matrixElement; node["matrix"].array) {
445 					nodes[i].matrix[j] = getFloatValue (matrixElement);
446 				}			
447 			}
448 			if ("weights" in node) {
449 				nodes[i].weights.length = node["weights"].array.length;
450 				foreach (j, weight; node["weights"].array) {
451 					nodes[i].weights[j] = getFloatValue (weight);
452 				}
453 			}
454 		}
455 	}
456 
457 	private void loadSkins () {
458 		if ("skins" !in json)
459 			return;
460 		skins.length = json["skins"].array.length;
461 		
462 		foreach (i, skin; json["skins"].array) {
463 			skins[i].joints.length = skin["joints"].array.length;
464 			foreach (j, joint; skin["joints"].array) {
465 				skins[i].joints[j] = cast (int) joint.integer;
466 			}
467 			if ("skeleton" in skin)
468 				skins[i].skeleton = cast (int) skin["skeleton"].integer;
469 			if ("inverseBindMatrices" in skin)
470 				skins[i].inverseBindMatrices = cast (int) skin["inverseBindMatrices"].integer;
471 		}
472 	}
473 
474 	private void loadAnimations () {
475 		if ("animations" !in json)
476 			return;
477 		animations.length = json["animations"].array.length;
478 		
479 		foreach (i, animation; json["animations"].array) {
480 			if ("name" in animation) {
481 				animations[i].name = animation["name"].str;
482 			}
483 			animations[i].samplers.length = animation["samplers"].array.length;
484 
485 			foreach (j, sampler; animation["samplers"].array) {
486 				animations[i].samplers[j].input = cast (int) sampler["input"].integer;
487 				animations[i].samplers[j].output = cast (int) sampler["output"].integer;
488 				if ("interpolation" in sampler) {
489 					foreach (k, interpolationMode; interpolationModes) {
490 						if (interpolationMode == sampler["interpolation"].str)
491 							animations[i].samplers[j].interpolation = cast (glTF2Animation.Interpolation) k;
492 					}
493 				}
494 			}
495 
496 			animations[i].channels.length = animation["channels"].array.length;
497 			foreach (j, channel; animation["channels"].array) {
498 				animations[i].channels[j].sampler = cast (int) channel["sampler"].integer;
499 				if ("node" in channel["target"])
500 					animations[i].channels[j].target.node = cast (int) channel["target"]["node"].integer;
501 				foreach (k, targetType; targetTypes) {
502 					if (targetType == channel["target"]["path"].str)
503 						animations[i].channels[j].target.path = cast (glTF2Animation.Path) k;
504 				}
505 			}
506 		}
507 	}
508 
509 	private void loadBuffers () {
510 		if ("buffers" !in json)
511 			return;
512 		buffers.length = json["buffers"].array.length;
513 		
514 		foreach (i, buffer; json["buffers"].array) {
515 			if ("name" in buffer)
516 				buffers[i].name = buffer["name"].str;
517 			buffers[i].byteLength = cast (int) buffer["byteLength"].integer;
518 			if ("uri" in buffer)
519 				buffers[i].uri = buffer["uri"].str;
520 		}
521 	}
522 
523 	private void loadBufferViews () {
524 		if ("bufferViews" !in json)
525 			return;
526 		bufferViews.length = json["bufferViews"].array.length;
527 		
528 		foreach (i, bufferView; json["bufferViews"].array) {
529 			if ("name" in bufferView)
530 				bufferViews[i].name = bufferView["name"].str;
531 			bufferViews[i].buffer = cast (int) bufferView["buffer"].integer;
532 			if ("byteOffset" in bufferView)
533 				bufferViews[i].byteOffset = bufferView["byteOffset"].integer;
534 			bufferViews[i].byteLength = bufferView["byteLength"].integer;
535 			if ("byteStride" in bufferView)
536 				bufferViews[i].byteStride = cast (int) bufferView["byteStride"].integer;
537 			if ("target" in bufferView)
538 				bufferViews[i].target = cast (glTF2BufferView.TargetType) bufferView["target"].integer;
539 			
540 		}
541 	}
542 
543 	private void loadAccessors () {
544 		if ("accessors" !in json)
545 			return;
546 		accessors.length = json["accessors"].array.length;
547 		
548 		foreach (i, accessor; json["accessors"].array) {
549 			accessors[i].componentType = cast (glTF2Accessor.ComponentType) accessor["componentType"].integer;
550 			foreach (j, typeName; typeNames) {
551 				if (typeName == accessor["type"].str)
552 					accessors[i].type = cast (glTF2Accessor.Type) j;
553 			}
554 			accessors[i].bufferView = cast (int) accessor["bufferView"].integer;
555 			if ("byteOffset" in accessor)
556 				accessors[i].byteOffset = accessor["byteOffset"].integer;
557 			if ("normalized" in accessor) {
558 				if (accessor["normalized"].type == JSONType.true_)
559 					accessors[i].normalized = true;
560 				else
561 					accessors[i].normalized = false;
562 			}
563 			accessors[i].count = accessor["count"].integer;
564 			int minMaxArrayLength = 0;
565 			if ("min" in accessor) {
566 				switch (accessors[i].type) {
567 					case glTF2Accessor.Type.Scalar: minMaxArrayLength = 1; break;
568 					case glTF2Accessor.Type.Vec2: minMaxArrayLength = 2; break;
569 					case glTF2Accessor.Type.Vec3: minMaxArrayLength = 3; break;
570 					case glTF2Accessor.Type.Vec4: minMaxArrayLength = 4; break;
571 					case glTF2Accessor.Type.Mat2: minMaxArrayLength = 4; break;
572 					case glTF2Accessor.Type.Mat3: minMaxArrayLength = 9; break;
573 					case glTF2Accessor.Type.Mat4: minMaxArrayLength = 16; break;
574 					default: break;
575 				}
576 				if (accessors[i].componentType == glTF2Accessor.ComponentType.Float) {
577 					accessors[i].min.f.length = minMaxArrayLength;
578 					foreach (j, minElement; accessor["min"].array)
579 						accessors[i].min.f[j] = getFloatValue (minElement);
580 				}
581 				else {
582 					accessors[i].min.i.length = minMaxArrayLength;
583 					foreach (j, minElement; accessor["min"].array)
584 						accessors[i].min.i[j] = cast (int) minElement.integer;
585 				}
586 			}
587 			if ("max" in accessor) {
588 				switch (accessors[i].type) {
589 					case glTF2Accessor.Type.Scalar: minMaxArrayLength = 1; break;
590 					case glTF2Accessor.Type.Vec2: minMaxArrayLength = 2; break;
591 					case glTF2Accessor.Type.Vec3: minMaxArrayLength = 3; break;
592 					case glTF2Accessor.Type.Vec4: minMaxArrayLength = 4; break;
593 					case glTF2Accessor.Type.Mat2: minMaxArrayLength = 4; break;
594 					case glTF2Accessor.Type.Mat3: minMaxArrayLength = 9; break;
595 					case glTF2Accessor.Type.Mat4: minMaxArrayLength = 16; break;
596 					default: break;
597 				}
598 				if (accessors[i].componentType == glTF2Accessor.ComponentType.Float) {
599 					accessors[i].max.f.length = minMaxArrayLength;
600 					foreach (j, maxElement; accessor["max"].array)
601 						accessors[i].max.f[j] = getFloatValue (maxElement);
602 				}
603 				else {
604 					accessors[i].max.i.length = minMaxArrayLength;
605 					foreach (j, maxElement; accessor["max"].array)
606 						accessors[i].max.i[j] = cast (int) maxElement.integer;
607 				}
608 			}
609 			if ("sparse" in accessor) {
610 				accessors[i].sparse.count = cast (int) accessor["sparse"]["count"].integer;
611 
612 				accessors[i].sparse.indices.bufferView = cast (int) accessor["sparse"]["indices"]["bufferView"].integer;
613 				accessors[i].sparse.indices.componentType = cast (glTF2Accessor.ComponentType) accessor["sparse"]["indices"]["componentType"].integer;
614 				if ("byteOffset" in accessor["sparse"]["indices"])
615 					accessors[i].sparse.indices.byteOffset = cast (int) accessor["sparse"]["indices"]["byteOffset"].integer;
616 
617 				accessors[i].sparse.values.bufferView = cast (int) accessor["sparse"]["values"]["bufferView"].integer;
618 				if ("byteOffset" in accessor["sparse"]["values"])
619 					accessors[i].sparse.values.byteOffset = cast (int) accessor["sparse"]["values"]["byteOffset"].integer;
620 			}
621 		}
622 	}
623 
624 	private void loadMaterials () {
625 		if ("materials" !in json)
626 			return;
627 		materials.length = json["materials"].array.length;
628 		
629 		foreach (i, material; json["materials"].array) {
630 			if ("name" in material)
631 				materials[i].name = material["name"].str;
632 			if ("pbrMetallicRoughness" in material) {
633 				if ("baseColorFactor" in material["pbrMetallicRoughness"]) {
634 					enforce (material["pbrMetallicRoughness"]["baseColorFactor"].array.length = materials[i].pbrMetallicRoughness.baseColorFactor.length, "material " ~ to!string (i) ~ " baseColorFactor array length must be " ~ materials[i].pbrMetallicRoughness.baseColorFactor.length);
635 					foreach (j, colorComponent; material["pbrMetallicRoughness"]["baseColorFactor"].array) {
636 						materials[i].pbrMetallicRoughness.baseColorFactor[j] = getFloatValue (colorComponent);
637 					}
638 				}
639 				if ("baseColorTexture" in material["pbrMetallicRoughness"]) {
640 					materials[i].pbrMetallicRoughness.baseColorTexture.index = cast (int) material["pbrMetallicRoughness"]["baseColorTexture"]["index"].integer;
641 					if ("texCoord" in material["pbrMetallicRoughness"]["baseColorTexture"])
642 						materials[i].pbrMetallicRoughness.baseColorTexture.texCoord = cast (int) material["pbrMetallicRoughness"]["baseColorTexture"]["texCoord"].integer;
643 				}
644 				if ("metallicFactor" in material["pbrMetallicRoughness"])
645 					materials[i].pbrMetallicRoughness.metallicFactor = getFloatValue (material["pbrMetallicRoughness"]["metallicFactor"]);
646 				if ("roughnessFactor" in material["pbrMetallicRoughness"])
647 					materials[i].pbrMetallicRoughness.roughnessFactor = getFloatValue (material["pbrMetallicRoughness"]["roughnessFactor"]);
648 				if ("metallicRoughnessTexture" in material["pbrMetallicRoughness"]) {
649 					materials[i].pbrMetallicRoughness.metallicRoughnessTexture.index = cast (int) material["pbrMetallicRoughness"]["metallicRoughnessTexture"]["index"].integer;
650 					if ("texCoord" in material["pbrMetallicRoughness"]["metallicRoughnessTexture"])
651 						materials[i].pbrMetallicRoughness.metallicRoughnessTexture.texCoord = cast (int) material["pbrMetallicRoughness"]["metallicRoughnessTexture"]["texCoord"].integer;
652 				}
653 			}
654 			if ("normalTexture" in material) {
655 				materials[i].normalTexture.index = cast (int) material["normalTexture"]["index"].integer;
656 				if ("texCoord" in material["normalTexture"])
657 					materials[i].normalTexture.texCoord = cast (int) material["normalTexture"]["texCoord"].integer;
658 				if ("scale" in material["normalTexture"])
659 					materials[i].normalTexture.scale = getFloatValue (material["normalTexture"]["scale"]);
660 			}
661 			if ("occlusionTexture" in material) {
662 				materials[i].occlusionTexture.index = cast (int) material["occlusionTexture"]["index"].integer;
663 				if ("texCoord" in material["occlusionTexture"])
664 					materials[i].occlusionTexture.texCoord = cast (int) material["occlusionTexture"]["texCoord"].integer;
665 				if ("strength" in material["occlusionTexture"])
666 					materials[i].occlusionTexture.strength = getFloatValue (material["occlusionTexture"]["strength"]);
667 			}
668 			if ("emissiveTexture" in material) {
669 				materials[i].emissiveTexture.index = cast (int) material["emissiveTexture"]["index"].integer;
670 				if ("texCoord" in material["emissiveTexture"])
671 					materials[i].emissiveTexture.texCoord = cast (int) material["emissiveTexture"]["texCoord"].integer;
672 			}
673 			if ("emissiveFactor" in material) {
674 				enforce (material["emissiveFactor"].array.length = materials[i].emissiveFactor.length, "material " ~ to!string (i) ~ " emissiveFactor array length must be " ~ materials[i].emissiveFactor.length);
675 				foreach (j, colorComponent; material["emissiveFactor"].array) 
676 					materials[i].emissiveFactor[j] = getFloatValue (colorComponent);
677 				
678 			}
679 			if ("alphaMode" in material)
680 				foreach (j, alphaModeName; alphaModes) {
681 					if (alphaModeName == material["alphaMode"].str)
682 						materials[i].alphaMode = cast (glTF2Material.AlphaMode) j;
683 				}
684 			if ("alphaCutoff" in material)
685 				materials[i].alphaCutoff = getFloatValue (material["alphaCutoff"]);
686 			if ("doubleSided" in material) {
687 				if (material["doubleSided"].type == JSONType.true_)
688 					materials[i].doubleSided = true;
689 				else
690 					materials[i].doubleSided = false;
691 			}
692 		}
693 	}
694 
695 	private void loadCameras () {
696 		if ("cameras" !in json)
697 			return;
698 		cameras.length = json["cameras"].array.length;
699 		
700 		foreach (i, camera; json["cameras"].array) {
701 			enforce (((camera["type"].str == "perspective") || (camera["type"].str == "orthographic")), "Camera " ~ to!string(i) ~ " - camera type should be 'perspective' or 'orthographic'");
702 			if (camera["type"].str == "perspective") {
703 				cameras[i].cameraType = glTF2Camera.CameraType.Perspective;
704 				cameras[i].znear = getFloatValue (camera["perspective"]["znear"]);
705 				cameras[i].yfov = getFloatValue (camera["perspective"]["yfov"]);
706 				if ("zfar" in camera["perspective"])
707 					cameras[i].zfar = getFloatValue (camera["perspective"]["zfar"]);
708 				if ("aspectRatio" in camera["perspective"])
709 					cameras[i].aspectRatio = getFloatValue (camera["perspective"]["aspectRatio"]);
710 			}
711 			else {
712 				cameras[i].cameraType = glTF2Camera.CameraType.Orthographic;
713 				cameras[i].znear = getFloatValue (camera["orthographic"]["znear"]);
714 				cameras[i].zfar = getFloatValue (camera["orthographic"]["zfar"]);
715 				cameras[i].xmag = getFloatValue (camera["orthographic"]["xmag"]);
716 				cameras[i].ymag = getFloatValue (camera["orthographic"]["ymag"]);
717 			}
718 		}
719 	}
720 
721 	private void loadImages () {
722 		if ("images" !in json)
723 			return;
724 		images.length = json["images"].array.length;
725 		
726 		foreach (i, image; json["images"].array) {
727 			if ("name" in image)
728 				images[i].name = image["name"].str;		
729 			if ("mimeType" in image)
730 				images[i].mimeType = image["mimeType"].str;
731 			if ("bufferView" in image)
732 				images[i].bufferView = cast (int) image["bufferView"].integer;
733 			if ("uri" in image)
734 				images[i].uri = image["uri"].str;
735 		}
736 	}
737 
738 	private void loadSamplers () {
739 		if ("samplers" !in json)
740 			return;
741 		samplers.length = json["samplers"].array.length;
742 		
743 		foreach (i, sampler; json["samplers"].array) {
744 			if ("name" in sampler)
745 				samplers[i].name = sampler["name"].str;
746 			if ("magFilter" in sampler)
747 				samplers[i].magFilter = cast (glTF2Sampler.MagFilter) sampler["magFilter"].integer;
748 			if ("minFilter" in sampler)
749 				samplers[i].minFilter = cast (glTF2Sampler.MinFilter) sampler["magFilter"].integer;
750 			if ("wrapS" in sampler)
751 				samplers[i].wrapS = cast (glTF2Sampler.WrappingMode) sampler["wrapS"].integer;
752 			if ("wrapT" in sampler)
753 				samplers[i].wrapT = cast (glTF2Sampler.WrappingMode) sampler["wrapT"].integer;
754 		}
755 	}
756 
757 	private void loadTextures () {
758 		if ("textures" !in json)
759 			return;
760 		textures.length = json["textures"].array.length;
761 		
762 		foreach (i, texture; json["textures"].array) {
763 			if ("name" in texture)
764 				textures[i].name = texture["name"].str;
765 			if ("sampler" in texture)
766 				textures[i].sampler = cast (int) texture["sampler"].integer;
767 			if ("source" in texture)
768 				textures[i].source = cast (int) texture["source"].integer;
769 		}
770 	}
771 
772 	private JSONValue readBinaryBlob (ubyte[] fileContent) {
773 		uint binaryContainerVersion = fileContent.peek!(uint, Endian.littleEndian) (4);
774 		enforce (binaryContainerVersion == 2, "Version " ~ binaryContainerVersion.to!string ~ " of the binary container is declared. Only version 2 is supported.");
775 		uint totalLength = fileContent.peek!(uint, Endian.littleEndian) (8);
776 		enforce (totalLength == fileContent.length, "Declared file length does not match actual file length.");
777 
778 		char[] jsonChunkIdentifier = cast (char[]) fileContent [16 .. 20];
779 		enforce (jsonChunkIdentifier == "JSON", "JSON chunk not found.");
780 		uint jsonChunkLength = fileContent.peek!(uint, Endian.littleEndian) (12);
781 		string jsonChunkData = cast (string) fileContent [20 .. 20 + jsonChunkLength];
782 
783 		JSONValue json = parseJSON (jsonChunkData);
784 
785 		return json;
786 	}
787 
788 	void loadBufferData (ubyte[] fileContent) {
789 		foreach (i, ref buffer; buffers) {
790 			if (buffer.uri) {
791 				if (buffer.uri.length > 4 && buffer.uri[0 .. 5] == "data:") {
792 					auto splits = findSplitAfter (buffer.uri, "base64,");
793 					if (splits[1].length % 4 == 2)
794 						splits[1] ~= "==";
795 					if (splits[1].length % 4 == 3)
796 						splits[1] ~= "=";
797 					buffer.data = Base64.decode (splits[1]);
798 				}
799 				else {
800 					buffer.data = cast (ubyte[]) read (directoryName ~ "/" ~ buffer.uri);
801 				}
802 			}
803 			else {
804 				uint totalLength = fileContent.peek!(uint, Endian.littleEndian) (8);
805 				uint jsonChunkLength = fileContent.peek!(uint, Endian.littleEndian) (12);
806 				uint binaryChunkOffset = jsonChunkLength + 20;
807 				enforce (totalLength > binaryChunkOffset, "Unexpected end of file.");
808 
809 				char[4] binaryChunkIdentifier = cast (char[]) fileContent [binaryChunkOffset + 4.. binaryChunkOffset + 8];
810 				enforce (binaryChunkIdentifier == "BIN\0", "Binary chunk not found.");
811 
812 				uint binaryChunkLength = fileContent.peek!(uint, Endian.littleEndian) (binaryChunkOffset);
813 				void[] data = cast (void[]) fileContent [binaryChunkOffset + 8 .. binaryChunkOffset + 8 + binaryChunkLength];
814 
815 				buffer.data = data;
816 			}
817 		}
818 	}
819 
820 	void loadImageData () {
821 		foreach (i, ref image; images) {
822 			if (image.bufferView == -1) {
823 				if (image.uri.length > 4 && image.uri[0 .. 5] == "data:") {
824 					auto splits = findSplit (image.uri[5 .. $], ";base64,");
825 
826 					image.mimeType = splits[0];
827 					if (splits[2].length % 4 == 2)
828 						splits[2] ~= "==";
829 					if (splits[2].length % 4 == 3)
830 						splits[2] ~= "=";
831 
832 					image.data = Base64.decode (splits[2]);
833 				}
834 				else {
835 					image.data = read (directoryName ~ "/" ~ image.uri);
836 					enforce (image.data, "Failed to load " ~ directoryName ~ "/" ~ image.uri);
837 				}
838 			}
839 			//NB: image data already in buffers is not duplicated; reading image.data is NOT recommended, use asset.accessImage (imageIndex) instead
840 		}
841 	}
842 
843 	private void resetAsset () {
844 		json = null;
845 		fileName = null;
846 		directoryName = null;
847 
848     	scene = -1;
849 		metadata = cast (glTF2Metadata) null;
850 		scenes = null;
851 		meshes = null;
852 		nodes = null;
853 		skins = null;
854 		animations = null;
855 		buffers = null;
856 		bufferViews = null;
857 		accessors = null;
858 		materials = null;
859 		cameras = null;
860 		images = null;
861 		samplers = null;
862 		textures = null;
863 	}
864 
865 	void load (string fileName) {
866 		resetAsset ();
867 
868 		this.fileName = fileName;
869 		this.directoryName = dirName (fileName);
870 		ubyte[] fileContent = cast (ubyte[]) read (fileName);
871 		enforce (fileContent, "Failed to load " ~ fileName);
872 
873 		if (cast (char[]) fileContent[0 .. 4] == "glTF") {
874 			json = readBinaryBlob (fileContent);
875 		}
876 		else
877 			json = parseJSON (cast (string) fileContent);
878 		
879 		if ("extensionsRequired" in json) {
880 			writeln ("No extensions are supported at this time. Aborting.");
881 			return;
882 		}
883 
884 		loadMetadata ();
885 		loadScenes ();
886 		loadMeshes ();
887 		loadNodes ();
888 		loadSkins ();
889 		loadAnimations ();
890 		loadBuffers ();
891 		loadBufferViews ();
892 		loadAccessors ();
893 		loadMaterials ();
894 		loadCameras ();
895 		loadImages ();
896 		loadSamplers ();
897 		loadTextures ();
898 
899 		loadBufferData (fileContent);
900 		loadImageData ();
901 	}
902 
903 	this (string fileName) {
904 		load (fileName);
905 	}
906 
907 	void[] accessData (uint accessor) {
908 		ulong offset = accessors[accessor].byteOffset + bufferViews [accessors[accessor].bufferView].byteOffset;
909 
910 		uint elementSize;
911 		final switch (accessors[accessor].componentType) {
912 			case glTF2Accessor.ComponentType.Byte, glTF2Accessor.ComponentType.UnsignedByte: elementSize = 1; break;
913 			case glTF2Accessor.ComponentType.Short, glTF2Accessor.ComponentType.UnsignedShort: elementSize = 2; break;
914 			case glTF2Accessor.ComponentType.UnsignedInt, glTF2Accessor.ComponentType.Float: elementSize = 4; break;
915 		}
916 		final switch (accessors[accessor].type) {
917 			case glTF2Accessor.Type.Scalar: break;
918 			case glTF2Accessor.Type.Vec2: elementSize *= 2; break;
919 			case glTF2Accessor.Type.Vec3: elementSize *= 3; break;
920 			case glTF2Accessor.Type.Vec4, glTF2Accessor.Type.Mat2: elementSize *= 4; break;
921 			case glTF2Accessor.Type.Mat3: elementSize *= 9; break;
922 			case glTF2Accessor.Type.Mat4: elementSize *= 16; break;
923 		}
924 		ulong elementCount = accessors[accessor].count;
925 		ulong byteLength = elementCount * elementSize;
926 
927 		void[] data = buffers[bufferViews[accessors[accessor].bufferView].buffer].data [offset .. offset + byteLength];
928 
929 		return data;
930 	}
931 
932 	void[][uint] accessSparseData (uint sparseAccessor) {
933 		enforce (accessors[sparseAccessor].sparse.count > 0, "No sparse accessor found at accessor index " ~ sparseAccessor.to!string ~ ".");
934 
935 		void[][uint] data;
936 
937 		//retrieving values
938 		uint dataElementSize;
939 		final switch (accessors[sparseAccessor].componentType) {
940 			case glTF2Accessor.ComponentType.Byte, glTF2Accessor.ComponentType.UnsignedByte: dataElementSize = 1; break;
941 			case glTF2Accessor.ComponentType.Short, glTF2Accessor.ComponentType.UnsignedShort: dataElementSize = 2; break;
942 			case glTF2Accessor.ComponentType.UnsignedInt, glTF2Accessor.ComponentType.Float: dataElementSize = 4; break;
943 		}
944 		final switch (accessors[sparseAccessor].type) {
945 			case glTF2Accessor.Type.Scalar: break;
946 			case glTF2Accessor.Type.Vec2: dataElementSize *= 2; break;
947 			case glTF2Accessor.Type.Vec3: dataElementSize *= 3; break;
948 			case glTF2Accessor.Type.Vec4, glTF2Accessor.Type.Mat2: dataElementSize *= 4; break;
949 			case glTF2Accessor.Type.Mat3: dataElementSize *= 9; break;
950 			case glTF2Accessor.Type.Mat4: dataElementSize *= 16; break;
951 		}
952 		ulong dataByteLength = accessors[sparseAccessor].sparse.count * dataElementSize;
953 		ulong dataOffset = accessors[sparseAccessor].sparse.values.byteOffset + bufferViews[accessors[sparseAccessor].sparse.values.bufferView].byteOffset;
954 		void[] rawData = cast (void[]) buffers[bufferViews[accessors[sparseAccessor].sparse.values.bufferView].buffer].data [dataOffset .. dataOffset + dataByteLength];
955 
956 		//retrieving indices
957 		uint indicesElementSize;
958 		switch (accessors[sparseAccessor].sparse.indices.componentType) {
959 			case glTF2Accessor.ComponentType.UnsignedByte: indicesElementSize = 1; break;
960 			case glTF2Accessor.ComponentType.UnsignedShort: indicesElementSize = 2; break;
961 			case glTF2Accessor.ComponentType.UnsignedInt: indicesElementSize = 4; break;
962 			default: (enforce (0, "Wrong sparse accessor indices type.")); break;
963 		}
964 		ulong indicesByteLength = accessors[sparseAccessor].sparse.count * indicesElementSize;
965 		ulong indicesOffset = accessors[sparseAccessor].sparse.indices.byteOffset + bufferViews[accessors[sparseAccessor].sparse.indices.bufferView].byteOffset;
966 		void[] rawIndices = buffers[bufferViews[accessors[sparseAccessor].sparse.indices.bufferView].buffer].data [indicesOffset .. indicesOffset + indicesByteLength];
967 
968 		//populating the array
969 		for (int i = 0; i < accessors[sparseAccessor].sparse.count; i++) {
970 			switch (accessors[sparseAccessor].sparse.indices.componentType) {
971 				case glTF2Accessor.ComponentType.UnsignedByte:
972 					ubyte index = (cast (ubyte[]) rawIndices).peek!(ubyte, Endian.littleEndian) (ubyte.sizeof * i);
973 					data[index] = cast (void[]) rawData [dataElementSize * i .. dataElementSize * (i + 1)];
974 				break;
975 				case glTF2Accessor.ComponentType.UnsignedShort:
976 					ushort index = (cast (ubyte[]) rawIndices).peek!(ushort, Endian.littleEndian) (ushort.sizeof * i);
977 					data[index] = cast (void[]) rawData [dataElementSize * i .. dataElementSize * (i + 1)];
978 				break;
979 				case glTF2Accessor.ComponentType.UnsignedInt:
980 					uint index = (cast (ubyte[]) rawIndices).peek!(uint, Endian.littleEndian) (uint.sizeof * i);
981 					data[index] = cast (void[]) rawData [dataElementSize * i .. dataElementSize * (i + 1)];
982 				break;
983 			default: break;
984 			}
985 		}
986 
987 		return data;
988 	}
989 
990 	void[] accessImage (uint imageIndex) {
991 		if (images[imageIndex].bufferView != -1) {
992 			uint bufferView = images[imageIndex].bufferView;
993 			ulong offset = bufferViews[bufferView].byteOffset;
994 			ulong byteLength = bufferViews[bufferView].byteLength;
995 			return buffers[bufferViews[bufferView].buffer].data [offset .. offset + byteLength];
996 		}
997 		else
998 			return images[imageIndex].data;
999 	}
1000 };