UNPKG

playcanvas

Version:

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

140 lines (139 loc) 3.82 kB
import { ReadStream } from "../../../core/read-stream.js"; import { TEXHINT_ASSET, ADDRESS_REPEAT, ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_RGBA8, TEXTURETYPE_RGBE } 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, // RGBE can't be filtered, so mipmaps are out of the question! (unless we generated them ourselves) mipmaps: false, ...textureOptions }); texture.upload(); return texture; } // https://floyd.lbl.gov/radiance/refer/filefmts.pdf with help from http://www.graphics.cornell.edu/~bjw/rgbe/rgbe.c 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, height, levels: [pixels] }; } _readPixels(readStream, width, height, flipY) { if (width < 8 || width > 32767) { return this._readPixelsFlat(readStream, width, height); } const rgbe = [0, 0, 0, 0]; readStream.readArray(rgbe); if (rgbe[0] !== 2 || rgbe[1] !== 2 || (rgbe[2] & 128) !== 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 };