playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
102 lines (99 loc) • 3.29 kB
JavaScript
import { ReadStream } from '../../../core/read-stream.js';
import { pixelFormatLinearToGamma, ADDRESS_CLAMP_TO_EDGE, ADDRESS_REPEAT } 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_UASTC: 166
};
class Ktx2Parser extends TextureParser {
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,
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: 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] !== 0xAB4B5458 || magic[1] !== 0x203230BB || magic[2] !== 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) {
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');
}
}
constructor(registry, device){
super();
this.maxRetries = 0;
this.device = device;
}
}
export { Ktx2Parser };