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 }