UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

144 lines (143 loc) 4.07 kB
import { ADDRESS_CLAMP_TO_EDGE, ADDRESS_REPEAT, PIXELFORMAT_DXT1, PIXELFORMAT_DXT3, PIXELFORMAT_DXT5, PIXELFORMAT_ETC1, PIXELFORMAT_ETC2_RGB, PIXELFORMAT_ETC2_RGBA, PIXELFORMAT_PVRTC_4BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_4BPP_RGBA_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1, PIXELFORMAT_RGB8, PIXELFORMAT_RGBA8, PIXELFORMAT_SRGB8, PIXELFORMAT_SRGBA8, PIXELFORMAT_111110F, PIXELFORMAT_RGB16F, PIXELFORMAT_RGBA16F, TEXHINT_ASSET, pixelFormatLinearToGamma } from "../../../platform/graphics/constants.js"; import { Texture } from "../../../platform/graphics/texture.js"; import { Asset } from "../../asset/asset.js"; import { TextureParser } from "./texture.js"; const IDENTIFIER = [1481919403, 3140563232, 169478669]; const KNOWN_FORMATS = { // compressed formats 33776: PIXELFORMAT_DXT1, 33778: PIXELFORMAT_DXT3, 33779: PIXELFORMAT_DXT5, 36196: PIXELFORMAT_ETC1, 37492: PIXELFORMAT_ETC2_RGB, 37496: PIXELFORMAT_ETC2_RGBA, 35840: PIXELFORMAT_PVRTC_4BPP_RGB_1, 35841: PIXELFORMAT_PVRTC_2BPP_RGB_1, 35842: PIXELFORMAT_PVRTC_4BPP_RGBA_1, 35843: PIXELFORMAT_PVRTC_2BPP_RGBA_1, // uncompressed formats 32849: PIXELFORMAT_RGB8, // GL_RGB8 32856: PIXELFORMAT_RGBA8, // GL_RGBA8 35905: PIXELFORMAT_SRGB8, // GL_SRGB8 35907: PIXELFORMAT_SRGBA8, // GL_SRGB8_ALPHA8 35898: PIXELFORMAT_111110F, // GL_R11F_G11F_B10F 34843: PIXELFORMAT_RGB16F, // GL_RGB16F 34842: PIXELFORMAT_RGBA16F // GL_RGBA16F }; function createContainer(pixelFormat, buffer, byteOffset, byteSize) { return pixelFormat === PIXELFORMAT_111110F ? new Uint32Array(buffer, byteOffset, byteSize / 4) : new Uint8Array(buffer, byteOffset, byteSize); } class KtxParser extends TextureParser { constructor(registry) { super(); this.maxRetries = 0; } load(url, callback, asset) { Asset.fetchArrayBuffer(url.load, callback, asset, this.maxRetries); } open(url, data, device, textureOptions = {}) { const textureData = this.parse(data); if (!textureData) { return null; } const format = textureOptions.srgb ? pixelFormatLinearToGamma(textureData.format) : textureData.format; const texture = new Texture(device, { name: url, addressU: textureData.cubemap ? ADDRESS_CLAMP_TO_EDGE : ADDRESS_REPEAT, addressV: textureData.cubemap ? ADDRESS_CLAMP_TO_EDGE : ADDRESS_REPEAT, width: textureData.width, height: textureData.height, format, cubemap: textureData.cubemap, levels: textureData.levels, ...textureOptions }); texture.upload(); return texture; } parse(data) { const dataU32 = new Uint32Array(data); if (IDENTIFIER[0] !== dataU32[0] || IDENTIFIER[1] !== dataU32[1] || IDENTIFIER[2] !== dataU32[2]) { return null; } const header = { endianness: dataU32[3], // todo: Use this information glType: dataU32[4], glTypeSize: dataU32[5], glFormat: dataU32[6], glInternalFormat: dataU32[7], glBaseInternalFormat: dataU32[8], pixelWidth: dataU32[9], pixelHeight: dataU32[10], pixelDepth: dataU32[11], numberOfArrayElements: dataU32[12], numberOfFaces: dataU32[13], numberOfMipmapLevels: dataU32[14], bytesOfKeyValueData: dataU32[15] }; if (header.pixelDepth > 1) { return null; } if (header.numberOfArrayElements !== 0) { return null; } const format = KNOWN_FORMATS[header.glInternalFormat]; if (format === void 0) { return null; } let offset = 16 + header.bytesOfKeyValueData / 4; const isCubemap = header.numberOfFaces > 1; const levels = []; for (let mipmapLevel = 0; mipmapLevel < (header.numberOfMipmapLevels || 1); mipmapLevel++) { const imageSizeInBytes = dataU32[offset++]; if (isCubemap) { levels.push([]); } const target = isCubemap ? levels[mipmapLevel] : levels; for (let face = 0; face < (isCubemap ? 6 : 1); ++face) { target.push(createContainer(format, data, offset * 4, imageSizeInBytes)); offset += imageSizeInBytes + 3 >> 2; } } return { format, width: header.pixelWidth, height: header.pixelHeight, levels, cubemap: isCubemap }; } } export { KtxParser };