@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.
754 lines (753 loc) • 33.9 kB
JavaScript
import { Tools } from "./tools.js";
import { Vector3 } from "../Maths/math.vector.js";
import { ILog2 } from "../Maths/math.scalar.functions.js";
import { SphericalPolynomial } from "../Maths/sphericalPolynomial.js";
import { InternalTexture } from "../Materials/Textures/internalTexture.js";
import { BaseTexture } from "../Materials/Textures/baseTexture.js";
import { Scene } from "../scene.js";
import { PostProcess } from "../PostProcesses/postProcess.js";
import { Logger } from "../Misc/logger.js";
import { RGBDTextureTools } from "./rgbdTextureTools.js";
import { DumpDataAsync } from "../Misc/dumpTools.js";
import "../Materials/Textures/baseTexture.polynomial.js";
const DefaultEnvironmentTextureImageType = "image/png";
const CurrentVersion = 2;
/**
* Magic number identifying the env file.
*/
const MagicBytes = [0x86, 0x16, 0x87, 0x96, 0xf6, 0xd6, 0x96, 0x36];
/**
* Gets the environment info from an env file.
* @param data The array buffer containing the .env bytes.
* @returns the environment file info (the json header) if successfully parsed, normalized to the latest supported version.
*/
export function GetEnvInfo(data) {
const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);
let pos = 0;
for (let i = 0; i < MagicBytes.length; i++) {
if (dataView.getUint8(pos++) !== MagicBytes[i]) {
Logger.Error("Not a babylon environment map");
return null;
}
}
// Read json manifest - collect characters up to null terminator
let manifestString = "";
let charCode = 0x00;
while ((charCode = dataView.getUint8(pos++))) {
manifestString += String.fromCharCode(charCode);
}
let manifest = JSON.parse(manifestString);
manifest = normalizeEnvInfo(manifest);
// Extend the header with the position of the payload.
manifest.binaryDataPosition = pos;
if (manifest.specular) {
// Fallback to 0.8 exactly if lodGenerationScale is not defined for backward compatibility.
manifest.specular.lodGenerationScale = manifest.specular.lodGenerationScale || 0.8;
}
return manifest;
}
/**
* Normalizes any supported version of the environment file info to the latest version
* @param info environment file info on any supported version
* @returns environment file info in the latest supported version
* @private
*/
export function normalizeEnvInfo(info) {
if (info.version > CurrentVersion) {
throw new Error(`Unsupported babylon environment map version "${info.version}". Latest supported version is "${CurrentVersion}".`);
}
if (info.version === 2) {
return info;
}
// Migrate a v1 info to v2
info = { ...info, version: 2, imageType: DefaultEnvironmentTextureImageType };
return info;
}
/**
* Creates an environment texture from a loaded cube texture.
* @param texture defines the cube texture to convert in env file
* @param options options for the conversion process
* @returns a promise containing the environment data if successful.
*/
export async function CreateEnvTextureAsync(texture, options = {}) {
const internalTexture = texture.getInternalTexture();
if (!internalTexture) {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
return await Promise.reject("The cube texture is invalid.");
}
const engine = internalTexture.getEngine();
if (texture.textureType !== 2 &&
texture.textureType !== 1 &&
texture.textureType !== 0 &&
texture.textureType !== 0 &&
texture.textureType !== 7 &&
texture.textureType !== -1) {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
return await Promise.reject("The cube texture should allow HDR (Full Float or Half Float).");
}
let textureType = 1;
if (!engine.getCaps().textureFloatRender) {
textureType = 2;
if (!engine.getCaps().textureHalfFloatRender) {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
return await Promise.reject("Env texture can only be created when the browser supports half float or full float rendering.");
}
}
// sphericalPolynomial is lazy loaded so simply accessing it should trigger the computation.
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
texture.sphericalPolynomial;
// Lets keep track of the polynomial promise so we can wait for it to be ready before generating the pixels.
const sphericalPolynomialPromise = texture.getInternalTexture()?._sphericalPolynomialPromise;
const cubeWidth = internalTexture.width;
const hostingScene = new Scene(engine);
const specularTextures = {};
const diffuseTextures = {};
// As we are going to readPixels the faces of the cube, make sure the drawing/update commands for the cube texture are fully sent to the GPU in case it is drawn for the first time in this very frame!
engine.flushFramebuffer();
const imageType = options.imageType ?? DefaultEnvironmentTextureImageType;
// Read and collect all mipmaps data from the cube.
const mipmapsCount = ILog2(internalTexture.width);
for (let i = 0; i <= mipmapsCount; i++) {
const faceWidth = Math.pow(2, mipmapsCount - i);
// All faces of the cube.
for (let face = 0; face < 6; face++) {
// eslint-disable-next-line no-await-in-loop
specularTextures[i * 6 + face] = await _GetTextureEncodedDataAsync(hostingScene, texture, textureType, face, i, faceWidth, imageType, options.imageQuality);
}
}
// Read and collect all irradiance data from the cube.
const irradianceTexture = options.disableIrradianceTexture ? null : texture.irradianceTexture;
if (irradianceTexture) {
const faceWidth = irradianceTexture.getSize().width;
// All faces of the cube.
for (let face = 0; face < 6; face++) {
// eslint-disable-next-line no-await-in-loop
diffuseTextures[face] = await _GetTextureEncodedDataAsync(hostingScene, irradianceTexture, textureType, face, 0, faceWidth, imageType, options.imageQuality);
}
}
// We can delete the hosting scene keeping track of all the creation objects
hostingScene.dispose();
// Ensure completion of the polynomial creation promise.
if (sphericalPolynomialPromise) {
await sphericalPolynomialPromise;
}
// Creates the json header for the env texture
const info = {
version: CurrentVersion,
width: cubeWidth,
imageType,
irradiance: CreateEnvTextureIrradiance(texture),
specular: {
mipmaps: [],
lodGenerationScale: texture.lodGenerationScale,
},
};
// Sets the specular image data information
let position = 0;
for (let i = 0; i <= mipmapsCount; i++) {
for (let face = 0; face < 6; face++) {
const byteLength = specularTextures[i * 6 + face].byteLength;
info.specular.mipmaps.push({
length: byteLength,
position: position,
});
position += byteLength;
}
}
// Sets the irradiance image data information
if (irradianceTexture) {
info.irradiance = info.irradiance || {
x: [0, 0, 0],
xx: [0, 0, 0],
y: [0, 0, 0],
yy: [0, 0, 0],
z: [0, 0, 0],
zz: [0, 0, 0],
yz: [0, 0, 0],
zx: [0, 0, 0],
xy: [0, 0, 0],
};
info.irradiance.irradianceTexture = {
size: irradianceTexture.getSize().width,
faces: [],
dominantDirection: irradianceTexture._dominantDirection?.asArray(),
};
for (let face = 0; face < 6; face++) {
const byteLength = diffuseTextures[face].byteLength;
info.irradiance.irradianceTexture.faces.push({
length: byteLength,
position: position,
});
position += byteLength;
}
}
// Encode the JSON as an array buffer
const infoString = JSON.stringify(info);
const infoBuffer = new ArrayBuffer(infoString.length + 1);
const infoView = new Uint8Array(infoBuffer); // Limited to ascii subset matching unicode.
for (let i = 0, strLen = infoString.length; i < strLen; i++) {
infoView[i] = infoString.charCodeAt(i);
}
// Ends up with a null terminator for easier parsing
infoView[infoString.length] = 0x00;
// Computes the final required size and creates the storage
const totalSize = MagicBytes.length + position + infoBuffer.byteLength;
const finalBuffer = new ArrayBuffer(totalSize);
const finalBufferView = new Uint8Array(finalBuffer);
const dataView = new DataView(finalBuffer);
// Copy the magic bytes identifying the file in
let pos = 0;
for (let i = 0; i < MagicBytes.length; i++) {
dataView.setUint8(pos++, MagicBytes[i]);
}
// Add the json info
finalBufferView.set(new Uint8Array(infoBuffer), pos);
pos += infoBuffer.byteLength;
// Finally inserts the radiance texture data
for (let i = 0; i <= mipmapsCount; i++) {
for (let face = 0; face < 6; face++) {
const dataBuffer = specularTextures[i * 6 + face];
finalBufferView.set(new Uint8Array(dataBuffer), pos);
pos += dataBuffer.byteLength;
}
}
// Finally inserts the irradiance texture data
if (irradianceTexture) {
for (let face = 0; face < 6; face++) {
const dataBuffer = diffuseTextures[face];
finalBufferView.set(new Uint8Array(dataBuffer), pos);
pos += dataBuffer.byteLength;
}
}
// Voila
return finalBuffer;
}
/**
* Get the texture encoded data from the current texture
* @internal
*/
async function _GetTextureEncodedDataAsync(hostingScene, texture, textureType, face, i, size, imageType, imageQuality) {
let faceData = await texture.readPixels(face, i, undefined, false);
if (faceData && faceData.byteLength === faceData.length) {
const faceDataFloat = new Float32Array(faceData.byteLength * 4);
for (let i = 0; i < faceData.byteLength; i++) {
faceDataFloat[i] = faceData[i] / 255;
// Gamma to linear
faceDataFloat[i] = Math.pow(faceDataFloat[i], 2.2);
}
faceData = faceDataFloat;
}
else if (faceData && texture.gammaSpace) {
const floatData = faceData;
for (let i = 0; i < floatData.length; i++) {
// Gamma to linear
floatData[i] = Math.pow(floatData[i], 2.2);
}
}
const engine = hostingScene.getEngine();
const tempTexture = engine.createRawTexture(faceData, size, size, 5, false, true, 1, null, textureType);
await RGBDTextureTools.EncodeTextureToRGBD(tempTexture, hostingScene, textureType);
const rgbdEncodedData = await engine._readTexturePixels(tempTexture, size, size);
const imageEncodedData = await DumpDataAsync(size, size, rgbdEncodedData, imageType, undefined, false, true, imageQuality);
tempTexture.dispose();
return imageEncodedData;
}
/**
* Creates a JSON representation of the spherical data.
* @param texture defines the texture containing the polynomials
* @returns the JSON representation of the spherical info
*/
function CreateEnvTextureIrradiance(texture) {
const polynmials = texture.sphericalPolynomial;
if (polynmials == null) {
return null;
}
return {
x: [polynmials.x.x, polynmials.x.y, polynmials.x.z],
y: [polynmials.y.x, polynmials.y.y, polynmials.y.z],
z: [polynmials.z.x, polynmials.z.y, polynmials.z.z],
xx: [polynmials.xx.x, polynmials.xx.y, polynmials.xx.z],
yy: [polynmials.yy.x, polynmials.yy.y, polynmials.yy.z],
zz: [polynmials.zz.x, polynmials.zz.y, polynmials.zz.z],
yz: [polynmials.yz.x, polynmials.yz.y, polynmials.yz.z],
zx: [polynmials.zx.x, polynmials.zx.y, polynmials.zx.z],
xy: [polynmials.xy.x, polynmials.xy.y, polynmials.xy.z],
};
}
/**
* Creates the ArrayBufferViews used for initializing environment texture image data.
* @param data the image data
* @param info parameters that determine what views will be created for accessing the underlying buffer
* @returns the views described by info providing access to the underlying buffer
*/
export function CreateRadianceImageDataArrayBufferViews(data, info) {
info = normalizeEnvInfo(info);
const specularInfo = info.specular;
// Double checks the enclosed info
let mipmapsCount = Math.log2(info.width);
mipmapsCount = Math.round(mipmapsCount) + 1;
if (specularInfo.mipmaps.length !== 6 * mipmapsCount) {
throw new Error(`Unsupported specular mipmaps number "${specularInfo.mipmaps.length}"`);
}
const imageData = new Array(mipmapsCount);
for (let i = 0; i < mipmapsCount; i++) {
imageData[i] = new Array(6);
for (let face = 0; face < 6; face++) {
const imageInfo = specularInfo.mipmaps[i * 6 + face];
imageData[i][face] = new Uint8Array(data.buffer, data.byteOffset + info.binaryDataPosition + imageInfo.position, imageInfo.length);
}
}
return imageData;
}
/**
* Creates the ArrayBufferViews used for initializing environment texture image data.
* @param data the image data
* @param info parameters that determine what views will be created for accessing the underlying buffer
* @returns the views described by info providing access to the underlying buffer
*/
export function CreateIrradianceImageDataArrayBufferViews(data, info) {
info = normalizeEnvInfo(info);
const imageData = new Array(6);
const irradianceTexture = info.irradiance?.irradianceTexture;
if (irradianceTexture) {
if (irradianceTexture.faces.length !== 6) {
throw new Error(`Incorrect irradiance texture faces number "${irradianceTexture.faces.length}"`);
}
for (let face = 0; face < 6; face++) {
const imageInfo = irradianceTexture.faces[face];
imageData[face] = new Uint8Array(data.buffer, data.byteOffset + info.binaryDataPosition + imageInfo.position, imageInfo.length);
}
}
return imageData;
}
/**
* Uploads the texture info contained in the env file to the GPU.
* @param texture defines the internal texture to upload to
* @param data defines the data to load
* @param info defines the texture info retrieved through the GetEnvInfo method
* @returns a promise
*/
// eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
export function UploadEnvLevelsAsync(texture, data, info) {
info = normalizeEnvInfo(info);
const specularInfo = info.specular;
if (!specularInfo) {
// Nothing else parsed so far
return Promise.resolve([]);
}
texture._lodGenerationScale = specularInfo.lodGenerationScale;
const promises = [];
const radianceImageData = CreateRadianceImageDataArrayBufferViews(data, info);
promises.push(UploadRadianceLevelsAsync(texture, radianceImageData, info.imageType));
const irradianceTexture = info.irradiance?.irradianceTexture;
if (irradianceTexture) {
const irradianceImageData = CreateIrradianceImageDataArrayBufferViews(data, info);
let dominantDirection = null;
if (info.irradiance?.irradianceTexture?.dominantDirection) {
dominantDirection = Vector3.FromArray(info.irradiance.irradianceTexture.dominantDirection);
}
promises.push(UploadIrradianceLevelsAsync(texture, irradianceImageData, irradianceTexture.size, info.imageType, dominantDirection));
}
return Promise.all(promises);
}
async function _OnImageReadyAsync(image, engine, expandTexture, rgbdPostProcess, url, face, i, generateNonLODTextures, lodTextures, cubeRtt, texture) {
return await new Promise((resolve, reject) => {
if (expandTexture) {
const tempTexture = engine.createTexture(null, true, true, null, 1, null, (message) => {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(message);
}, image);
rgbdPostProcess?.onEffectCreatedObservable.addOnce((effect) => {
effect.executeWhenCompiled(() => {
// Uncompress the data to a RTT
rgbdPostProcess.externalTextureSamplerBinding = true;
rgbdPostProcess.onApply = (effect) => {
effect._bindTexture("textureSampler", tempTexture);
effect.setFloat2("scale", 1, engine._features.needsInvertingBitmap && image instanceof ImageBitmap ? -1 : 1);
};
if (!engine.scenes.length) {
return;
}
engine.scenes[0].postProcessManager.directRender([rgbdPostProcess], cubeRtt, true, face, i);
// Cleanup
engine.restoreDefaultFramebuffer();
tempTexture.dispose();
URL.revokeObjectURL(url);
resolve();
});
});
}
else {
engine._uploadImageToTexture(texture, image, face, i);
// Upload the face to the non lod texture support
if (generateNonLODTextures) {
const lodTexture = lodTextures[i];
if (lodTexture) {
engine._uploadImageToTexture(lodTexture._texture, image, face, 0);
}
}
resolve();
}
});
}
/**
* Uploads the levels of image data to the GPU.
* @param texture defines the internal texture to upload to
* @param imageData defines the array buffer views of image data [mipmap][face]
* @param imageType the mime type of the image data
* @returns a promise
*/
export async function UploadRadianceLevelsAsync(texture, imageData, imageType = DefaultEnvironmentTextureImageType) {
const engine = texture.getEngine();
texture.format = 5;
texture.type = 0;
texture.generateMipMaps = true;
texture._cachedAnisotropicFilteringLevel = null;
engine.updateTextureSamplingMode(3, texture);
await _UploadLevelsAsync(texture, imageData, true, imageType);
// Flag internal texture as ready in case they are in use.
texture.isReady = true;
}
/**
* Uploads the levels of image data to the GPU.
* @param mainTexture defines the internal texture to upload to
* @param imageData defines the array buffer views of image data [mipmap][face]
* @param size defines the size of the texture faces
* @param imageType the mime type of the image data
* @param dominantDirection the dominant direction of light in the environment texture, if available
* @returns a promise
*/
export async function UploadIrradianceLevelsAsync(mainTexture, imageData, size, imageType = DefaultEnvironmentTextureImageType, dominantDirection = null) {
// Gets everything ready.
const engine = mainTexture.getEngine();
const texture = new InternalTexture(engine, 5 /* InternalTextureSource.RenderTarget */);
const baseTexture = new BaseTexture(engine, texture);
mainTexture._irradianceTexture = baseTexture;
baseTexture._dominantDirection = dominantDirection;
texture.isCube = true;
texture.format = 5;
texture.type = 0;
texture.generateMipMaps = true;
texture._cachedAnisotropicFilteringLevel = null;
texture.generateMipMaps = true;
texture.width = size;
texture.height = size;
engine.updateTextureSamplingMode(3, texture);
await _UploadLevelsAsync(texture, [imageData], false, imageType);
engine.generateMipMapsForCubemap(texture);
// Flag internal texture as ready in case they are in use.
texture.isReady = true;
}
/**
* Uploads the levels of image data to the GPU.
* @param texture defines the internal texture to upload to
* @param imageData defines the array buffer views of image data [mipmap][face]
* @param canGenerateNonLODTextures defines whether or not to generate non lod textures
* @param imageType the mime type of the image data
* @returns a promise
*/
async function _UploadLevelsAsync(texture, imageData, canGenerateNonLODTextures, imageType = DefaultEnvironmentTextureImageType) {
if (!Tools.IsExponentOfTwo(texture.width)) {
throw new Error("Texture size must be a power of two");
}
const mipmapsCount = ILog2(texture.width) + 1;
// Gets everything ready.
const engine = texture.getEngine();
let expandTexture = false;
let generateNonLODTextures = false;
let rgbdPostProcess = null;
let cubeRtt = null;
let lodTextures = null;
const caps = engine.getCaps();
if (!caps.textureLOD) {
expandTexture = false;
generateNonLODTextures = canGenerateNonLODTextures;
}
else if (!engine._features.supportRenderAndCopyToLodForFloatTextures) {
expandTexture = false;
}
// If half float available we can uncompress the texture
else if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
expandTexture = true;
texture.type = 2;
}
// If full float available we can uncompress the texture
else if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
expandTexture = true;
texture.type = 1;
}
// Expand the texture if possible
let shaderLanguage = 0 /* ShaderLanguage.GLSL */;
if (expandTexture) {
if (engine.isWebGPU) {
shaderLanguage = 1 /* ShaderLanguage.WGSL */;
await import("../ShadersWGSL/rgbdDecode.fragment.js");
}
else {
await import("../Shaders/rgbdDecode.fragment.js");
}
// Simply run through the decode PP
rgbdPostProcess = new PostProcess("rgbdDecode", "rgbdDecode", null, null, 1, null, 3, engine, false, undefined, texture.type, undefined, null, false, undefined, shaderLanguage);
texture._isRGBD = false;
texture.invertY = false;
cubeRtt = engine.createRenderTargetCubeTexture(texture.width, {
generateDepthBuffer: false,
generateMipMaps: true,
generateStencilBuffer: false,
samplingMode: 3,
type: texture.type,
format: 5,
});
}
else {
texture._isRGBD = true;
texture.invertY = true;
// In case of missing support, applies the same patch than DDS files.
if (generateNonLODTextures) {
const mipSlices = 3;
lodTextures = {};
const scale = texture._lodGenerationScale;
const offset = texture._lodGenerationOffset;
for (let i = 0; i < mipSlices; i++) {
//compute LOD from even spacing in smoothness (matching shader calculation)
const smoothness = i / (mipSlices - 1);
const roughness = 1 - smoothness;
const minLODIndex = offset; // roughness = 0
const maxLODIndex = (mipmapsCount - 1) * scale + offset; // roughness = 1 (mipmaps start from 0)
const lodIndex = minLODIndex + (maxLODIndex - minLODIndex) * roughness;
const mipmapIndex = Math.round(Math.min(Math.max(lodIndex, 0), maxLODIndex));
//compute LOD from even spacing in smoothness (matching shader calculation)
const glTextureFromLod = new InternalTexture(engine, 2 /* InternalTextureSource.Temp */);
glTextureFromLod.isCube = true;
glTextureFromLod.invertY = true;
glTextureFromLod.generateMipMaps = false;
engine.updateTextureSamplingMode(2, glTextureFromLod);
// Wrap in a base texture for easy binding.
const lodTexture = new BaseTexture(null);
lodTexture._isCube = true;
lodTexture._texture = glTextureFromLod;
lodTextures[mipmapIndex] = lodTexture;
switch (i) {
case 0:
texture._lodTextureLow = lodTexture;
break;
case 1:
texture._lodTextureMid = lodTexture;
break;
case 2:
texture._lodTextureHigh = lodTexture;
break;
}
}
}
}
const promises = [];
// All mipmaps up to provided number of images
for (let i = 0; i < imageData.length; i++) {
// All faces
for (let face = 0; face < 6; face++) {
// Constructs an image element from image data
const bytes = imageData[i][face];
const blob = new Blob([bytes], { type: imageType });
const url = URL.createObjectURL(blob);
let promise;
if (engine._features.forceBitmapOverHTMLImageElement) {
// eslint-disable-next-line github/no-then
promise = engine.createImageBitmap(blob, { premultiplyAlpha: "none" }).then(async (img) => {
return await _OnImageReadyAsync(img, engine, expandTexture, rgbdPostProcess, url, face, i, generateNonLODTextures, lodTextures, cubeRtt, texture);
});
}
else {
const image = new Image();
image.src = url;
// Enqueue promise to upload to the texture.
promise = new Promise((resolve, reject) => {
image.onload = () => {
_OnImageReadyAsync(image, engine, expandTexture, rgbdPostProcess, url, face, i, generateNonLODTextures, lodTextures, cubeRtt, texture)
// eslint-disable-next-line github/no-then
.then(() => resolve())
// eslint-disable-next-line github/no-then
.catch((reason) => {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(reason);
});
};
image.onerror = (error) => {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
reject(error);
};
});
}
promises.push(promise);
}
}
await Promise.all(promises);
// Fill remaining mipmaps with black textures.
if (imageData.length < mipmapsCount) {
let data;
const size = Math.pow(2, mipmapsCount - 1 - imageData.length);
const dataLength = size * size * 4;
switch (texture.type) {
case 0: {
data = new Uint8Array(dataLength);
break;
}
case 2: {
data = new Uint16Array(dataLength);
break;
}
case 1: {
data = new Float32Array(dataLength);
break;
}
}
for (let i = imageData.length; i < mipmapsCount; i++) {
for (let face = 0; face < 6; face++) {
engine._uploadArrayBufferViewToTexture(cubeRtt?.texture || texture, data, face, i);
}
}
}
// Release temp RTT.
if (cubeRtt) {
const irradiance = texture._irradianceTexture;
texture._irradianceTexture = null;
engine._releaseTexture(texture);
cubeRtt._swapAndDie(texture);
texture._irradianceTexture = irradiance;
}
// Release temp Post Process.
if (rgbdPostProcess) {
rgbdPostProcess.dispose();
}
// Flag internal texture as ready in case they are in use.
if (generateNonLODTextures) {
if (texture._lodTextureHigh && texture._lodTextureHigh._texture) {
texture._lodTextureHigh._texture.isReady = true;
}
if (texture._lodTextureMid && texture._lodTextureMid._texture) {
texture._lodTextureMid._texture.isReady = true;
}
if (texture._lodTextureLow && texture._lodTextureLow._texture) {
texture._lodTextureLow._texture.isReady = true;
}
}
}
/**
* Uploads spherical polynomials information to the texture.
* @param texture defines the texture we are trying to upload the information to
* @param info defines the environment texture info retrieved through the GetEnvInfo method
*/
export function UploadEnvSpherical(texture, info) {
info = normalizeEnvInfo(info);
const irradianceInfo = info.irradiance;
if (!irradianceInfo) {
return;
}
const sp = new SphericalPolynomial();
Vector3.FromArrayToRef(irradianceInfo.x, 0, sp.x);
Vector3.FromArrayToRef(irradianceInfo.y, 0, sp.y);
Vector3.FromArrayToRef(irradianceInfo.z, 0, sp.z);
Vector3.FromArrayToRef(irradianceInfo.xx, 0, sp.xx);
Vector3.FromArrayToRef(irradianceInfo.yy, 0, sp.yy);
Vector3.FromArrayToRef(irradianceInfo.zz, 0, sp.zz);
Vector3.FromArrayToRef(irradianceInfo.yz, 0, sp.yz);
Vector3.FromArrayToRef(irradianceInfo.zx, 0, sp.zx);
Vector3.FromArrayToRef(irradianceInfo.xy, 0, sp.xy);
texture._sphericalPolynomial = sp;
}
/**
* @internal
*/
// eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
export function _UpdateRGBDAsync(internalTexture, data, sphericalPolynomial, lodScale, lodOffset) {
const proxy = internalTexture
.getEngine()
.createRawCubeTexture(null, internalTexture.width, internalTexture.format, internalTexture.type, internalTexture.generateMipMaps, internalTexture.invertY, internalTexture.samplingMode, internalTexture._compression);
// eslint-disable-next-line github/no-then
const proxyPromise = UploadRadianceLevelsAsync(proxy, data).then(() => internalTexture);
internalTexture.onRebuildCallback = (_internalTexture) => {
return {
proxy: proxyPromise,
isReady: true,
isAsync: true,
};
};
internalTexture._source = 13 /* InternalTextureSource.CubeRawRGBD */;
internalTexture._bufferViewArrayArray = data;
internalTexture._lodGenerationScale = lodScale;
internalTexture._lodGenerationOffset = lodOffset;
internalTexture._sphericalPolynomial = sphericalPolynomial;
// eslint-disable-next-line github/no-then
return UploadRadianceLevelsAsync(internalTexture, data).then(() => {
internalTexture.isReady = true;
return internalTexture;
});
}
/**
* Sets of helpers addressing the serialization and deserialization of environment texture
* stored in a BabylonJS env file.
* Those files are usually stored as .env files.
*/
export const EnvironmentTextureTools = {
/**
* Gets the environment info from an env file.
* @param data The array buffer containing the .env bytes.
* @returns the environment file info (the json header) if successfully parsed, normalized to the latest supported version.
*/
GetEnvInfo,
/**
* Creates an environment texture from a loaded cube texture.
* @param texture defines the cube texture to convert in env file
* @param options options for the conversion process
* @param options.imageType the mime type for the encoded images, with support for "image/png" (default) and "image/webp"
* @param options.imageQuality the image quality of encoded WebP images.
* @returns a promise containing the environment data if successful.
*/
CreateEnvTextureAsync,
/**
* Creates the ArrayBufferViews used for initializing environment texture image data.
* @param data the image data
* @param info parameters that determine what views will be created for accessing the underlying buffer
* @returns the views described by info providing access to the underlying buffer
*/
CreateRadianceImageDataArrayBufferViews,
/**
* Creates the ArrayBufferViews used for initializing environment texture image data.
* @param data the image data
* @param info parameters that determine what views will be created for accessing the underlying buffer
* @returns the views described by info providing access to the underlying buffer
*/
CreateIrradianceImageDataArrayBufferViews,
/**
* Uploads the texture info contained in the env file to the GPU.
* @param texture defines the internal texture to upload to
* @param data defines the data to load
* @param info defines the texture info retrieved through the GetEnvInfo method
* @returns a promise
*/
UploadEnvLevelsAsync,
/**
* Uploads the levels of image data to the GPU.
* @param texture defines the internal texture to upload to
* @param imageData defines the array buffer views of image data [mipmap][face]
* @param imageType the mime type of the image data
* @returns a promise
*/
UploadRadianceLevelsAsync,
/**
* Uploads the levels of image data to the GPU.
* @param texture defines the internal texture to upload to
* @param imageData defines the array buffer views of image data [mipmap][face]
* @param imageType the mime type of the image data
* @param dominantDirection the dominant direction of light in the environment texture, if available
* @returns a promise
*/
UploadIrradianceLevelsAsync,
/**
* Uploads spherical polynomials information to the texture.
* @param texture defines the texture we are trying to upload the information to
* @param info defines the environment texture info retrieved through the GetEnvInfo method
*/
UploadEnvSpherical,
};
//# sourceMappingURL=environmentTextureTools.js.map