threepipe
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gltf.ts 3.1KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import type {BlobExt} from '../assetmanager'
  2. /**
  3. * Returns a buffer aligned to 4-byte boundary.
  4. * https://github.com/mrdoob/three.js/blob/4dbd0065f2ec29b89c250d8582f61e9f4792e077/examples/jsm/exporters/GLTFExporter.js#L381
  5. * @param arrayBuffer Buffer to pad
  6. * @param paddingByte (Optional)
  7. * @returns The same buffer if it's already aligned to 4-byte boundary or a new buffer
  8. */
  9. function getPaddedArrayBuffer(arrayBuffer: ArrayBuffer, paddingByte = 0): ArrayBuffer {
  10. const paddedLength = getPaddedBufferSize(arrayBuffer.byteLength)
  11. if (paddedLength !== arrayBuffer.byteLength) {
  12. const array = new Uint8Array(paddedLength)
  13. array.set(new Uint8Array(arrayBuffer))
  14. if (paddingByte !== 0) {
  15. for (let i = arrayBuffer.byteLength; i < paddedLength; i++) {
  16. array[ i ] = paddingByte
  17. }
  18. }
  19. return array.buffer
  20. }
  21. return arrayBuffer
  22. }
  23. /**
  24. * Get the required size + padding for a buffer, rounded to the next 4-byte boundary.
  25. * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
  26. *
  27. * @param bufferSize The size the original buffer.
  28. * @returns new buffer size with required padding.
  29. *
  30. */
  31. function getPaddedBufferSize(bufferSize: number) {
  32. return Math.ceil(bufferSize / 4) * 4
  33. }
  34. // GLB constants
  35. // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
  36. const GLB_HEADER_BYTES = 12
  37. const GLB_HEADER_MAGIC = 0x46546C67
  38. const GLB_VERSION = 2
  39. const GLB_CHUNK_PREFIX_BYTES = 8
  40. const GLB_CHUNK_TYPE_JSON = 0x4E4F534A
  41. const GLB_CHUNK_TYPE_BIN = 0x004E4942
  42. // https://github.com/mrdoob/three.js/blob/4dbd0065f2ec29b89c250d8582f61e9f4792e077/examples/jsm/exporters/GLTFExporter.js#L558
  43. export function makeGLBFile(buffers: ArrayBuffer, json: any): BlobExt {
  44. // Binary chunk.
  45. const binaryChunk = getPaddedArrayBuffer(buffers)
  46. const binaryChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES))
  47. binaryChunkPrefix.setUint32(0, binaryChunk.byteLength, true)
  48. binaryChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_BIN, true)
  49. // JSON chunk.
  50. const jsonChunk = getPaddedArrayBuffer(new TextEncoder().encode(JSON.stringify(json || {})).buffer, 0x20)
  51. const jsonChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES))
  52. jsonChunkPrefix.setUint32(0, jsonChunk.byteLength, true)
  53. jsonChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_JSON, true)
  54. // GLB header.
  55. const header = new ArrayBuffer(GLB_HEADER_BYTES)
  56. const headerView = new DataView(header)
  57. headerView.setUint32(0, GLB_HEADER_MAGIC, true)
  58. headerView.setUint32(4, GLB_VERSION, true)
  59. const totalByteLength = GLB_HEADER_BYTES
  60. + jsonChunkPrefix.byteLength + jsonChunk.byteLength
  61. + binaryChunkPrefix.byteLength + binaryChunk.byteLength
  62. headerView.setUint32(8, totalByteLength, true)
  63. const glbBlob: BlobExt = new Blob([
  64. header,
  65. jsonChunkPrefix,
  66. jsonChunk,
  67. binaryChunkPrefix,
  68. binaryChunk,
  69. ], {type: 'model/gltf+binary'}) as any
  70. glbBlob.ext = 'glb'
  71. return glbBlob
  72. }