1 /*
2 Copyright (c) 2021 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 module dagon.resource.gltf;
28 
29 import std.stdio;
30 import std.math;
31 import std.path;
32 import std.algorithm;
33 import std.base64;
34 import dlib.core.memory;
35 import dlib.core.ownership;
36 import dlib.core.stream;
37 import dlib.filesystem.filesystem;
38 import dlib.container.array;
39 import dlib.serialization.json;
40 import dlib.text.str;
41 import dlib.math.vector;
42 import dlib.math.matrix;
43 import dlib.math.transformation;
44 import dlib.math.quaternion;
45 import dlib.geometry.triangle;
46 import dlib.image.color;
47 import dlib.image.image;
48 
49 import dagon.core.bindings;
50 import dagon.core.event;
51 import dagon.core.time;
52 import dagon.resource.asset;
53 import dagon.resource.texture;
54 import dagon.graphics.drawable;
55 import dagon.graphics.texture;
56 import dagon.graphics.material;
57 import dagon.graphics.mesh;
58 import dagon.graphics.entity;
59 
60 Vector3f asVector(JSONValue value)
61 {
62     Vector3f vec = Vector3f(0.0f, 0.0f, 0.0f);
63     foreach(i, v; value.asArray)
64         vec[i] = v.asNumber;
65     return vec;
66 }
67 
68 Matrix4x4f asMatrix(JSONValue value)
69 {
70     Matrix4x4f mat = Matrix4x4f.identity;
71     foreach(i, v; value.asArray)
72         mat[i] = v.asNumber;
73     return mat;
74 }
75 
76 Quaternionf asQuaternion(JSONValue value)
77 {
78     Quaternionf q = Quaternionf.identity;
79     foreach(i, v; value.asArray)
80         q[i] = v.asNumber;
81     return q;
82 }
83 
84 Color4f asColor(JSONValue value)
85 {
86     Color4f col = Color4f(1.0f, 1.0f, 1.0f, 1.0f);
87     foreach(i, v; value.asArray)
88         col[i] = v.asNumber;
89     return col;
90 }
91 
92 class GLTFBuffer: Owner
93 {
94     ubyte[] array;
95     
96     this(Owner o)
97     {
98         super(o);
99     }
100     
101     void fromArray(ubyte[] arr)
102     {
103         array = New!(ubyte[])(arr.length);
104         array[] = arr[];
105     }
106     
107     void fromStream(InputStream istrm)
108     {
109         if (istrm is null)
110             return;
111         
112         array = New!(ubyte[])(istrm.size);
113         if (!istrm.fillArray(array))
114         {
115             writeln("Warning: failed to read buffer");
116             Delete(array);
117         }
118     }
119     
120     void fromFile(ReadOnlyFileSystem fs, string filename)
121     {
122         FileStat fstat;
123         if (fs.stat(filename, fstat))
124         {
125             auto bufStream = fs.openForInput(filename);
126             fromStream(bufStream);
127             Delete(bufStream);
128         }
129         else
130             writeln("Warning: buffer file \"", filename, "\" not found");
131     }
132     
133     void fromBase64(string encoded)
134     {
135         auto decodedLength = Base64.decodeLength(encoded.length);
136         array = New!(ubyte[])(decodedLength);
137         auto decoded = Base64.decode(encoded, array);
138     }
139     
140     ~this()
141     {
142         if (array.length)
143             Delete(array);
144     }
145 }
146 
147 class GLTFBufferView: Owner
148 {
149     GLTFBuffer buffer;
150     uint offset;
151     uint len;
152     uint stride;
153     ubyte[] slice;
154     GLenum target;
155     
156     this(GLTFBuffer buffer, uint offset, uint len, uint stride, GLenum target, Owner o)
157     {
158         super(o);
159         
160         if (buffer is null)
161             return;
162         
163         this.buffer = buffer;
164         this.offset = offset;
165         this.len = len;
166         this.stride = stride;
167         this.target = target;
168         
169         if (offset < buffer.array.length && offset+len <= buffer.array.length)
170         {
171             this.slice = buffer.array[offset..offset+len];
172         }
173         else
174         {
175             writeln("Warning: invalid buffer view bounds");
176         }
177     }
178     
179     ~this()
180     {
181     }
182 }
183 
184 enum GLTFDataType
185 {
186     Undefined,
187     Scalar,
188     Vec2,
189     Vec3,
190     Vec4,
191     Mat2,
192     Mat3,
193     Mat4
194 }
195 
196 class GLTFAccessor: Owner
197 {
198     GLTFBufferView bufferView;
199     GLTFDataType dataType;
200     uint numComponents;
201     GLenum componentType;
202     uint count;
203     uint byteOffset;
204     
205     this(GLTFBufferView bufferView, GLTFDataType dataType, GLenum componentType, uint count, uint byteOffset, Owner o)
206     {
207         super(o);
208         
209         if (bufferView is null)
210             return;
211         
212         this.bufferView = bufferView;
213         this.dataType = dataType;
214         this.componentType = componentType;
215         this.count = count;
216         this.byteOffset = byteOffset;
217         
218         switch(dataType)
219         {
220             case GLTFDataType.Scalar: numComponents = 1; break;
221             case GLTFDataType.Vec2:   numComponents = 2; break;
222             case GLTFDataType.Vec3:   numComponents = 3; break;
223             case GLTFDataType.Vec4:   numComponents = 4; break;
224             case GLTFDataType.Mat2:   numComponents = 2 * 2; break;
225             case GLTFDataType.Mat3:   numComponents = 3 * 3; break;
226             case GLTFDataType.Mat4:   numComponents = 4 * 4; break;
227             default: numComponents = 1; break;
228         }
229     }
230     
231     ~this()
232     {
233     }
234 }
235 
236 class GLTFMeshPrimitive: Owner, Drawable
237 {
238     GLTFAccessor positionAccessor;
239     GLTFAccessor normalAccessor;
240     GLTFAccessor texCoord0Accessor;
241     GLTFAccessor indexAccessor;
242     Material material;
243     
244     GLuint vao = 0;
245     GLuint vbo = 0;
246     GLuint nbo = 0;
247     GLuint tbo = 0;
248     GLuint eao = 0;
249     
250     bool canRender = false;
251     
252     this(GLTFAccessor positionAccessor, GLTFAccessor normalAccessor, GLTFAccessor texCoord0Accessor, GLTFAccessor indexAccessor, Material material, Owner o)
253     {
254         super(o);
255         this.positionAccessor = positionAccessor;
256         this.normalAccessor = normalAccessor;
257         this.texCoord0Accessor = texCoord0Accessor;
258         this.indexAccessor = indexAccessor;
259         this.material = material;
260     }
261     
262     void prepareVAO()
263     {
264         if (positionAccessor is null || 
265             normalAccessor is null || 
266             texCoord0Accessor is null || 
267             indexAccessor is null)
268             return;
269         
270         if (positionAccessor.bufferView.slice.length == 0)
271             return;
272         if (normalAccessor.bufferView.slice.length == 0)
273             return;
274         if (texCoord0Accessor.bufferView.slice.length == 0)
275             return;
276         if (indexAccessor.bufferView.slice.length == 0)
277             return;
278         
279         glGenBuffers(1, &vbo);
280         glBindBuffer(GL_ARRAY_BUFFER, vbo);
281         glBufferData(GL_ARRAY_BUFFER, positionAccessor.bufferView.slice.length, positionAccessor.bufferView.slice.ptr, GL_STATIC_DRAW); 
282         
283         glGenBuffers(1, &nbo);
284         glBindBuffer(GL_ARRAY_BUFFER, nbo);
285         glBufferData(GL_ARRAY_BUFFER, normalAccessor.bufferView.slice.length, normalAccessor.bufferView.slice.ptr, GL_STATIC_DRAW);
286         
287         glGenBuffers(1, &tbo);
288         glBindBuffer(GL_ARRAY_BUFFER, tbo);
289         glBufferData(GL_ARRAY_BUFFER, texCoord0Accessor.bufferView.slice.length, texCoord0Accessor.bufferView.slice.ptr, GL_STATIC_DRAW);
290         
291         glGenBuffers(1, &eao);
292         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
293         glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexAccessor.bufferView.slice.length, indexAccessor.bufferView.slice.ptr, GL_STATIC_DRAW);
294         
295         glGenVertexArrays(1, &vao);
296         glBindVertexArray(vao);
297         
298         glEnableVertexAttribArray(VertexAttrib.Vertices);
299         glBindBuffer(GL_ARRAY_BUFFER, vbo);
300         glVertexAttribPointer(VertexAttrib.Vertices, positionAccessor.numComponents, positionAccessor.componentType, GL_FALSE, positionAccessor.bufferView.stride, cast(void*)positionAccessor.byteOffset);
301         
302         glEnableVertexAttribArray(VertexAttrib.Normals);
303         glBindBuffer(GL_ARRAY_BUFFER, nbo);
304         glVertexAttribPointer(VertexAttrib.Normals, normalAccessor.numComponents, normalAccessor.componentType, GL_FALSE, normalAccessor.bufferView.stride, cast(void*)normalAccessor.byteOffset);
305         
306         glEnableVertexAttribArray(VertexAttrib.Texcoords);
307         glBindBuffer(GL_ARRAY_BUFFER, tbo);
308         glVertexAttribPointer(VertexAttrib.Texcoords, texCoord0Accessor.numComponents, texCoord0Accessor.componentType, GL_FALSE, texCoord0Accessor.bufferView.stride, cast(void*)texCoord0Accessor.byteOffset);
309         
310         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eao);
311         
312         glBindVertexArray(0);
313         
314         canRender = true;
315     }
316     
317     void render(GraphicsState* state)
318     {
319         if (canRender)
320         {
321             glBindVertexArray(vao);
322             glDrawElements(GL_TRIANGLES, indexAccessor.count, indexAccessor.componentType, cast(void*)indexAccessor.byteOffset);
323             glBindVertexArray(0);
324         }
325     }
326     
327     ~this()
328     {
329         if (canRender)
330         {
331             glDeleteVertexArrays(1, &vao);
332             glDeleteBuffers(1, &vbo);
333             glDeleteBuffers(1, &nbo);
334             glDeleteBuffers(1, &tbo);
335             glDeleteBuffers(1, &eao);
336         }
337     }
338 }
339 
340 class GLTFMesh: Owner, Drawable
341 {
342     Array!GLTFMeshPrimitive primitives;
343 
344     this(Owner o)
345     {
346         super(o);
347     }
348     
349     void render(GraphicsState* state)
350     {
351         GraphicsState newState = *state;
352         foreach(primitive; primitives)
353         {
354             if (primitive.material)
355                 primitive.material.bind(&newState);
356 
357             newState.shader.bindParameters(&newState);
358 
359             primitive.render(&newState);
360 
361             newState.shader.unbindParameters(&newState);
362 
363             if (primitive.material)
364                 primitive.material.unbind(&newState);
365         }
366     }
367     
368     ~this()
369     {
370     }
371 }
372 
373 class GLTFNode: Owner
374 {
375     Entity entity;
376     GLTFMesh mesh;
377     Matrix4x4f transformation;
378     size_t[] children;
379     
380     this(Owner o)
381     {
382         super(o);
383         transformation = Matrix4x4f.identity;
384         entity = New!Entity(this);
385     }
386     
387     ~this()
388     {
389         if (children.length)
390             Delete(children);
391     }
392 }
393 
394 class StaticTransformComponent: EntityComponent
395 {
396     Matrix4x4f transformation;
397     
398     this(EventManager em, Entity e, Matrix4x4f transformation)
399     {
400         super(em, e);
401         this.transformation = transformation;
402     }
403     
404     override void update(Time t)
405     {
406         entity.transformation = transformation;
407         entity.updateAbsoluteTransformation();
408     }
409 }
410 
411 class GLTFScene: Owner
412 {
413     string name;
414     Array!GLTFNode nodes;
415     
416     this(Owner o)
417     {
418         super(o);
419     }
420     
421     ~this()
422     {
423         nodes.free();
424     }
425 }
426 
427 class GLTFAsset: Asset, TriangleSet
428 {
429     AssetManager assetManager;
430     String str;
431     JSONDocument doc;
432     Array!GLTFBuffer buffers;
433     Array!GLTFBufferView bufferViews;
434     Array!GLTFAccessor accessors;
435     Array!GLTFMesh meshes;
436     Array!TextureAsset images;
437     Array!Texture textures;
438     Array!Material materials;
439     Array!GLTFNode nodes;
440     Array!GLTFScene scenes;
441     Entity rootEntity;
442     
443     this(Owner o)
444     {
445         super(o);
446     }
447     
448     ~this()
449     {
450         release();
451     }
452     
453     override bool loadThreadSafePart(string filename, InputStream istrm, ReadOnlyFileSystem fs, AssetManager mngr)
454     {
455         assetManager = mngr;
456         rootEntity = New!Entity(this);
457         string rootDir = dirName(filename);
458         str = String(istrm);
459         doc = New!JSONDocument(str.toString);
460         loadBuffers(doc.root, fs, rootDir);
461         loadBufferViews(doc.root);
462         loadAccessors(doc.root);
463         loadImages(doc.root, fs, rootDir);
464         loadTextures(doc.root);
465         loadMaterials(doc.root);
466         loadMeshes(doc.root);
467         loadNodes(doc.root);
468         loadScenes(doc.root);
469         return true;
470     }
471     
472     void loadBuffers(JSONValue root, ReadOnlyFileSystem fs, string rootDir)
473     {
474         if ("buffers" in root.asObject)
475         {
476             foreach(buffer; root.asObject["buffers"].asArray)
477             {
478                 auto buf = buffer.asObject;
479                 if ("uri" in buf)
480                 {
481                     string uri = buf["uri"].asString;
482                     string base64Prefix = "data:application/octet-stream;base64,";
483                     
484                     GLTFBuffer b = New!GLTFBuffer(this);
485                     
486                     if (uri.startsWith(base64Prefix))
487                     {
488                         auto encoded = uri[base64Prefix.length..$];
489                         b.fromBase64(encoded);
490                     }
491                     else
492                     {
493                         String bufFilename = String(rootDir);
494                         bufFilename ~= "/";
495                         bufFilename ~= buf["uri"].asString;
496                         b.fromFile(fs, bufFilename.toString);
497                         bufFilename.free();
498                     }
499                     
500                     buffers.insertBack(b);
501                 }
502             }
503         }
504     }
505     
506     void loadBufferViews(JSONValue root)
507     {
508         if ("bufferViews" in root.asObject)
509         {
510             foreach(bufferView; root.asObject["bufferViews"].asArray)
511             {
512                 auto bv = bufferView.asObject;
513                 uint bufferIndex = 0;
514                 uint byteOffset = 0;
515                 uint byteLength = 0;
516                 uint byteStride = 0;
517                 GLenum target = GL_ARRAY_BUFFER;
518                 
519                 if ("buffer" in bv)
520                     bufferIndex = cast(uint)bv["buffer"].asNumber;
521                 if ("byteOffset" in bv)
522                     byteOffset = cast(uint)bv["byteOffset"].asNumber;
523                 if ("byteLength" in bv)
524                     byteLength = cast(uint)bv["byteLength"].asNumber;
525                 if ("byteStride" in bv)
526                     byteStride = cast(uint)bv["byteStride"].asNumber;
527                 if ("target" in bv)
528                     target = cast(GLenum)bv["target"].asNumber;
529                 
530                 if (bufferIndex < buffers.length)
531                 {
532                     GLTFBufferView bufv = New!GLTFBufferView(buffers[bufferIndex], byteOffset, byteLength, byteStride, target, this);
533                     bufferViews.insertBack(bufv);
534                 }
535                 else
536                 {
537                     writeln("Warning: can't create buffer view for nonexistent buffer ", bufferIndex);
538                     GLTFBufferView bufv = New!GLTFBufferView(null, 0, 0, 0, 0, this);
539                     bufferViews.insertBack(bufv);
540                 }
541             }
542         }
543     }
544     
545     void loadAccessors(JSONValue root)
546     {
547         if ("accessors" in root.asObject)
548         {
549             foreach(i, accessor; root.asObject["accessors"].asArray)
550             {
551                 auto acc = accessor.asObject;
552                 uint bufferViewIndex = 0;
553                 GLenum componentType = GL_FLOAT;
554                 string type;
555                 uint count = 0;
556                 uint byteOffset = 0;
557                 
558                 if ("bufferView" in acc)
559                     bufferViewIndex = cast(uint)acc["bufferView"].asNumber;
560                 if ("componentType" in acc)
561                     componentType = cast(GLenum)acc["componentType"].asNumber;
562                 if ("type" in acc)
563                     type = acc["type"].asString;
564                 if ("count" in acc)
565                     count = cast(uint)acc["count"].asNumber;
566                 if ("byteOffset" in acc)
567                     byteOffset = cast(uint)acc["byteOffset"].asNumber;
568                 
569                 GLTFDataType dataType = GLTFDataType.Undefined;
570                 if (type == "SCALAR")
571                     dataType = GLTFDataType.Scalar;
572                 else if (type == "VEC2")
573                     dataType = GLTFDataType.Vec2;
574                 else if (type == "VEC3")
575                     dataType = GLTFDataType.Vec3;
576                 else if (type == "VEC4")
577                     dataType = GLTFDataType.Vec4;
578                 else if (type == "MAT2")
579                     dataType = GLTFDataType.Mat2;
580                 else if (type == "MAT3")
581                     dataType = GLTFDataType.Mat3;
582                 else if (type == "MAT4")
583                     dataType = GLTFDataType.Mat4;
584                 else
585                     writeln("Warning: unsupported data type for accessor ", i);
586                 
587                 if (bufferViewIndex < bufferViews.length)
588                 {
589                     GLTFAccessor ac = New!GLTFAccessor(bufferViews[bufferViewIndex], dataType, componentType, count, byteOffset, this);
590                     accessors.insertBack(ac);
591                 }
592                 else
593                 {
594                     writeln("Warning: can't create accessor for nonexistent buffer view ", bufferViewIndex);
595                     GLTFAccessor ac = New!GLTFAccessor(null, dataType, componentType, count, byteOffset, this);
596                     accessors.insertBack(ac);
597                 }
598             }
599         }
600     }
601     
602     void loadImages(JSONValue root, ReadOnlyFileSystem fs, string rootDir)
603     {
604         if ("images" in root.asObject)
605         {
606             foreach(i, img; root.asObject["images"].asArray)
607             {
608                 auto im = img.asObject;
609                 
610                 if ("uri" in im)
611                 {
612                     String imgFilename = String(rootDir);
613                     imgFilename ~= "/";
614                     imgFilename ~= im["uri"].asString;
615                     
616                     auto ta = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, this);
617                     
618                     FileStat fstat;
619                     if (fs.stat(imgFilename.toString, fstat))
620                     {
621                         bool res = assetManager.loadAssetThreadSafePart(ta, imgFilename.toString);
622                         if (!res)
623                             writeln("Warning: failed to load \"", imgFilename, "\" not found");
624                     }
625                     else
626                     {
627                         writeln("Warning: image file \"", imgFilename, "\" not found");
628                     }
629                     
630                     images.insertBack(ta);
631                     
632                     imgFilename.free();
633                 }
634                 else if ("bufferView" in im)
635                 {
636                     uint bufferViewIndex = cast(uint)im["bufferView"].asNumber;
637                     
638                     auto ta = New!TextureAsset(assetManager.imageFactory, assetManager.hdrImageFactory, this);
639                     
640                     if (bufferViewIndex < bufferViews.length)
641                     {
642                         auto bv = bufferViews[bufferViewIndex];
643                         string mimeType = "";
644                         if ("mimeType" in im)
645                             mimeType = im["mimeType"].asString;
646                         if (mimeType == "")
647                             writeln("Warning: image MIME type missing");
648                         else
649                         {
650                             string name = nameFromMimeType(mimeType);
651                             if (name == "")
652                             {
653                                 writeln("Warning: unsupported image MIME type ", mimeType);
654                             }
655                             else
656                             {
657                                 
658                                 bool res = assetManager.loadAssetThreadSafePart(ta, bv.slice, name);
659                                 if (!res)
660                                     writeln("Warning: failed to load image");
661                             }
662                         }
663                     }
664                     else
665                     {
666                         writeln("Warning: can't create image from nonexistent buffer view ", bufferViewIndex);
667                     }
668                     
669                     images.insertBack(ta);
670                 }
671             }
672         }
673     }
674     
675     // TODO: loadSamplers
676     
677     void loadTextures(JSONValue root)
678     {
679         if ("textures" in root.asObject)
680         {
681             foreach(i, tex; root.asObject["textures"].asArray)
682             {
683                 auto te = tex.asObject;
684                 
685                 if ("source" in te)
686                 {
687                     uint imageIndex = cast(uint)te["source"].asNumber;
688                     TextureAsset img;
689                     if (imageIndex < images.length)
690                         img = images[imageIndex];
691                     else
692                         writeln("Warning: can't create texture for nonexistent image ", imageIndex);
693                     
694                     if (img !is null)
695                     {
696                         Texture texture = img.texture;
697                         textures.insertBack(texture);
698                     }
699                     else
700                     {
701                         Texture texture;
702                         textures.insertBack(texture);
703                     }
704                 }
705                 
706                 // TODO: sampler
707             }
708         }
709     }
710     
711     void loadMaterials(JSONValue root)
712     {
713         if ("materials" in root.asObject)
714         {
715             foreach(i, mat; root.asObject["materials"].asArray)
716             {
717                 auto ma = mat.asObject;
718                 
719                 Material material = New!Material(this);
720                 
721                 if ("pbrMetallicRoughness" in ma)
722                 {
723                     auto pbr = ma["pbrMetallicRoughness"].asObject;
724                     
725                     Color4f baseColorFactor = Color4f(1.0f, 1.0f, 1.0f, 1.0f);
726                     if (pbr && "baseColorFactor" in pbr)
727                     {
728                         baseColorFactor = pbr["baseColorFactor"].asColor;
729                         
730                         if (baseColorFactor.a < 1.0f)
731                             material.blending = Transparent;
732                     }
733                     
734                     if (pbr && "baseColorTexture" in pbr)
735                     {
736                         auto bct = pbr["baseColorTexture"].asObject;
737                         if ("index" in bct)
738                         {
739                             uint baseColorTexIndex = cast(uint)bct["index"].asNumber;
740                             if (baseColorTexIndex < textures.length)
741                             {
742                                 Texture baseColorTex = textures[baseColorTexIndex];
743                                 if (baseColorTex)
744                                 {
745                                     material.diffuse = baseColorTex;
746                                 }
747                             }
748                         }
749                         
750                         material.transparency = baseColorFactor.a;
751                     }
752                     else
753                     {
754                         material.diffuse = baseColorFactor;
755                     }
756                     
757                     if (pbr && "metallicRoughnessTexture" in pbr)
758                     {
759                         uint metallicRoughnessTexIndex = cast(uint)pbr["metallicRoughnessTexture"].asObject["index"].asNumber;
760                         if (metallicRoughnessTexIndex < textures.length)
761                         {
762                             Texture metallicRoughnessTex = textures[metallicRoughnessTexIndex];
763                             if (metallicRoughnessTex)
764                                 material.roughnessMetallic = metallicRoughnessTex;
765                         }
766                     }
767                     else
768                     {
769                         if (pbr && "metallicFactor" in pbr)
770                         {
771                             material.metallic = pbr["metallicFactor"].asNumber;
772                         }
773                         else
774                         {
775                             material.metallic = 1.0f;
776                         }
777                         
778                         if (pbr && "roughnessFactor" in pbr)
779                         {
780                             material.roughness = pbr["roughnessFactor"].asNumber;
781                         }
782                         else
783                         {
784                             material.roughness = 1.0f;
785                         }
786                     }
787                 }
788                 else if ("extensions" in ma)
789                 {
790                     auto extensions = ma["extensions"].asObject;
791                     
792                     if ("KHR_materials_pbrSpecularGlossiness" in extensions)
793                     {
794                         auto pbr = extensions["KHR_materials_pbrSpecularGlossiness"].asObject;
795                         
796                         if (pbr && "diffuseTexture" in pbr)
797                         {
798                             auto dt = pbr["diffuseTexture"].asObject;
799                             if ("index" in dt)
800                             {
801                                 uint diffuseTexIndex = cast(uint)dt["index"].asNumber;
802                                 if (diffuseTexIndex < textures.length)
803                                 {
804                                     Texture diffuseTex = textures[diffuseTexIndex];
805                                     if (diffuseTex)
806                                     {
807                                         material.diffuse = diffuseTex;
808                                     }
809                                 }
810                             }
811                         }
812                     }
813                 }
814                 
815                 if ("normalTexture" in ma)
816                 {
817                     uint normalTexIndex = cast(uint)ma["normalTexture"].asObject["index"].asNumber;
818                     if (normalTexIndex < textures.length)
819                     {
820                         Texture normalTex = textures[normalTexIndex];
821                         if (normalTex)
822                             material.normal = normalTex;
823                     }
824                 }
825                 
826                 if ("emissiveTexture" in ma)
827                 {
828                     uint emissiveTexIndex = cast(uint)ma["emissiveTexture"].asObject["index"].asNumber;
829                     if (emissiveTexIndex < textures.length)
830                     {
831                         Texture emissiveTex = textures[emissiveTexIndex];
832                         if (emissiveTex)
833                             material.emission = emissiveTex;
834                     }
835                 }
836                 
837                 if ("doubleSided" in ma)
838                 {
839                     uint doubleSided = cast(uint)ma["doubleSided"].asNumber;
840                     if (doubleSided)
841                         material.culling = false;
842                     else
843                         material.culling = true;
844                 }
845                 else
846                 {
847                     material.culling = true;
848                 }
849                 
850                 if ("alphaCutoff" in ma)
851                 {
852                     material.clipThreshold = ma["alphaCutoff"].asNumber;
853                 }
854                 
855                 materials.insertBack(material);
856             }
857         }
858     }
859     
860     void loadMeshes(JSONValue root)
861     {
862         if ("meshes" in root.asObject)
863         {
864             foreach(i, mesh; root.asObject["meshes"].asArray)
865             {
866                 auto m = mesh.asObject;
867                 
868                 GLTFMesh me = New!GLTFMesh(this);
869                 
870                 if ("primitives" in m)
871                 {
872                     foreach(prim; m["primitives"].asArray)
873                     {
874                         auto p = prim.asObject;
875                         
876                         GLTFAccessor positionAccessor;
877                         GLTFAccessor normalAccessor;
878                         GLTFAccessor texCoord0Accessor;
879                         GLTFAccessor indexAccessor;
880                         
881                         if ("attributes" in p)
882                         {
883                             auto attributes = p["attributes"].asObject;
884                             
885                             if ("POSITION" in attributes)
886                             {
887                                 uint positionsAccessorIndex = cast(uint)attributes["POSITION"].asNumber;
888                                 if (positionsAccessorIndex < accessors.length)
889                                     positionAccessor = accessors[positionsAccessorIndex];
890                                 else
891                                     writeln("Warning: can't create position attributes for nonexistent accessor ", positionsAccessorIndex);
892                             }
893                             
894                             if ("NORMAL" in attributes)
895                             {
896                                 uint normalsAccessorIndex = cast(uint)attributes["NORMAL"].asNumber;
897                                 if (normalsAccessorIndex < accessors.length)
898                                     normalAccessor = accessors[normalsAccessorIndex];
899                                 else
900                                     writeln("Warning: can't create normal attributes for nonexistent accessor ", normalsAccessorIndex);
901                             }
902                             
903                             if ("TEXCOORD_0" in attributes)
904                             {
905                                 uint texCoord0AccessorIndex = cast(uint)attributes["TEXCOORD_0"].asNumber;
906                                 if (texCoord0AccessorIndex < accessors.length)
907                                     texCoord0Accessor = accessors[texCoord0AccessorIndex];
908                                 else
909                                     writeln("Warning: can't create texCoord0 attributes for nonexistent accessor ", texCoord0AccessorIndex);
910                             }
911                         }
912                         
913                         if ("indices" in p)
914                         {
915                             uint indicesAccessorIndex = cast(uint)p["indices"].asNumber;
916                             if (indicesAccessorIndex < accessors.length)
917                                 indexAccessor = accessors[indicesAccessorIndex];
918                             else
919                                 writeln("Warning: can't create indices for nonexistent accessor ", indicesAccessorIndex);
920                         }
921                         
922                         Material material;
923                         if ("material" in p)
924                         {
925                             uint materialIndex = cast(uint)p["material"].asNumber;
926                             if (materialIndex < materials.length)
927                                 material = materials[materialIndex];
928                             else
929                                 writeln("Warning: nonexistent material ", materialIndex);
930                         }
931                         
932                         if (positionAccessor is null)
933                         {
934                             writeln("Warning: mesh ", i, " lacks vertex position attributes");
935                         }
936                         if (normalAccessor is null)
937                         {
938                             writeln("Warning: mesh ", i, " lacks vertex normal attributes");
939                         }
940                         if (texCoord0Accessor is null)
941                         {
942                             writeln("Warning: mesh ", i, " lacks vertex texCoord0 attributes");
943                         }
944                         if (indexAccessor is null)
945                         {
946                             writeln("Warning: mesh ", i, " lacks indices");
947                         }
948                         
949                         GLTFMeshPrimitive pr = New!GLTFMeshPrimitive(positionAccessor, normalAccessor, texCoord0Accessor, indexAccessor, material, this);
950                         me.primitives.insertBack(pr);
951                     }
952                 }
953                 
954                 meshes.insertBack(me);
955             }
956         }
957     }
958     
959     void loadNodes(JSONValue root)
960     {
961         if ("nodes" in root.asObject)
962         {
963             foreach(i, n; root.asObject["nodes"].asArray)
964             {
965                 auto node = n.asObject;
966                 
967                 GLTFNode nodeObj = New!GLTFNode(this);
968                 nodeObj.entity.setParent(rootEntity);
969                 
970                 if ("mesh" in node)
971                 {
972                     uint meshIndex = cast(uint)node["mesh"].asNumber;
973                     if (meshIndex < meshes.length)
974                     {
975                         GLTFMesh mesh = meshes[meshIndex];
976                         nodeObj.mesh = mesh;
977                         nodeObj.entity.drawable = mesh;
978                     }
979                     else
980                         writeln("Warning: mesh ", meshIndex, " doesn't exist");
981                 }
982                 
983                 if ("matrix" in node)
984                 {
985                     nodeObj.transformation = node["matrix"].asMatrix;
986                     auto nodeComponent = New!StaticTransformComponent(assetManager.eventManager, nodeObj.entity, nodeObj.transformation);
987                 }
988                 else
989                 {
990                     Vector3f position = Vector3f(0.0f, 0.0f, 0.0f);
991                     Quaternionf rotation = Quaternionf.identity;
992                     Vector3f scale = Vector3f(1.0f, 1.0f, 1.0f);
993                     
994                     if ("translation" in node)
995                     {
996                         position = node["translation"].asVector;
997                     }
998                     if ("rotation" in node)
999                     {
1000                         rotation = node["rotation"].asQuaternion;
1001                     }
1002                     if ("scale" in node)
1003                     {
1004                         scale = node["scale"].asVector;
1005                     }
1006                     
1007                     nodeObj.entity.position = position;
1008                     nodeObj.entity.rotation = rotation;
1009                     nodeObj.entity.scaling = scale;
1010                     
1011                     nodeObj.transformation =
1012                         translationMatrix(position) *
1013                         rotation.toMatrix4x4 *
1014                         scaleMatrix(scale);
1015                 }
1016                 
1017                 if ("children" in node)
1018                 {
1019                     auto children = node["children"].asArray;
1020                     nodeObj.children = New!(size_t[])(children.length);
1021                     
1022                     foreach(ci, c; children)
1023                     {
1024                         uint childIndex = cast(uint)c.asNumber;
1025                         nodeObj.children[ci] = childIndex;
1026                     }
1027                 }
1028                 
1029                 nodes.insertBack(nodeObj);
1030             }
1031         }
1032         
1033         foreach(node; nodes)
1034         {
1035             foreach(i; node.children)
1036             {
1037                 GLTFNode child = nodes[i];
1038                 node.entity.addChild(child.entity);
1039             }
1040         }
1041         
1042         rootEntity.updateTransformationTopDown();
1043     }
1044     
1045     void loadScenes(JSONValue root)
1046     {
1047         if ("scenes" in root.asObject)
1048         {
1049             foreach(i, s; root.asObject["scenes"].asArray)
1050             {
1051                 auto scene = s.asObject;
1052                 
1053                 GLTFScene sceneObj = New!GLTFScene(this);
1054                 
1055                 if ("name" in scene)
1056                 {
1057                     sceneObj.name = scene["name"].asString;
1058                 }
1059                 
1060                 if ("nodes" in scene)
1061                 {
1062                     foreach(ni, n; scene["nodes"].asArray)
1063                     {
1064                         uint nodeIndex = cast(uint)n.asNumber;
1065                         if (nodeIndex < nodes.length)
1066                             sceneObj.nodes.insertBack(nodes[nodeIndex]);
1067                     }
1068                 }
1069                 
1070                 scenes.insertBack(sceneObj);
1071             }
1072         }
1073     }
1074     
1075     override bool loadThreadUnsafePart()
1076     {
1077         foreach(me; meshes)
1078         {
1079             foreach(p; me.primitives)
1080                 p.prepareVAO();
1081         }
1082         
1083         foreach(img; images)
1084         {
1085             img.loadThreadUnsafePart();
1086         }
1087         
1088         return true;
1089     }
1090     
1091     void markTransparentEntities()
1092     {
1093         foreach(node; nodes)
1094         {
1095             if (node.mesh)
1096             {
1097                 foreach(primitive; node.mesh.primitives)
1098                 {
1099                     Material material = primitive.material;
1100                     if (material)
1101                     {
1102                         Texture diffuseTex = material.diffuse.texture;
1103                         if (diffuseTex)
1104                         {
1105                             if (diffuseTex.image.pixelFormat == IntegerPixelFormat.RGBA8)
1106                             {
1107                                 material.blending = Transparent;
1108                                 node.entity.transparent = true;
1109                             }
1110                         }
1111                         else
1112                         {
1113                             if (material.diffuse.asVector4f.a < 1.0f)
1114                             {
1115                                 material.blending = Transparent;
1116                                 node.entity.transparent = true;
1117                             }
1118                         }
1119                         
1120                         if (material.transparency.asFloat < 1.0f)
1121                         {
1122                             material.blending = Transparent;
1123                             node.entity.transparent = true;
1124                         }
1125                     }
1126                 }
1127             }
1128         }
1129     }
1130     
1131     override void release()
1132     {
1133         foreach(b; buffers)
1134             deleteOwnedObject(b);
1135         buffers.free();
1136         
1137         foreach(bv; bufferViews)
1138             deleteOwnedObject(bv);
1139         bufferViews.free();
1140         
1141         foreach(ac; accessors)
1142             deleteOwnedObject(ac);
1143         accessors.free();
1144         
1145         foreach(me; meshes)
1146             deleteOwnedObject(me);
1147         meshes.free();
1148         
1149         foreach(im; images)
1150             deleteOwnedObject(im);
1151         images.free();
1152         
1153         foreach(no; nodes)
1154             deleteOwnedObject(no);
1155         nodes.free();
1156         
1157         foreach(sc; scenes)
1158             deleteOwnedObject(sc);
1159         scenes.free();
1160         
1161         textures.free();
1162         materials.free();
1163         
1164         Delete(doc);
1165         str.free();
1166     }
1167     
1168     int opApply(scope int delegate(Triangle t) dg)
1169     {
1170         int result = 0;
1171         
1172         foreach(node; nodes)
1173         {
1174             auto mat = node.entity.absoluteTransformation;
1175             
1176             if (node.mesh)
1177             {
1178                 foreach(primitive; node.mesh.primitives)
1179                 {
1180                     GLTFAccessor pa = primitive.positionAccessor;
1181                     GLTFAccessor na = primitive.normalAccessor;
1182                     GLTFAccessor ia = primitive.indexAccessor;
1183                     
1184                     Vector3f* positions = cast(Vector3f*)(pa.bufferView.slice.ptr + pa.byteOffset);
1185                     size_t numPositions = pa.count;
1186                     
1187                     Vector3f* normals = cast(Vector3f*)(na.bufferView.slice.ptr + na.byteOffset);
1188                     size_t numNormals = na.count;
1189                     
1190                     ubyte* indicesStart = ia.bufferView.slice.ptr + ia.byteOffset;
1191                     size_t numTriangles = ia.count / 3;
1192                     
1193                     size_t indexStride;
1194                     if (ia.componentType == GL_UNSIGNED_BYTE)
1195                         indexStride = 1;
1196                     else if (ia.componentType == GL_UNSIGNED_SHORT)
1197                         indexStride = 2;
1198                     else if (ia.componentType == GL_UNSIGNED_INT)
1199                         indexStride = 4;
1200                     
1201                     foreach(i; 0..numTriangles)
1202                     {
1203                         ubyte* ptr = indicesStart + i * 3 * indexStride;
1204                         
1205                         size_t[3] indices;
1206                         if (ia.componentType == GL_UNSIGNED_BYTE)
1207                         {
1208                             indices[0] = ptr[0];
1209                             indices[1] = ptr[1];
1210                             indices[2] = ptr[2];
1211                         }
1212                         else if (ia.componentType == GL_UNSIGNED_SHORT)
1213                         {
1214                             indices[0] = *cast(ushort*)ptr;
1215                             indices[1] = *cast(ushort*)(ptr+2);
1216                             indices[2] = *cast(ushort*)(ptr+4);
1217                         }
1218                         else if (ia.componentType == GL_UNSIGNED_INT)
1219                         {
1220                             indices[0] = *cast(uint*)ptr;
1221                             indices[1] = *cast(uint*)(ptr+4);
1222                             indices[2] = *cast(uint*)(ptr+8);
1223                         }
1224                         
1225                         Triangle tri;
1226                         tri.v[0] = positions[indices[0]] * mat;
1227                         tri.v[1] = positions[indices[1]] * mat;
1228                         tri.v[2] = positions[indices[2]] * mat;
1229                         tri.n[0] = mat.rotate(normals[indices[0]]);
1230                         tri.n[1] = mat.rotate(normals[indices[1]]);
1231                         tri.n[2] = mat.rotate(normals[indices[2]]);
1232                         tri.normal = (tri.n[0] + tri.n[1] + tri.n[2]) / 3.0f;
1233                         
1234                         result = dg(tri);
1235                         if (result)
1236                             return result;
1237                     }
1238                 }
1239             }
1240         }
1241         
1242         return result;
1243     }
1244 }
1245 
1246 string nameFromMimeType(string mime)
1247 {
1248     string name;
1249     switch(mime)
1250     {
1251         case "image/jpeg": name = "undefined.jpg"; break;
1252         case "image/png": name = "undefined.png"; break;
1253         case "image/tga": name = "undefined.tga"; break;
1254         case "image/targa": name = "undefined.tga"; break;
1255         case "image/bmp": name = "undefined.bmp"; break;
1256         case "image/vnd.radiance": name = "undefined.hdr"; break;
1257         case "image/x-hdr": name = "undefined.hdr"; break;
1258         case "image/x-dds": name = "undefined.dds"; break;
1259         default: name = ""; break;
1260     }
1261     return name;
1262 }
1263 
1264 void decomposeMatrix(
1265     Matrix4x4f m,
1266     out Vector3f position,
1267     out Quaternionf rotation,
1268     out Vector3f scale)
1269 {
1270     position = Vector3f(m[12], m[13], m[14]);
1271     
1272     float sx = sqrt(m[0] * m[0] + m[1] * m[1] + m[2] * m[2]);
1273     float sy = sqrt(m[4] * m[4] + m[5] * m[5] + m[6] * m[6]);
1274     float sz = sqrt(m[8] * m[8] + m[9] * m[9] + m[10] * m[10]);
1275 
1276     if (m.determinant < 0)
1277         sx = -sx;
1278     
1279     scale = Vector3f(sx, sy, sz);
1280     
1281     float invSx = 1.0f / sx;
1282     float invSy = 1.0f / sy;
1283     float invSz = 1.0f / sz;
1284     
1285     Matrix3x3f rotationMatrix = matrixf(
1286         m[0] * invSx, m[4] * invSy, m[8] * invSz,
1287         m[1] * invSx, m[5] * invSy, m[9] * invSz,
1288         m[2] * invSx, m[6] * invSy, m[10] * invSz
1289     );
1290     
1291     rotation = Quaternionf.fromMatrix(matrix3x3to4x4(rotationMatrix));
1292 }