UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

138 lines (135 loc) 3.99 kB
import { ReadStream } from '../../../core/read-stream.js'; import { TEXTURETYPE_RGBE, PIXELFORMAT_RGBA8, FILTER_NEAREST, 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 { TextureParser } from './texture.js'; class HdrParser extends TextureParser { constructor(registry){ super(); this.maxRetries = 0; } load(url, callback, asset) { Asset.fetchArrayBuffer(url.load, callback, asset, this.maxRetries); if (asset.data && !asset.data.type) { asset.data.type = TEXTURETYPE_RGBE; } } open(url, data, device, textureOptions = {}) { const textureData = this.parse(data); if (!textureData) { return null; } const texture = new Texture(device, { name: url, addressU: ADDRESS_REPEAT, addressV: ADDRESS_CLAMP_TO_EDGE, minFilter: FILTER_NEAREST, magFilter: FILTER_NEAREST, width: textureData.width, height: textureData.height, levels: textureData.levels, format: PIXELFORMAT_RGBA8, type: TEXTURETYPE_RGBE, mipmaps: false, ...textureOptions }); texture.upload(); return texture; } parse(data) { const readStream = new ReadStream(data); const magic = readStream.readLine(); if (!magic.startsWith('#?RADIANCE')) { return null; } const variables = {}; while(true){ const line = readStream.readLine(); if (line.length === 0) { break; } else { const parts = line.split('='); if (parts.length === 2) { variables[parts[0]] = parts[1]; } } } if (!variables.hasOwnProperty('FORMAT')) { return null; } const resolution = readStream.readLine().split(' '); if (resolution.length !== 4) { return null; } const height = parseInt(resolution[1], 10); const width = parseInt(resolution[3], 10); const pixels = this._readPixels(readStream, width, height, resolution[0] === '-Y'); if (!pixels) { return null; } return { width: width, height: height, levels: [ pixels ] }; } _readPixels(readStream, width, height, flipY) { if (width < 8 || width > 0x7fff) { return this._readPixelsFlat(readStream, width, height); } const rgbe = [ 0, 0, 0, 0 ]; readStream.readArray(rgbe); if (rgbe[0] !== 2 || rgbe[1] !== 2 || (rgbe[2] & 0x80) !== 0) { readStream.skip(-4); return this._readPixelsFlat(readStream, width, height); } const buffer = new ArrayBuffer(width * height * 4); const view = new Uint8Array(buffer); let scanstart = flipY ? 0 : width * 4 * (height - 1); let x, y, i, channel, count, value; for(y = 0; y < height; ++y){ if (y) { readStream.readArray(rgbe); } if ((rgbe[2] << 8) + rgbe[3] !== width) { return null; } for(channel = 0; channel < 4; ++channel){ x = 0; while(x < width){ count = readStream.readU8(); if (count > 128) { count -= 128; if (x + count > width) { return null; } value = readStream.readU8(); for(i = 0; i < count; ++i){ view[scanstart + channel + 4 * x++] = value; } } else { if (count === 0 || x + count > width) { return null; } for(i = 0; i < count; ++i){ view[scanstart + channel + 4 * x++] = readStream.readU8(); } } } } scanstart += width * 4 * (flipY ? 1 : -1); } return view; } _readPixelsFlat(readStream, width, height) { return readStream.remainingBytes === width * height * 4 ? new Uint8Array(readStream.arraybuffer, readStream.offset) : null; } } export { HdrParser };