UNPKG

threepipe

Version:

A 3D viewer framework built on top of three.js in TypeScript with a focus on quality rendering, modularity and extensibility.

88 lines (73 loc) 3.21 kB
import type {BlobExt} from '../assetmanager' /** * Returns a buffer aligned to 4-byte boundary. * https://github.com/mrdoob/three.js/blob/4dbd0065f2ec29b89c250d8582f61e9f4792e077/examples/jsm/exporters/GLTFExporter.js#L381 * @param arrayBuffer Buffer to pad * @param paddingByte (Optional) * @returns The same buffer if it's already aligned to 4-byte boundary or a new buffer */ function getPaddedArrayBuffer(arrayBuffer: Uint8Array<ArrayBuffer>, paddingByte = 0): ArrayBuffer { const paddedLength = getPaddedBufferSize(arrayBuffer.byteLength) if (paddedLength !== arrayBuffer.byteLength) { const array = new Uint8Array(paddedLength) array.set(new Uint8Array(arrayBuffer)) if (paddingByte !== 0) { for (let i = arrayBuffer.byteLength; i < paddedLength; i++) { array[ i ] = paddingByte } } return array.buffer } return arrayBuffer.buffer } /** * Get the required size + padding for a buffer, rounded to the next 4-byte boundary. * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment * * @param bufferSize The size the original buffer. * @returns new buffer size with required padding. * */ function getPaddedBufferSize(bufferSize: number) { return Math.ceil(bufferSize / 4) * 4 } // GLB constants // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification const GLB_HEADER_BYTES = 12 const GLB_HEADER_MAGIC = 0x46546C67 const GLB_VERSION = 2 const GLB_CHUNK_PREFIX_BYTES = 8 const GLB_CHUNK_TYPE_JSON = 0x4E4F534A const GLB_CHUNK_TYPE_BIN = 0x004E4942 // https://github.com/mrdoob/three.js/blob/4dbd0065f2ec29b89c250d8582f61e9f4792e077/examples/jsm/exporters/GLTFExporter.js#L558 export function makeGLBFile(buffers: Uint8Array<ArrayBuffer>, json: any): BlobExt { // Binary chunk. const binaryChunk = getPaddedArrayBuffer(buffers) const binaryChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES)) binaryChunkPrefix.setUint32(0, binaryChunk.byteLength, true) binaryChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_BIN, true) // JSON chunk. const buffer1 = new TextEncoder().encode(JSON.stringify(json || {})) as Uint8Array<ArrayBuffer> const jsonChunk = getPaddedArrayBuffer(buffer1, 0x20) const jsonChunkPrefix = new DataView(new ArrayBuffer(GLB_CHUNK_PREFIX_BYTES)) jsonChunkPrefix.setUint32(0, jsonChunk.byteLength, true) jsonChunkPrefix.setUint32(4, GLB_CHUNK_TYPE_JSON, true) // GLB header. const header = new ArrayBuffer(GLB_HEADER_BYTES) const headerView = new DataView(header) headerView.setUint32(0, GLB_HEADER_MAGIC, true) headerView.setUint32(4, GLB_VERSION, true) const totalByteLength = GLB_HEADER_BYTES + jsonChunkPrefix.byteLength + jsonChunk.byteLength + binaryChunkPrefix.byteLength + binaryChunk.byteLength headerView.setUint32(8, totalByteLength, true) const glbBlob: BlobExt = new Blob([ header, jsonChunkPrefix, jsonChunk, binaryChunkPrefix, binaryChunk, ], {type: 'model/gltf+binary'}) as any glbBlob.ext = 'glb' return glbBlob }