UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

234 lines 9.06 kB
import { PanoramaToCubeMapTools } from "./panoramaToCubemap.js"; /* This groups tools to convert HDR texture to native colors array. */ function Ldexp(mantissa, exponent) { if (exponent > 1023) { return mantissa * Math.pow(2, 1023) * Math.pow(2, exponent - 1023); } if (exponent < -1074) { return mantissa * Math.pow(2, -1074) * Math.pow(2, exponent + 1074); } return mantissa * Math.pow(2, exponent); } function Rgbe2float(float32array, red, green, blue, exponent, index) { if (exponent > 0) { /*nonzero pixel*/ exponent = Ldexp(1.0, exponent - (128 + 8)); float32array[index + 0] = red * exponent; float32array[index + 1] = green * exponent; float32array[index + 2] = blue * exponent; } else { float32array[index + 0] = 0; float32array[index + 1] = 0; float32array[index + 2] = 0; } } function ReadStringLine(uint8array, startIndex) { let line = ""; let character = ""; for (let i = startIndex; i < uint8array.length - startIndex; i++) { character = String.fromCharCode(uint8array[i]); if (character == "\n") { break; } line += character; } return line; } /** * Reads header information from an RGBE texture stored in a native array. * More information on this format are available here: * https://en.wikipedia.org/wiki/RGBE_image_format * * @param uint8array The binary file stored in native array. * @returns The header information. */ // eslint-disable-next-line @typescript-eslint/naming-convention export function RGBE_ReadHeader(uint8array) { let height = 0; let width = 0; let line = ReadStringLine(uint8array, 0); if (line[0] != "#" || line[1] != "?") { // eslint-disable-next-line no-throw-literal throw "Bad HDR Format."; } let endOfHeader = false; let findFormat = false; let lineIndex = 0; do { lineIndex += line.length + 1; line = ReadStringLine(uint8array, lineIndex); if (line == "FORMAT=32-bit_rle_rgbe") { findFormat = true; } else if (line.length == 0) { endOfHeader = true; } } while (!endOfHeader); if (!findFormat) { // eslint-disable-next-line no-throw-literal throw "HDR Bad header format, unsupported FORMAT"; } lineIndex += line.length + 1; line = ReadStringLine(uint8array, lineIndex); const sizeRegexp = /^-Y (.*) \+X (.*)$/g; const match = sizeRegexp.exec(line); // TODO. Support +Y and -X if needed. if (!match || match.length < 3) { // eslint-disable-next-line no-throw-literal throw "HDR Bad header format, no size"; } width = parseInt(match[2]); height = parseInt(match[1]); if (width < 8 || width > 0x7fff) { // eslint-disable-next-line no-throw-literal throw "HDR Bad header format, unsupported size"; } lineIndex += line.length + 1; return { height: height, width: width, dataPosition: lineIndex, }; } /** * Returns the cubemap information (each faces texture data) extracted from an RGBE texture. * This RGBE texture needs to store the information as a panorama. * * More information on this format are available here: * https://en.wikipedia.org/wiki/RGBE_image_format * * @param buffer The binary file stored in an array buffer. * @param size The expected size of the extracted cubemap. * @param supersample enable supersampling the cubemap (default: false) * @returns The Cube Map information. */ export function GetCubeMapTextureData(buffer, size, supersample = false) { const uint8array = new Uint8Array(buffer); const hdrInfo = RGBE_ReadHeader(uint8array); const data = RGBE_ReadPixels(uint8array, hdrInfo); const cubeMapData = PanoramaToCubeMapTools.ConvertPanoramaToCubemap(data, hdrInfo.width, hdrInfo.height, size, supersample); return cubeMapData; } /** * Returns the pixels data extracted from an RGBE texture. * This pixels will be stored left to right up to down in the R G B order in one array. * * More information on this format are available here: * https://en.wikipedia.org/wiki/RGBE_image_format * * @param uint8array The binary file stored in an array buffer. * @param hdrInfo The header information of the file. * @returns The pixels data in RGB right to left up to down order. */ // eslint-disable-next-line @typescript-eslint/naming-convention export function RGBE_ReadPixels(uint8array, hdrInfo) { return ReadRGBEPixelsRLE(uint8array, hdrInfo); } // eslint-disable-next-line @typescript-eslint/naming-convention function ReadRGBEPixelsRLE(uint8array, hdrInfo) { let numScanlines = hdrInfo.height; const scanlineWidth = hdrInfo.width; let a, b, c, d, count; let dataIndex = hdrInfo.dataPosition; let index = 0, endIndex = 0, i = 0; const scanLineArrayBuffer = new ArrayBuffer(scanlineWidth * 4); // four channel R G B E const scanLineArray = new Uint8Array(scanLineArrayBuffer); // 3 channels of 4 bytes per pixel in float. const resultBuffer = new ArrayBuffer(hdrInfo.width * hdrInfo.height * 4 * 3); const resultArray = new Float32Array(resultBuffer); // read in each successive scanline while (numScanlines > 0) { a = uint8array[dataIndex++]; b = uint8array[dataIndex++]; c = uint8array[dataIndex++]; d = uint8array[dataIndex++]; if (a != 2 || b != 2 || c & 0x80 || hdrInfo.width < 8 || hdrInfo.width > 32767) { return ReadRGBEPixelsNotRLE(uint8array, hdrInfo); } if (((c << 8) | d) != scanlineWidth) { // eslint-disable-next-line no-throw-literal throw "HDR Bad header format, wrong scan line width"; } index = 0; // read each of the four channels for the scanline into the buffer for (i = 0; i < 4; i++) { endIndex = (i + 1) * scanlineWidth; while (index < endIndex) { a = uint8array[dataIndex++]; b = uint8array[dataIndex++]; if (a > 128) { // a run of the same value count = a - 128; if (count == 0 || count > endIndex - index) { // eslint-disable-next-line no-throw-literal throw "HDR Bad Format, bad scanline data (run)"; } while (count-- > 0) { scanLineArray[index++] = b; } } else { // a non-run count = a; if (count == 0 || count > endIndex - index) { // eslint-disable-next-line no-throw-literal throw "HDR Bad Format, bad scanline data (non-run)"; } scanLineArray[index++] = b; if (--count > 0) { for (let j = 0; j < count; j++) { scanLineArray[index++] = uint8array[dataIndex++]; } } } } } // now convert data from buffer into floats for (i = 0; i < scanlineWidth; i++) { a = scanLineArray[i]; b = scanLineArray[i + scanlineWidth]; c = scanLineArray[i + 2 * scanlineWidth]; d = scanLineArray[i + 3 * scanlineWidth]; Rgbe2float(resultArray, a, b, c, d, (hdrInfo.height - numScanlines) * scanlineWidth * 3 + i * 3); } numScanlines--; } return resultArray; } // eslint-disable-next-line @typescript-eslint/naming-convention function ReadRGBEPixelsNotRLE(uint8array, hdrInfo) { // this file is not run length encoded // read values sequentially let numScanlines = hdrInfo.height; const scanlineWidth = hdrInfo.width; let a, b, c, d, i; let dataIndex = hdrInfo.dataPosition; // 3 channels of 4 bytes per pixel in float. const resultBuffer = new ArrayBuffer(hdrInfo.width * hdrInfo.height * 4 * 3); const resultArray = new Float32Array(resultBuffer); // read in each successive scanline while (numScanlines > 0) { for (i = 0; i < hdrInfo.width; i++) { a = uint8array[dataIndex++]; b = uint8array[dataIndex++]; c = uint8array[dataIndex++]; d = uint8array[dataIndex++]; Rgbe2float(resultArray, a, b, c, d, (hdrInfo.height - numScanlines) * scanlineWidth * 3 + i * 3); } numScanlines--; } return resultArray; } /** * @deprecated Use functions separately */ export const HDRTools = { // eslint-disable-next-line @typescript-eslint/naming-convention RGBE_ReadHeader, // eslint-disable-next-line @typescript-eslint/naming-convention GetCubeMapTextureData, // eslint-disable-next-line @typescript-eslint/naming-convention RGBE_ReadPixels, }; //# sourceMappingURL=hdr.js.map