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