1 /*
2 Copyright (c) 2021-2022 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 uri = im["uri"].asString;
613                     TextureAsset textureAsset = New!TextureAsset(assetManager);
614                     
615                     ImageFormatInfo info = assetManager.detectBase64Image(uri);
616                     if (info.format.length)
617                     {
618                         auto encoded = uri[info.prefix.length..$];
619                         auto decodedLength = Base64.decodeLength(encoded.length);
620                         auto array = New!(ubyte[])(decodedLength);
621                         auto decoded = Base64.decode(encoded, array);
622                         bool res = assetManager.loadAssetThreadSafePart(textureAsset, decoded, info.dummyFilename);
623                         if (!res)
624                             writeln("Warning: failed to load Base64-encoded image");
625                         Delete(array);
626                     }
627                     else
628                     {
629                         String imgFilename = String(rootDir);
630                         imgFilename ~= "/";
631                         imgFilename ~= uri;
632                         string assetFilename = imgFilename.toString;
633                         
634                         FileStat fstat;
635                         if (fs.stat(assetFilename, fstat))
636                         {
637                             bool res = assetManager.loadAssetThreadSafePart(textureAsset, assetFilename);
638                             if (!res)
639                                 writeln("Warning: failed to load \"", imgFilename, "\"");
640                         }
641                         else
642                             writeln("Warning: image file \"", imgFilename, "\" not found");
643                         
644                         imgFilename.free();
645                     }
646                     
647                     images.insertBack(textureAsset);
648                 }
649                 else if ("bufferView" in im)
650                 {
651                     uint bufferViewIndex = cast(uint)im["bufferView"].asNumber;
652                     
653                     auto textureAsset = New!TextureAsset(this);
654                     
655                     if (bufferViewIndex < bufferViews.length)
656                     {
657                         auto bv = bufferViews[bufferViewIndex];
658                         string mimeType = "";
659                         if ("mimeType" in im)
660                             mimeType = im["mimeType"].asString;
661                         if (mimeType == "")
662                             writeln("Warning: image MIME type missing");
663                         else
664                         {
665                             string name = nameFromMimeType(mimeType);
666                             if (name == "")
667                             {
668                                 writeln("Warning: unsupported image MIME type ", mimeType);
669                             }
670                             else
671                             {
672                                 
673                                 bool res = assetManager.loadAssetThreadSafePart(textureAsset, bv.slice, name);
674                                 if (!res)
675                                     writeln("Warning: failed to load image");
676                             }
677                         }
678                     }
679                     else
680                     {
681                         writeln("Warning: can't create image from nonexistent buffer view ", bufferViewIndex);
682                     }
683                     
684                     images.insertBack(textureAsset);
685                 }
686             }
687         }
688     }
689     
690     // TODO: loadSamplers
691     
692     void loadTextures(JSONValue root)
693     {
694         if ("textures" in root.asObject)
695         {
696             foreach(i, tex; root.asObject["textures"].asArray)
697             {
698                 auto te = tex.asObject;
699                 
700                 if ("source" in te)
701                 {
702                     uint imageIndex = cast(uint)te["source"].asNumber;
703                     TextureAsset img;
704                     if (imageIndex < images.length)
705                         img = images[imageIndex];
706                     else
707                         writeln("Warning: can't create texture for nonexistent image ", imageIndex);
708                     
709                     if (img !is null)
710                     {
711                         Texture texture = img.texture;
712                         textures.insertBack(texture);
713                     }
714                     else
715                     {
716                         Texture texture;
717                         textures.insertBack(texture);
718                     }
719                 }
720                 
721                 // TODO: sampler
722             }
723         }
724     }
725     
726     void loadMaterials(JSONValue root)
727     {
728         if ("materials" in root.asObject)
729         {
730             foreach(i, mat; root.asObject["materials"].asArray)
731             {
732                 auto ma = mat.asObject;
733                 
734                 Material material = New!Material(this);
735                 
736                 if ("pbrMetallicRoughness" in ma)
737                 {
738                     auto pbr = ma["pbrMetallicRoughness"].asObject;
739                     
740                     Color4f baseColorFactor = Color4f(1.0f, 1.0f, 1.0f, 1.0f);
741                     if (pbr && "baseColorFactor" in pbr)
742                     {
743                         baseColorFactor = pbr["baseColorFactor"].asColor;
744                         
745                         if (baseColorFactor.a < 1.0f)
746                             material.blendMode = Transparent;
747                         
748                         material.baseColorFactor = baseColorFactor;
749                     }
750                     
751                     if (pbr && "baseColorTexture" in pbr)
752                     {
753                         auto bct = pbr["baseColorTexture"].asObject;
754                         if ("index" in bct)
755                         {
756                             uint baseColorTexIndex = cast(uint)bct["index"].asNumber;
757                             if (baseColorTexIndex < textures.length)
758                             {
759                                 Texture baseColorTex = textures[baseColorTexIndex];
760                                 if (baseColorTex)
761                                 {
762                                     material.baseColorTexture = baseColorTex;
763                                 }
764                             }
765                         }
766                         
767                         material.opacity = baseColorFactor.a;
768                     }
769                     
770                     if (pbr && "metallicRoughnessTexture" in pbr)
771                     {
772                         uint metallicRoughnessTexIndex = cast(uint)pbr["metallicRoughnessTexture"].asObject["index"].asNumber;
773                         if (metallicRoughnessTexIndex < textures.length)
774                         {
775                             Texture metallicRoughnessTex = textures[metallicRoughnessTexIndex];
776                             if (metallicRoughnessTex)
777                                 material.roughnessMetallicTexture = metallicRoughnessTex;
778                         }
779                     }
780                     else
781                     {
782                         if (pbr && "metallicFactor" in pbr)
783                         {
784                             material.metallicFactor = pbr["metallicFactor"].asNumber;
785                         }
786                         else
787                         {
788                             material.metallicFactor = 1.0f;
789                         }
790                         
791                         if (pbr && "roughnessFactor" in pbr)
792                         {
793                             material.roughnessFactor = pbr["roughnessFactor"].asNumber;
794                         }
795                         else
796                         {
797                             material.roughnessFactor = 1.0f;
798                         }
799                     }
800                 }
801                 else if ("extensions" in ma)
802                 {
803                     auto extensions = ma["extensions"].asObject;
804                     
805                     if ("KHR_materials_pbrSpecularGlossiness" in extensions)
806                     {
807                         auto pbr = extensions["KHR_materials_pbrSpecularGlossiness"].asObject;
808                         
809                         if (pbr && "diffuseTexture" in pbr)
810                         {
811                             auto dt = pbr["diffuseTexture"].asObject;
812                             if ("index" in dt)
813                             {
814                                 uint diffuseTexIndex = cast(uint)dt["index"].asNumber;
815                                 if (diffuseTexIndex < textures.length)
816                                 {
817                                     Texture diffuseTex = textures[diffuseTexIndex];
818                                     if (diffuseTex)
819                                     {
820                                         material.baseColorTexture = diffuseTex;
821                                     }
822                                 }
823                             }
824                         }
825                     }
826                 }
827                 
828                 if ("normalTexture" in ma)
829                 {
830                     uint normalTexIndex = cast(uint)ma["normalTexture"].asObject["index"].asNumber;
831                     if (normalTexIndex < textures.length)
832                     {
833                         Texture normalTex = textures[normalTexIndex];
834                         if (normalTex)
835                             material.normalTexture = normalTex;
836                     }
837                 }
838                 
839                 if ("emissiveTexture" in ma)
840                 {
841                     uint emissiveTexIndex = cast(uint)ma["emissiveTexture"].asObject["index"].asNumber;
842                     if (emissiveTexIndex < textures.length)
843                     {
844                         Texture emissiveTex = textures[emissiveTexIndex];
845                         if (emissiveTex)
846                             material.emissionTexture = emissiveTex;
847                     }
848                 }
849                 
850                 if ("emissiveFactor" in ma)
851                 {
852                     Color4f emissiveFactor = ma["emissiveFactor"].asColor;
853                     material.emissionFactor = emissiveFactor;
854                 }
855                 
856                 if ("doubleSided" in ma)
857                 {
858                     uint doubleSided = cast(uint)ma["doubleSided"].asNumber;
859                     if (doubleSided > 0)
860                         material.useCulling = false;
861                     else
862                         material.useCulling = true;
863                 }
864                 else
865                 {
866                     material.useCulling = true;
867                 }
868                 
869                 if ("alphaMode" in ma)
870                 {
871                     auto alphaMode = ma["alphaMode"].asString;
872                     if (alphaMode == "BLEND")
873                     {
874                         material.blendMode = Transparent;
875                     }
876                 }
877                 
878                 if ("alphaCutoff" in ma)
879                 {
880                     material.alphaTestThreshold = ma["alphaCutoff"].asNumber;
881                 }
882                 
883                 materials.insertBack(material);
884             }
885         }
886     }
887     
888     void loadMeshes(JSONValue root)
889     {
890         if ("meshes" in root.asObject)
891         {
892             foreach(i, mesh; root.asObject["meshes"].asArray)
893             {
894                 auto m = mesh.asObject;
895                 
896                 GLTFMesh me = New!GLTFMesh(this);
897                 
898                 if ("primitives" in m)
899                 {
900                     foreach(prim; m["primitives"].asArray)
901                     {
902                         auto p = prim.asObject;
903                         
904                         GLTFAccessor positionAccessor;
905                         GLTFAccessor normalAccessor;
906                         GLTFAccessor texCoord0Accessor;
907                         GLTFAccessor indexAccessor;
908                         
909                         if ("attributes" in p)
910                         {
911                             auto attributes = p["attributes"].asObject;
912                             
913                             if ("POSITION" in attributes)
914                             {
915                                 uint positionsAccessorIndex = cast(uint)attributes["POSITION"].asNumber;
916                                 if (positionsAccessorIndex < accessors.length)
917                                     positionAccessor = accessors[positionsAccessorIndex];
918                                 else
919                                     writeln("Warning: can't create position attributes for nonexistent accessor ", positionsAccessorIndex);
920                             }
921                             
922                             if ("NORMAL" in attributes)
923                             {
924                                 uint normalsAccessorIndex = cast(uint)attributes["NORMAL"].asNumber;
925                                 if (normalsAccessorIndex < accessors.length)
926                                     normalAccessor = accessors[normalsAccessorIndex];
927                                 else
928                                     writeln("Warning: can't create normal attributes for nonexistent accessor ", normalsAccessorIndex);
929                             }
930                             
931                             if ("TEXCOORD_0" in attributes)
932                             {
933                                 uint texCoord0AccessorIndex = cast(uint)attributes["TEXCOORD_0"].asNumber;
934                                 if (texCoord0AccessorIndex < accessors.length)
935                                     texCoord0Accessor = accessors[texCoord0AccessorIndex];
936                                 else
937                                     writeln("Warning: can't create texCoord0 attributes for nonexistent accessor ", texCoord0AccessorIndex);
938                             }
939                         }
940                         
941                         if ("indices" in p)
942                         {
943                             uint indicesAccessorIndex = cast(uint)p["indices"].asNumber;
944                             if (indicesAccessorIndex < accessors.length)
945                                 indexAccessor = accessors[indicesAccessorIndex];
946                             else
947                                 writeln("Warning: can't create indices for nonexistent accessor ", indicesAccessorIndex);
948                         }
949                         
950                         Material material;
951                         if ("material" in p)
952                         {
953                             uint materialIndex = cast(uint)p["material"].asNumber;
954                             if (materialIndex < materials.length)
955                                 material = materials[materialIndex];
956                             else
957                                 writeln("Warning: nonexistent material ", materialIndex);
958                         }
959                         
960                         if (positionAccessor is null)
961                         {
962                             writeln("Warning: mesh ", i, " lacks vertex position attributes");
963                         }
964                         if (normalAccessor is null)
965                         {
966                             writeln("Warning: mesh ", i, " lacks vertex normal attributes");
967                         }
968                         if (texCoord0Accessor is null)
969                         {
970                             writeln("Warning: mesh ", i, " lacks vertex texCoord0 attributes");
971                         }
972                         if (indexAccessor is null)
973                         {
974                             writeln("Warning: mesh ", i, " lacks indices");
975                         }
976                         
977                         GLTFMeshPrimitive pr = New!GLTFMeshPrimitive(positionAccessor, normalAccessor, texCoord0Accessor, indexAccessor, material, this);
978                         me.primitives.insertBack(pr);
979                     }
980                 }
981                 
982                 meshes.insertBack(me);
983             }
984         }
985     }
986     
987     void loadNodes(JSONValue root)
988     {
989         if ("nodes" in root.asObject)
990         {
991             foreach(i, n; root.asObject["nodes"].asArray)
992             {
993                 auto node = n.asObject;
994                 
995                 GLTFNode nodeObj = New!GLTFNode(this);
996                 nodeObj.entity.setParent(rootEntity);
997                 
998                 if ("mesh" in node)
999                 {
1000                     uint meshIndex = cast(uint)node["mesh"].asNumber;
1001                     if (meshIndex < meshes.length)
1002                     {
1003                         GLTFMesh mesh = meshes[meshIndex];
1004                         nodeObj.mesh = mesh;
1005                         nodeObj.entity.drawable = mesh;
1006                     }
1007                     else
1008                         writeln("Warning: mesh ", meshIndex, " doesn't exist");
1009                 }
1010                 
1011                 if ("matrix" in node)
1012                 {
1013                     nodeObj.transformation = node["matrix"].asMatrix;
1014                     auto nodeComponent = New!StaticTransformComponent(assetManager.eventManager, nodeObj.entity, nodeObj.transformation);
1015                 }
1016                 else
1017                 {
1018                     Vector3f position = Vector3f(0.0f, 0.0f, 0.0f);
1019                     Quaternionf rotation = Quaternionf.identity;
1020                     Vector3f scale = Vector3f(1.0f, 1.0f, 1.0f);
1021                     
1022                     if ("translation" in node)
1023                     {
1024                         position = node["translation"].asVector;
1025                     }
1026                     if ("rotation" in node)
1027                     {
1028                         rotation = node["rotation"].asQuaternion;
1029                     }
1030                     if ("scale" in node)
1031                     {
1032                         scale = node["scale"].asVector;
1033                     }
1034                     
1035                     nodeObj.entity.position = position;
1036                     nodeObj.entity.rotation = rotation;
1037                     nodeObj.entity.scaling = scale;
1038                     
1039                     nodeObj.transformation =
1040                         translationMatrix(position) *
1041                         rotation.toMatrix4x4 *
1042                         scaleMatrix(scale);
1043                 }
1044                 
1045                 if ("children" in node)
1046                 {
1047                     auto children = node["children"].asArray;
1048                     nodeObj.children = New!(size_t[])(children.length);
1049                     
1050                     foreach(ci, c; children)
1051                     {
1052                         uint childIndex = cast(uint)c.asNumber;
1053                         nodeObj.children[ci] = childIndex;
1054                     }
1055                 }
1056                 
1057                 nodes.insertBack(nodeObj);
1058             }
1059         }
1060         
1061         foreach(node; nodes)
1062         {
1063             foreach(i; node.children)
1064             {
1065                 GLTFNode child = nodes[i];
1066                 node.entity.addChild(child.entity);
1067             }
1068         }
1069         
1070         rootEntity.updateTransformationTopDown();
1071     }
1072     
1073     void loadScenes(JSONValue root)
1074     {
1075         if ("scenes" in root.asObject)
1076         {
1077             foreach(i, s; root.asObject["scenes"].asArray)
1078             {
1079                 auto scene = s.asObject;
1080                 
1081                 GLTFScene sceneObj = New!GLTFScene(this);
1082                 
1083                 if ("name" in scene)
1084                 {
1085                     sceneObj.name = scene["name"].asString;
1086                 }
1087                 
1088                 if ("nodes" in scene)
1089                 {
1090                     foreach(ni, n; scene["nodes"].asArray)
1091                     {
1092                         uint nodeIndex = cast(uint)n.asNumber;
1093                         if (nodeIndex < nodes.length)
1094                             sceneObj.nodes.insertBack(nodes[nodeIndex]);
1095                     }
1096                 }
1097                 
1098                 scenes.insertBack(sceneObj);
1099             }
1100         }
1101     }
1102     
1103     override bool loadThreadUnsafePart()
1104     {
1105         foreach(me; meshes)
1106         {
1107             foreach(p; me.primitives)
1108                 p.prepareVAO();
1109         }
1110         
1111         foreach(img; images)
1112         {
1113             img.loadThreadUnsafePart();
1114         }
1115         
1116         return true;
1117     }
1118     
1119     void markTransparentEntities()
1120     {
1121         foreach(node; nodes)
1122         {
1123             if (node.mesh)
1124             {
1125                 foreach(primitive; node.mesh.primitives)
1126                 {
1127                     Material material = primitive.material;
1128                     if (material)
1129                     {
1130                         if (material.blendMode == Transparent)
1131                         {
1132                             node.entity.transparent = true;
1133                         }
1134                         
1135                         /*
1136                         Texture diffuseTex = material.baseColorTexture;
1137                         if (diffuseTex)
1138                         {
1139                             if (diffuseTex.hasAlpha)
1140                             {
1141                                 material.blendMode = Transparent;
1142                                 node.entity.transparent = true;
1143                             }
1144                         }
1145                         else if (material.baseColorFactor.a < 1.0f || material.opacity < 1.0f)
1146                         {
1147                             material.blendMode = Transparent;
1148                             node.entity.transparent = true;
1149                         }
1150                         */
1151                     }
1152                 }
1153             }
1154         }
1155     }
1156     
1157     override void release()
1158     {
1159         foreach(b; buffers)
1160             deleteOwnedObject(b);
1161         buffers.free();
1162         
1163         foreach(bv; bufferViews)
1164             deleteOwnedObject(bv);
1165         bufferViews.free();
1166         
1167         foreach(ac; accessors)
1168             deleteOwnedObject(ac);
1169         accessors.free();
1170         
1171         foreach(me; meshes)
1172             deleteOwnedObject(me);
1173         meshes.free();
1174         
1175         foreach(im; images)
1176             deleteOwnedObject(im);
1177         images.free();
1178         
1179         foreach(no; nodes)
1180             deleteOwnedObject(no);
1181         nodes.free();
1182         
1183         foreach(sc; scenes)
1184             deleteOwnedObject(sc);
1185         scenes.free();
1186         
1187         textures.free();
1188         materials.free();
1189         
1190         Delete(doc);
1191         str.free();
1192     }
1193     
1194     int opApply(scope int delegate(Triangle t) dg)
1195     {
1196         int result = 0;
1197         
1198         foreach(node; nodes)
1199         {
1200             auto mat = node.entity.absoluteTransformation;
1201             
1202             if (node.mesh)
1203             {
1204                 foreach(primitive; node.mesh.primitives)
1205                 {
1206                     GLTFAccessor pa = primitive.positionAccessor;
1207                     GLTFAccessor na = primitive.normalAccessor;
1208                     GLTFAccessor ia = primitive.indexAccessor;
1209                     
1210                     Vector3f* positions = cast(Vector3f*)(pa.bufferView.slice.ptr + pa.byteOffset);
1211                     size_t numPositions = pa.count;
1212                     
1213                     Vector3f* normals = cast(Vector3f*)(na.bufferView.slice.ptr + na.byteOffset);
1214                     size_t numNormals = na.count;
1215                     
1216                     ubyte* indicesStart = ia.bufferView.slice.ptr + ia.byteOffset;
1217                     size_t numTriangles = ia.count / 3;
1218                     
1219                     size_t indexStride;
1220                     if (ia.componentType == GL_UNSIGNED_BYTE)
1221                         indexStride = 1;
1222                     else if (ia.componentType == GL_UNSIGNED_SHORT)
1223                         indexStride = 2;
1224                     else if (ia.componentType == GL_UNSIGNED_INT)
1225                         indexStride = 4;
1226                     
1227                     foreach(i; 0..numTriangles)
1228                     {
1229                         ubyte* ptr = indicesStart + i * 3 * indexStride;
1230                         
1231                         size_t[3] indices;
1232                         if (ia.componentType == GL_UNSIGNED_BYTE)
1233                         {
1234                             indices[0] = ptr[0];
1235                             indices[1] = ptr[1];
1236                             indices[2] = ptr[2];
1237                         }
1238                         else if (ia.componentType == GL_UNSIGNED_SHORT)
1239                         {
1240                             indices[0] = *cast(ushort*)ptr;
1241                             indices[1] = *cast(ushort*)(ptr+2);
1242                             indices[2] = *cast(ushort*)(ptr+4);
1243                         }
1244                         else if (ia.componentType == GL_UNSIGNED_INT)
1245                         {
1246                             indices[0] = *cast(uint*)ptr;
1247                             indices[1] = *cast(uint*)(ptr+4);
1248                             indices[2] = *cast(uint*)(ptr+8);
1249                         }
1250                         
1251                         Triangle tri;
1252                         tri.v[0] = positions[indices[0]] * mat;
1253                         tri.v[1] = positions[indices[1]] * mat;
1254                         tri.v[2] = positions[indices[2]] * mat;
1255                         tri.n[0] = mat.rotate(normals[indices[0]]);
1256                         tri.n[1] = mat.rotate(normals[indices[1]]);
1257                         tri.n[2] = mat.rotate(normals[indices[2]]);
1258                         tri.normal = (tri.n[0] + tri.n[1] + tri.n[2]) / 3.0f;
1259                         
1260                         result = dg(tri);
1261                         if (result)
1262                             return result;
1263                     }
1264                 }
1265             }
1266         }
1267         
1268         return result;
1269     }
1270 }
1271 
1272 // TODO: generate random name instead of "undefined"
1273 string nameFromMimeType(string mime)
1274 {
1275     string name;
1276     switch(mime)
1277     {
1278         case "image/jpeg": name = "undefined.jpg"; break;
1279         case "image/png": name = "undefined.png"; break;
1280         case "image/tga": name = "undefined.tga"; break;
1281         case "image/targa": name = "undefined.tga"; break;
1282         case "image/bmp": name = "undefined.bmp"; break;
1283         case "image/vnd.radiance": name = "undefined.hdr"; break;
1284         case "image/x-hdr": name = "undefined.hdr"; break;
1285         case "image/x-dds": name = "undefined.dds"; break;
1286         default: name = ""; break;
1287     }
1288     return name;
1289 }
1290 
1291 void decomposeMatrix(
1292     Matrix4x4f m,
1293     out Vector3f position,
1294     out Quaternionf rotation,
1295     out Vector3f scale)
1296 {
1297     position = Vector3f(m[12], m[13], m[14]);
1298     
1299     float sx = sqrt(m[0] * m[0] + m[1] * m[1] + m[2] * m[2]);
1300     float sy = sqrt(m[4] * m[4] + m[5] * m[5] + m[6] * m[6]);
1301     float sz = sqrt(m[8] * m[8] + m[9] * m[9] + m[10] * m[10]);
1302 
1303     if (m.determinant < 0)
1304         sx = -sx;
1305     
1306     scale = Vector3f(sx, sy, sz);
1307     
1308     float invSx = 1.0f / sx;
1309     float invSy = 1.0f / sy;
1310     float invSz = 1.0f / sz;
1311     
1312     Matrix3x3f rotationMatrix = matrixf(
1313         m[0] * invSx, m[4] * invSy, m[8] * invSz,
1314         m[1] * invSx, m[5] * invSy, m[9] * invSz,
1315         m[2] * invSx, m[6] * invSy, m[10] * invSz
1316     );
1317     
1318     rotation = Quaternionf.fromMatrix(matrix3x3to4x4(rotationMatrix));
1319 }