parseGlb.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import addPipelineExtras from "./addPipelineExtras.js";
  2. import removeExtensionsUsed from "./removeExtensionsUsed.js";
  3. import defaultValue from "../../Core/defaultValue.js";
  4. import defined from "../../Core/defined.js";
  5. import getMagic from "../../Core/getMagic.js";
  6. import getStringFromTypedArray from "../../Core/getStringFromTypedArray.js";
  7. import RuntimeError from "../../Core/RuntimeError.js";
  8. const sizeOfUint32 = 4;
  9. /**
  10. * Convert a binary glTF to glTF.
  11. *
  12. * The returned glTF has pipeline extras included. The embedded binary data is stored in gltf.buffers[0].extras._pipeline.source.
  13. *
  14. * @param {Buffer} glb The glb data to parse.
  15. * @returns {object} A javascript object containing a glTF asset with pipeline extras included.
  16. *
  17. * @private
  18. */
  19. function parseGlb(glb) {
  20. // Check that the magic string is present
  21. const magic = getMagic(glb);
  22. if (magic !== "glTF") {
  23. throw new RuntimeError("File is not valid binary glTF");
  24. }
  25. const header = readHeader(glb, 0, 5);
  26. const version = header[1];
  27. if (version !== 1 && version !== 2) {
  28. throw new RuntimeError("Binary glTF version is not 1 or 2");
  29. }
  30. if (version === 1) {
  31. return parseGlbVersion1(glb, header);
  32. }
  33. return parseGlbVersion2(glb, header);
  34. }
  35. function readHeader(glb, byteOffset, count) {
  36. const dataView = new DataView(glb.buffer);
  37. const header = new Array(count);
  38. for (let i = 0; i < count; ++i) {
  39. header[i] = dataView.getUint32(
  40. glb.byteOffset + byteOffset + i * sizeOfUint32,
  41. true
  42. );
  43. }
  44. return header;
  45. }
  46. function parseGlbVersion1(glb, header) {
  47. const length = header[2];
  48. const contentLength = header[3];
  49. const contentFormat = header[4];
  50. // Check that the content format is 0, indicating that it is JSON
  51. if (contentFormat !== 0) {
  52. throw new RuntimeError("Binary glTF scene format is not JSON");
  53. }
  54. const jsonStart = 20;
  55. const binaryStart = jsonStart + contentLength;
  56. const contentString = getStringFromTypedArray(glb, jsonStart, contentLength);
  57. const gltf = JSON.parse(contentString);
  58. addPipelineExtras(gltf);
  59. const binaryBuffer = glb.subarray(binaryStart, length);
  60. const buffers = gltf.buffers;
  61. if (defined(buffers) && Object.keys(buffers).length > 0) {
  62. // In some older models, the binary glTF buffer is named KHR_binary_glTF
  63. const binaryGltfBuffer = defaultValue(
  64. buffers.binary_glTF,
  65. buffers.KHR_binary_glTF
  66. );
  67. if (defined(binaryGltfBuffer)) {
  68. binaryGltfBuffer.extras._pipeline.source = binaryBuffer;
  69. delete binaryGltfBuffer.uri;
  70. }
  71. }
  72. // Remove the KHR_binary_glTF extension
  73. removeExtensionsUsed(gltf, "KHR_binary_glTF");
  74. return gltf;
  75. }
  76. function parseGlbVersion2(glb, header) {
  77. const length = header[2];
  78. let byteOffset = 12;
  79. let gltf;
  80. let binaryBuffer;
  81. while (byteOffset < length) {
  82. const chunkHeader = readHeader(glb, byteOffset, 2);
  83. const chunkLength = chunkHeader[0];
  84. const chunkType = chunkHeader[1];
  85. byteOffset += 8;
  86. const chunkBuffer = glb.subarray(byteOffset, byteOffset + chunkLength);
  87. byteOffset += chunkLength;
  88. // Load JSON chunk
  89. if (chunkType === 0x4e4f534a) {
  90. const jsonString = getStringFromTypedArray(chunkBuffer);
  91. gltf = JSON.parse(jsonString);
  92. addPipelineExtras(gltf);
  93. }
  94. // Load Binary chunk
  95. else if (chunkType === 0x004e4942) {
  96. binaryBuffer = chunkBuffer;
  97. }
  98. }
  99. if (defined(gltf) && defined(binaryBuffer)) {
  100. const buffers = gltf.buffers;
  101. if (defined(buffers) && buffers.length > 0) {
  102. const buffer = buffers[0];
  103. buffer.extras._pipeline.source = binaryBuffer;
  104. }
  105. }
  106. return gltf;
  107. }
  108. export default parseGlb;