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 }