UNPKG

nanogl-gltf

Version:
117 lines (116 loc) 4.76 kB
import Image, { filterHasMipmap } from "../../elements/Image"; import Texture from "../../elements/Texture"; import { createNativeSignal } from "../../lib/cancellation"; import GltfTypes from "../../types/GltfTypes"; const EXT_ID = 'KHR_texture_basisu'; class BasisImage extends Image { constructor(_decoder) { super(); this._decoder = _decoder; this._decodePromise = null; } get hasAlpha() { return false; } async loadImage(gltfLoader) { this._basisFile = await this.loadBuffer(gltfLoader.abortSignal); } async loadBuffer(abortSignal) { if (this.bufferView) { // mimeType is guaranted here const ptr = this.bufferView.getByteOffset(); return this.bufferView.buffer._bytes.slice(ptr, ptr + this.bufferView.byteLength); } else { // assume uri is defained as uri or data uri const signal = createNativeSignal(abortSignal); const request = await fetch(this.resolvedUri, { signal }); const buffer = await request.arrayBuffer(); return buffer; } } decode(gl) { if (this._decodePromise === null) { this._decodePromise = this._decoder.decode(gl, this._basisFile); } return this._decodePromise; } async setupTexture(texture, wrapS, wrapT, minFilter, magFilter) { const gl = texture.gl; const { buffer, webglFormat, mipLevels } = await this.decode(gl); gl.bindTexture(gl.TEXTURE_2D, texture.id); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, mipLevels.length > 1 || webglFormat.uncompressed ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR); let levelData = null; for (let mipLevel of mipLevels) { if (!webglFormat.uncompressed) { levelData = new Uint8Array(buffer, mipLevel.offset, mipLevel.size); gl.compressedTexImage2D(gl.TEXTURE_2D, mipLevel.level, webglFormat.format, mipLevel.width, mipLevel.height, 0, levelData); } else { switch (webglFormat.type) { case WebGLRenderingContext.UNSIGNED_SHORT_4_4_4_4: case WebGLRenderingContext.UNSIGNED_SHORT_5_5_5_1: case WebGLRenderingContext.UNSIGNED_SHORT_5_6_5: levelData = new Uint16Array(buffer, mipLevel.offset, mipLevel.size / 2); break; default: levelData = new Uint8Array(buffer, mipLevel.offset, mipLevel.size); break; } gl.texImage2D(gl.TEXTURE_2D, mipLevel.level, webglFormat.format, mipLevel.width, mipLevel.height, 0, webglFormat.format, webglFormat.type, levelData); } } if (filterHasMipmap(minFilter) && webglFormat.uncompressed && mipLevels.length == 1) { gl.generateMipmap(gl.TEXTURE_2D); } } } class BasisTexture extends Texture { async parse(gltfLoader, data) { if (data.sampler !== undefined) { this.sampler = await gltfLoader.getElement(GltfTypes.SAMPLER, data.sampler); } const sourceId = data.extensions[EXT_ID].source; this.source = await gltfLoader.getElement(GltfTypes.IMAGE, sourceId); } } class Instance { constructor(gltfLoader, _decoder) { this._decoder = _decoder; this.name = EXT_ID; this.priority = 1; this.loader = gltfLoader; } acceptElement(data, element) { return null; } loadElement(data) { var _a; if (data.gltftype === GltfTypes.IMAGE && (data.mimeType === 'image/ktx2' || ((_a = data.uri) === null || _a === void 0 ? void 0 : _a.endsWith('ktx2')))) { const basisImg = new BasisImage(this._decoder); return basisImg.parse(this.loader, data).then(() => basisImg); } if (data.gltftype === GltfTypes.TEXTURE && data.extensions && data.extensions[EXT_ID]) { const tex = new BasisTexture(); return tex.parse(this.loader, data).then(() => tex); } return null; } } /** * This extension allows Textures to use the basisu format and Images to have an uri with a .ktx2 extension. * This format is useful to reduce texture size and memory usage. */ export default class KHR_texture_basisu { /** * @param decoder The decoder to use to decode the basis file */ constructor(decoder) { this.decoder = decoder; this.name = EXT_ID; } createInstance(gltfLoader) { return new Instance(gltfLoader, this.decoder); } }