UNPKG

playcanvas

Version:

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

109 lines (108 loc) 3.6 kB
import { Debug } from "../../../core/debug.js"; import { ReadStream } from "../../../core/read-stream.js"; import { ADDRESS_CLAMP_TO_EDGE, ADDRESS_REPEAT, TEXHINT_ASSET, pixelFormatLinearToGamma } from "../../../platform/graphics/constants.js"; import { Texture } from "../../../platform/graphics/texture.js"; import { Asset } from "../../asset/asset.js"; import { basisTranscode } from "../../handlers/basis.js"; import { TextureParser } from "./texture.js"; const KHRConstants = { KHR_DF_MODEL_ETC1S: 163, KHR_DF_MODEL_UASTC: 166 }; class Ktx2Parser extends TextureParser { constructor(registry, device) { super(); this.maxRetries = 0; this.device = device; } load(url, callback, asset) { Asset.fetchArrayBuffer(url.load, (err, result) => { if (err) { callback(err, result); } else { this.parse(result, url, callback, asset); } }, asset, this.maxRetries); } open(url, data, device, textureOptions = {}) { const format = textureOptions.srgb ? pixelFormatLinearToGamma(data.format) : data.format; const texture = new Texture(device, { name: url, profilerHint: TEXHINT_ASSET, addressU: data.cubemap ? ADDRESS_CLAMP_TO_EDGE : ADDRESS_REPEAT, addressV: data.cubemap ? ADDRESS_CLAMP_TO_EDGE : ADDRESS_REPEAT, width: data.width, height: data.height, format, cubemap: data.cubemap, levels: data.levels, ...textureOptions }); texture.upload(); return texture; } parse(arraybuffer, url, callback, asset) { const rs = new ReadStream(arraybuffer); const magic = [rs.readU32be(), rs.readU32be(), rs.readU32be()]; if (magic[0] !== 2873840728 || magic[1] !== 540160187 || magic[2] !== 218765834) { Debug.warn("Invalid definition header found in KTX2 file. Expected 0xAB4B5458, 0x203131BB, 0x0D0A1A0A"); return null; } const header = { vkFormat: rs.readU32(), typeSize: rs.readU32(), pixelWidth: rs.readU32(), pixelHeight: rs.readU32(), pixelDepth: rs.readU32(), layerCount: rs.readU32(), faceCount: rs.readU32(), levelCount: rs.readU32(), supercompressionScheme: rs.readU32() }; const index = { dfdByteOffset: rs.readU32(), dfdByteLength: rs.readU32(), kvdByteOffset: rs.readU32(), kvdByteLength: rs.readU32(), sgdByteOffset: rs.readU64(), sgdByteLength: rs.readU64() }; const levels = []; for (let i = 0; i < Math.max(1, header.levelCount); ++i) { levels.push({ byteOffset: rs.readU64(), byteLength: rs.readU64(), uncompressedByteLength: rs.readU64() }); } const dfdTotalSize = rs.readU32(); if (dfdTotalSize !== index.kvdByteOffset - index.dfdByteOffset) { Debug.warn("Invalid file data encountered."); return null; } rs.skip(8); const colorModel = rs.readU8(); rs.skip(index.dfdByteLength - 9); rs.skip(index.kvdByteLength); if (header.supercompressionScheme === 1 || colorModel === KHRConstants.KHR_DF_MODEL_UASTC) { const basisModuleFound = basisTranscode( this.device, url.load, arraybuffer, callback, { isGGGR: (asset?.file?.variants?.basis?.opt & 8) !== 0, isKTX2: true } ); if (!basisModuleFound) { callback(`Basis module not found. Asset [${asset.name}](${asset.getFileUrl()}) basis texture variant will not be loaded.`); } } else { callback("unsupported KTX2 pixel format"); } } } export { Ktx2Parser };