UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

135 lines (132 loc) 4.97 kB
import { PIXELFORMAT_RGB8, ADDRESS_CLAMP_TO_EDGE, ADDRESS_REPEAT, PIXELFORMAT_DXT1, PIXELFORMAT_DXT5, PIXELFORMAT_RGBA16F, PIXELFORMAT_RGBA32F, PIXELFORMAT_ETC1, PIXELFORMAT_PVRTC_2BPP_RGB_1, PIXELFORMAT_PVRTC_2BPP_RGBA_1, PIXELFORMAT_PVRTC_4BPP_RGB_1, PIXELFORMAT_PVRTC_4BPP_RGBA_1, PIXELFORMAT_RGBA8 } from '../../../platform/graphics/constants.js'; import { Texture } from '../../../platform/graphics/texture.js'; import { Asset } from '../../asset/asset.js'; import { TextureParser } from './texture.js'; class DdsParser 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 header = new Uint32Array(data, 0, 128 / 4); const width = header[4]; const height = header[3]; const mips = Math.max(header[7], 1); const isFourCc = header[20] === 4; const fcc = header[21]; const bpp = header[22]; const isCubemap = header[28] === 65024; const FCC_DXT1 = 827611204; const FCC_DXT5 = 894720068; const FCC_FP16 = 113; const FCC_FP32 = 116; const FCC_ETC1 = 826496069; const FCC_PVRTC_2BPP_RGB_1 = 825438800; const FCC_PVRTC_2BPP_RGBA_1 = 825504336; const FCC_PVRTC_4BPP_RGB_1 = 825439312; const FCC_PVRTC_4BPP_RGBA_1 = 825504848; let compressed = false; let etc1 = false; let pvrtc2 = false; let pvrtc4 = false; let format = null; let componentSize = 1; let texture; if (isFourCc) { if (fcc === FCC_DXT1) { format = PIXELFORMAT_DXT1; compressed = true; } else if (fcc === FCC_DXT5) { format = PIXELFORMAT_DXT5; compressed = true; } else if (fcc === FCC_FP16) { format = PIXELFORMAT_RGBA16F; componentSize = 2; } else if (fcc === FCC_FP32) { format = PIXELFORMAT_RGBA32F; componentSize = 4; } else if (fcc === FCC_ETC1) { format = PIXELFORMAT_ETC1; compressed = true; etc1 = true; } else if (fcc === FCC_PVRTC_2BPP_RGB_1 || fcc === FCC_PVRTC_2BPP_RGBA_1) { format = fcc === FCC_PVRTC_2BPP_RGB_1 ? PIXELFORMAT_PVRTC_2BPP_RGB_1 : PIXELFORMAT_PVRTC_2BPP_RGBA_1; compressed = true; pvrtc2 = true; } else if (fcc === FCC_PVRTC_4BPP_RGB_1 || fcc === FCC_PVRTC_4BPP_RGBA_1) { format = fcc === FCC_PVRTC_4BPP_RGB_1 ? PIXELFORMAT_PVRTC_4BPP_RGB_1 : PIXELFORMAT_PVRTC_4BPP_RGBA_1; compressed = true; pvrtc4 = true; } } else { if (bpp === 32) { format = PIXELFORMAT_RGBA8; } } if (!format) { texture = new Texture(device, { width: 4, height: 4, format: PIXELFORMAT_RGB8, name: 'dds-legacy-empty' }); return texture; } texture = new Texture(device, { name: url, addressU: isCubemap ? ADDRESS_CLAMP_TO_EDGE : ADDRESS_REPEAT, addressV: isCubemap ? ADDRESS_CLAMP_TO_EDGE : ADDRESS_REPEAT, width: width, height: height, format: format, cubemap: isCubemap, mipmaps: mips > 1, ...textureOptions }); let offset = 128; const faces = isCubemap ? 6 : 1; let mipSize; const DXT_BLOCK_WIDTH = 4; const DXT_BLOCK_HEIGHT = 4; const blockSize = fcc === FCC_DXT1 ? 8 : 16; let numBlocksAcross, numBlocksDown, numBlocks; for(let face = 0; face < faces; face++){ let mipWidth = width; let mipHeight = height; for(let i = 0; i < mips; i++){ if (compressed) { if (etc1) { mipSize = Math.floor((mipWidth + 3) / 4) * Math.floor((mipHeight + 3) / 4) * 8; } else if (pvrtc2) { mipSize = Math.max(mipWidth, 16) * Math.max(mipHeight, 8) / 4; } else if (pvrtc4) { mipSize = Math.max(mipWidth, 8) * Math.max(mipHeight, 8) / 2; } else { numBlocksAcross = Math.floor((mipWidth + DXT_BLOCK_WIDTH - 1) / DXT_BLOCK_WIDTH); numBlocksDown = Math.floor((mipHeight + DXT_BLOCK_HEIGHT - 1) / DXT_BLOCK_HEIGHT); numBlocks = numBlocksAcross * numBlocksDown; mipSize = numBlocks * blockSize; } } else { mipSize = mipWidth * mipHeight * 4; } const mipBuff = format === PIXELFORMAT_RGBA32F ? new Float32Array(data, offset, mipSize) : format === PIXELFORMAT_RGBA16F ? new Uint16Array(data, offset, mipSize) : new Uint8Array(data, offset, mipSize); if (!isCubemap) { texture._levels[i] = mipBuff; } else { if (!texture._levels[i]) texture._levels[i] = []; texture._levels[i][face] = mipBuff; } offset += mipSize * componentSize; mipWidth = Math.max(mipWidth * 0.5, 1); mipHeight = Math.max(mipHeight * 0.5, 1); } } texture.upload(); return texture; } } export { DdsParser };