@babylonjs/viewer
Version:
The Babylon Viewer aims to simplify a specific but common Babylon.js use case: loading, viewing, and interacting with a 3D model.
1,036 lines (1,023 loc) • 70.9 kB
JavaScript
import { cA as ThinEngine, C as Constants, w as InternalTexture, y as Logger, aN as IsExponentOfTwo, cD as allocateAndCopyTypedBuffer, cE as RegisterEngineDynamicBuffer, cB as GetExponentOfTwo, cn as HasStencilAspect, p as BaseTexture, cF as WebGLDataBuffer, cG as Engine } from './index-MZPybX0H.esm.js';
import { R as RenderTargetWrapper } from './renderTargetWrapper-CR-wj86S.esm.js';
import { a as SphericalPolynomial } from './sphericalPolynomial.pure-BJkHu_AG.esm.js';
import './textureLoaderManager-l5zAgBPe.esm.js';
let _Registered$a = false;
/**
* Register side effects for enginesExtensionsEngineAlpha.
* Safe to call multiple times; only the first call has an effect.
*/
function RegisterEnginesExtensionsEngineAlpha() {
if (_Registered$a) {
return;
}
_Registered$a = true;
ThinEngine.prototype.setAlphaMode = function (mode, noDepthWriteChange = false, targetIndex = 0) {
if (this._alphaMode[targetIndex] === mode) {
if (!noDepthWriteChange) {
// Make sure we still have the correct depth mask according to the alpha mode (a transparent material could have forced writting to the depth buffer, for instance)
const depthMask = mode === Constants.ALPHA_DISABLE;
if (this.depthCullingState.depthMask !== depthMask) {
this.depthCullingState.depthMask = depthMask;
}
}
return;
}
const alphaBlendDisabled = mode === Constants.ALPHA_DISABLE;
this._alphaState.setAlphaBlend(!alphaBlendDisabled, targetIndex);
this._alphaState.setAlphaMode(mode, targetIndex);
if (!noDepthWriteChange) {
this.depthCullingState.depthMask = alphaBlendDisabled;
}
this._alphaMode[targetIndex] = mode;
};
}
RegisterEnginesExtensionsEngineAlpha();
/** This file must only contain pure code and pure imports */
let _Registered$9 = false;
/**
* Registers alpha-to-coverage support for WebGL engines.
* Safe to call multiple times; only the first call has an effect.
*/
function RegisterEnginesExtensionsEngineAlphaToCoverage() {
if (_Registered$9) {
return;
}
_Registered$9 = true;
const alphaToCoverageState = new WeakMap();
const alphaToCoverageContext = new WeakMap();
const mainPassSampleCount = new WeakMap();
const mainPassSampleCountContext = new WeakMap();
ThinEngine.prototype.getAlphaToCoverage = function () {
return alphaToCoverageState.get(this) ?? false;
};
ThinEngine.prototype.setAlphaToCoverage = function (enable) {
if (alphaToCoverageState.get(this) === enable && (!this._gl || alphaToCoverageContext.get(this) === this._gl)) {
return;
}
alphaToCoverageState.set(this, enable);
if (!this._gl) {
return;
}
if (enable) {
this._gl.enable(this._gl.SAMPLE_ALPHA_TO_COVERAGE);
}
else {
this._gl.disable(this._gl.SAMPLE_ALPHA_TO_COVERAGE);
}
alphaToCoverageContext.set(this, this._gl);
};
Object.defineProperty(ThinEngine.prototype, "currentSampleCount", {
get: function () {
if (this._currentRenderTarget) {
return this._currentRenderTarget.samples;
}
if (!this._gl) {
return 1;
}
if (mainPassSampleCountContext.get(this) !== this._gl) {
mainPassSampleCount.set(this, this._gl.getContextAttributes()?.antialias ? Math.max(1, this._gl.getParameter(this._gl.SAMPLES)) : 1);
mainPassSampleCountContext.set(this, this._gl);
}
return mainPassSampleCount.get(this);
},
enumerable: false,
configurable: true,
});
}
RegisterEnginesExtensionsEngineAlphaToCoverage();
/** This file must only contain pure code and pure imports */
/**
* @internal
*/
/**
* Create a function for createRawTexture3D/createRawTexture2DArray
* @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
* @internal
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
/**
* Create a function for updateRawTexture3D/updateRawTexture2DArray
* @param is3D true for TEXTURE_3D and false for TEXTURE_2D_ARRAY
* @internal
*/
let _Registered$8 = false;
/**
* Register side effects for enginesExtensionsEngineRawTexture.
* Safe to call multiple times; only the first call has an effect.
*/
function RegisterEnginesExtensionsEngineRawTexture() {
if (_Registered$8) {
return;
}
_Registered$8 = true;
// eslint-disable-next-line @typescript-eslint/naming-convention
function ConvertRGBtoRGBATextureData(rgbData, width, height, textureType) {
// Create new RGBA data container.
let rgbaData;
let val1 = 1;
if (textureType === Constants.TEXTURETYPE_FLOAT) {
rgbaData = new Float32Array(width * height * 4);
}
else if (textureType === Constants.TEXTURETYPE_HALF_FLOAT) {
rgbaData = new Uint16Array(width * height * 4);
val1 = 15360; // 15360 is the encoding of 1 in half float
}
else if (textureType === Constants.TEXTURETYPE_UNSIGNED_INTEGER) {
rgbaData = new Uint32Array(width * height * 4);
}
else {
rgbaData = new Uint8Array(width * height * 4);
}
// Convert each pixel.
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const index = (y * width + x) * 3;
const newIndex = (y * width + x) * 4;
// Map Old Value to new value.
rgbaData[newIndex + 0] = rgbData[index + 0];
rgbaData[newIndex + 1] = rgbData[index + 1];
rgbaData[newIndex + 2] = rgbData[index + 2];
// Add fully opaque alpha channel.
rgbaData[newIndex + 3] = val1;
}
}
return rgbaData;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
function MakeCreateRawTextureFunction(is3D) {
return function (data, width, height, depth, format, generateMipMaps, invertY, samplingMode, compression = null, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE) {
const target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
const source = is3D ? 10 /* InternalTextureSource.Raw3D */ : 11 /* InternalTextureSource.Raw2DArray */;
const texture = new InternalTexture(this, source);
texture.baseWidth = width;
texture.baseHeight = height;
texture.baseDepth = depth;
texture.width = width;
texture.height = height;
texture.depth = depth;
texture.format = format;
texture.type = textureType;
texture.generateMipMaps = generateMipMaps;
texture.samplingMode = samplingMode;
if (is3D) {
texture.is3D = true;
}
else {
texture.is2DArray = true;
}
if (!this._doNotHandleContextLost) {
texture._bufferView = data;
}
if (is3D) {
this.updateRawTexture3D(texture, data, format, invertY, compression, textureType);
}
else {
this.updateRawTexture2DArray(texture, data, format, invertY, compression, textureType);
}
this._bindTextureDirectly(target, texture, true);
// Filters
const filters = this._getSamplingParameters(samplingMode, generateMipMaps);
this._gl.texParameteri(target, this._gl.TEXTURE_MAG_FILTER, filters.mag);
this._gl.texParameteri(target, this._gl.TEXTURE_MIN_FILTER, filters.min);
if (generateMipMaps) {
this._gl.generateMipmap(target);
}
this._bindTextureDirectly(target, null);
this._internalTexturesCache.push(texture);
return texture;
};
}
// eslint-disable-next-line @typescript-eslint/naming-convention
function MakeUpdateRawTextureFunction(is3D) {
return function (texture, data, format, invertY, compression = null, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE) {
const target = is3D ? this._gl.TEXTURE_3D : this._gl.TEXTURE_2D_ARRAY;
const internalType = this._getWebGLTextureType(textureType);
const internalFormat = this._getInternalFormat(format);
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(textureType, format);
this._bindTextureDirectly(target, texture, true);
this._unpackFlipY(invertY === undefined ? true : invertY ? true : false);
if (!this._doNotHandleContextLost) {
texture._bufferView = data;
texture.format = format;
texture.invertY = invertY;
texture._compression = compression;
}
if (texture.width % 4 !== 0) {
this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
}
if (compression && data) {
this._gl.compressedTexImage3D(target, 0, this.getCaps().s3tc[compression], texture.width, texture.height, texture.depth, 0, data);
}
else {
this._gl.texImage3D(target, 0, internalSizedFomat, texture.width, texture.height, texture.depth, 0, internalFormat, internalType, data);
}
if (texture.generateMipMaps) {
this._gl.generateMipmap(target);
}
this._bindTextureDirectly(target, null);
// this.resetTextureCache();
texture.isReady = true;
};
}
ThinEngine.prototype.updateRawTexture = function (texture, data, format, invertY, compression = null, type = Constants.TEXTURETYPE_UNSIGNED_BYTE, useSRGBBuffer = false) {
if (!texture) {
return;
}
// Babylon's internalSizedFomat but gl's texImage2D internalFormat
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type, format, useSRGBBuffer);
// Babylon's internalFormat but gl's texImage2D format
const internalFormat = this._getInternalFormat(format);
const textureType = this._getWebGLTextureType(type);
this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
this._unpackFlipY(invertY === undefined ? true : invertY ? true : false);
if (!this._doNotHandleContextLost) {
texture._bufferView = data;
texture.format = format;
texture.type = type;
texture.invertY = invertY;
texture._compression = compression;
}
if (texture.width % 4 !== 0) {
this._gl.pixelStorei(this._gl.UNPACK_ALIGNMENT, 1);
}
if (compression && data) {
this._gl.compressedTexImage2D(this._gl.TEXTURE_2D, 0, this.getCaps().s3tc[compression], texture.width, texture.height, 0, data);
}
else {
this._gl.texImage2D(this._gl.TEXTURE_2D, 0, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, data);
}
if (texture.generateMipMaps) {
this._gl.generateMipmap(this._gl.TEXTURE_2D);
}
this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
// this.resetTextureCache();
texture.isReady = true;
};
ThinEngine.prototype.createRawTexture = function (data, width, height, format, generateMipMaps, invertY, samplingMode, compression = null, type = Constants.TEXTURETYPE_UNSIGNED_BYTE,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
creationFlags = 0, useSRGBBuffer = false) {
const texture = new InternalTexture(this, 3 /* InternalTextureSource.Raw */);
texture.baseWidth = width;
texture.baseHeight = height;
texture.width = width;
texture.height = height;
texture.format = format;
texture.generateMipMaps = generateMipMaps;
texture.samplingMode = samplingMode;
texture.invertY = invertY;
texture._compression = compression;
texture.type = type;
texture._useSRGBBuffer = this._getUseSRGBBuffer(useSRGBBuffer, !generateMipMaps);
if (!this._doNotHandleContextLost) {
texture._bufferView = data;
}
this.updateRawTexture(texture, data, format, invertY, compression, type, texture._useSRGBBuffer);
this._bindTextureDirectly(this._gl.TEXTURE_2D, texture, true);
// Filters
const filters = this._getSamplingParameters(samplingMode, generateMipMaps);
this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, filters.mag);
this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, filters.min);
if (generateMipMaps) {
this._gl.generateMipmap(this._gl.TEXTURE_2D);
}
this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
this._internalTexturesCache.push(texture);
return texture;
};
ThinEngine.prototype.createRawCubeTexture = function (data, size, format, type, generateMipMaps, invertY, samplingMode, compression = null) {
const gl = this._gl;
const texture = new InternalTexture(this, 8 /* InternalTextureSource.CubeRaw */);
texture.isCube = true;
texture.format = format;
texture.type = type;
if (!this._doNotHandleContextLost) {
texture._bufferViewArray = data;
}
const textureType = this._getWebGLTextureType(type);
let internalFormat = this._getInternalFormat(format);
if (internalFormat === gl.RGB) {
internalFormat = gl.RGBA;
}
// Mipmap generation needs a sized internal format that is both color-renderable and texture-filterable
if (textureType === gl.FLOAT && !this._caps.textureFloatLinearFiltering) {
generateMipMaps = false;
samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
Logger.Warn("Float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively.");
}
else if (textureType === this._gl.HALF_FLOAT_OES && !this._caps.textureHalfFloatLinearFiltering) {
generateMipMaps = false;
samplingMode = Constants.TEXTURE_NEAREST_SAMPLINGMODE;
Logger.Warn("Half float texture filtering is not supported. Mipmap generation and sampling mode are forced to false and TEXTURE_NEAREST_SAMPLINGMODE, respectively.");
}
else if (textureType === gl.FLOAT && !this._caps.textureFloatRender) {
generateMipMaps = false;
Logger.Warn("Render to float textures is not supported. Mipmap generation forced to false.");
}
else if (textureType === gl.HALF_FLOAT && !this._caps.colorBufferFloat) {
generateMipMaps = false;
Logger.Warn("Render to half float textures is not supported. Mipmap generation forced to false.");
}
const width = size;
const height = width;
texture.width = width;
texture.height = height;
texture.invertY = invertY;
texture._compression = compression;
// Double check on POT to generate Mips.
const isPot = !this.needPOTTextures || (IsExponentOfTwo(texture.width) && IsExponentOfTwo(texture.height));
if (!isPot) {
generateMipMaps = false;
}
// Upload data if needed. The texture won't be ready until then.
if (data) {
this.updateRawCubeTexture(texture, data, format, type, invertY, compression);
}
else {
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
const level = 0;
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
if (compression) {
gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, this.getCaps().s3tc[compression], texture.width, texture.height, 0, undefined);
}
else {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, null);
}
}
this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
}
this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture, true);
// Filters
if (data && generateMipMaps) {
this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP);
}
const filters = this._getSamplingParameters(samplingMode, generateMipMaps);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, filters.mag);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, filters.min);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
texture.generateMipMaps = generateMipMaps;
texture.samplingMode = samplingMode;
texture.isReady = true;
return texture;
};
ThinEngine.prototype.updateRawCubeTexture = function (texture, data, format, type, invertY, compression = null, level = 0) {
texture._bufferViewArray = data;
texture.format = format;
texture.type = type;
texture.invertY = invertY;
texture._compression = compression;
const gl = this._gl;
const textureType = this._getWebGLTextureType(type);
let internalFormat = this._getInternalFormat(format);
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
let needConversion = false;
if (internalFormat === gl.RGB) {
internalFormat = gl.RGBA;
needConversion = true;
}
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
this._unpackFlipY(invertY === undefined ? true : invertY ? true : false);
if (texture.width % 4 !== 0) {
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
}
// Data are known to be in +X +Y +Z -X -Y -Z
for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
let faceData = data[faceIndex];
if (compression) {
gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, this.getCaps().s3tc[compression], texture.width, texture.height, 0, faceData);
}
else {
if (needConversion) {
faceData = ConvertRGBtoRGBATextureData(faceData, texture.width, texture.height, type);
}
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, internalSizedFomat, texture.width, texture.height, 0, internalFormat, textureType, faceData);
}
}
const isPot = !this.needPOTTextures || (IsExponentOfTwo(texture.width) && IsExponentOfTwo(texture.height));
if (isPot && texture.generateMipMaps && level === 0) {
this._gl.generateMipmap(this._gl.TEXTURE_CUBE_MAP);
}
this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
// this.resetTextureCache();
texture.isReady = true;
};
ThinEngine.prototype.createRawCubeTextureFromUrl = function (url, scene, size, format, type, noMipmap, callback, mipmapGenerator, onLoad = null, onError = null, samplingMode = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, invertY = false) {
const gl = this._gl;
const texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
scene?.addPendingData(texture);
texture.url = url;
texture.isReady = false;
this._internalTexturesCache.push(texture);
const onerror = (request, exception) => {
scene?.removePendingData(texture);
if (onError) {
onError(request ? request.status + " " + request.statusText : "Failed to parse texture data", exception);
}
};
const internalCallbackAsync = async (data) => {
// If the texture has been disposed
if (!texture._hardwareTexture) {
return;
}
const faceDataArraysResult = callback(data);
if (!faceDataArraysResult) {
return;
}
const faceDataArrays = faceDataArraysResult instanceof Promise ? await faceDataArraysResult : faceDataArraysResult;
const width = texture.width;
if (mipmapGenerator) {
const textureType = this._getWebGLTextureType(type);
let internalFormat = this._getInternalFormat(format);
const internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
let needConversion = false;
if (internalFormat === gl.RGB) {
internalFormat = gl.RGBA;
needConversion = true;
}
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
this._unpackFlipY(false);
const mipData = mipmapGenerator(faceDataArrays);
for (let level = 0; level < mipData.length; level++) {
const mipSize = width >> level;
for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
let mipFaceData = mipData[level][faceIndex];
if (needConversion) {
mipFaceData = ConvertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type);
}
gl.texImage2D(faceIndex, level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipFaceData);
}
}
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
}
else {
this.updateRawCubeTexture(texture, faceDataArrays, format, type, invertY);
}
texture.isReady = true;
// this.resetTextureCache();
scene?.removePendingData(texture);
texture.onLoadedObservable.notifyObservers(texture);
texture.onLoadedObservable.clear();
if (onLoad) {
onLoad();
}
};
this._loadFile(url, (data) => {
// eslint-disable-next-line github/no-then
internalCallbackAsync(data).catch((err) => {
onerror(undefined, err);
});
}, undefined, scene?.offlineProvider, true, onerror);
return texture;
};
ThinEngine.prototype.createRawTexture2DArray = MakeCreateRawTextureFunction(false);
ThinEngine.prototype.createRawTexture3D = MakeCreateRawTextureFunction(true);
ThinEngine.prototype.updateRawTexture2DArray = MakeUpdateRawTextureFunction(false);
ThinEngine.prototype.updateRawTexture3D = MakeUpdateRawTextureFunction(true);
}
RegisterEnginesExtensionsEngineRawTexture();
/** This file must only contain pure code and pure imports */
let _Registered$7 = false;
/**
* Register side effects for enginesExtensionsEngineReadTexture.
* Safe to call multiple times; only the first call has an effect.
*/
function RegisterEnginesExtensionsEngineReadTexture() {
if (_Registered$7) {
return;
}
_Registered$7 = true;
ThinEngine.prototype._readTexturePixelsSync = function (texture, width, height, faceIndex = -1, level = 0, buffer = null, flushRenderer = true, noDataConversion = false, x = 0, y = 0) {
const gl = this._gl;
if (!gl) {
throw new Error("Engine does not have gl rendering context.");
}
if (!this._dummyFramebuffer) {
const dummy = gl.createFramebuffer();
if (!dummy) {
throw new Error("Unable to create dummy framebuffer");
}
this._dummyFramebuffer = dummy;
}
gl.bindFramebuffer(gl.FRAMEBUFFER, this._dummyFramebuffer);
if (faceIndex > -1 && (texture.is2DArray || texture.is3D)) {
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture._hardwareTexture?.underlyingResource, level, faceIndex);
}
else if (faceIndex > -1) {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture._hardwareTexture?.underlyingResource, level);
}
else {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture._hardwareTexture?.underlyingResource, level);
}
let readType = texture.type !== undefined ? this._getWebGLTextureType(texture.type) : gl.UNSIGNED_BYTE;
if (!noDataConversion) {
switch (readType) {
case gl.UNSIGNED_BYTE:
if (!buffer) {
buffer = new Uint8Array(4 * width * height);
}
readType = gl.UNSIGNED_BYTE;
break;
default:
if (!buffer) {
buffer = new Float32Array(4 * width * height);
}
readType = gl.FLOAT;
break;
}
}
else if (!buffer) {
buffer = allocateAndCopyTypedBuffer(texture.type, 4 * width * height);
}
if (flushRenderer) {
this.flushFramebuffer();
}
gl.readPixels(x, y, width, height, gl.RGBA, readType, buffer);
gl.bindFramebuffer(gl.FRAMEBUFFER, this._currentFramebuffer);
return buffer;
};
// eslint-disable-next-line @typescript-eslint/promise-function-async
ThinEngine.prototype._readTexturePixels = function (texture, width, height, faceIndex = -1, level = 0, buffer = null, flushRenderer = true, noDataConversion = false, x = 0, y = 0) {
return Promise.resolve(this._readTexturePixelsSync(texture, width, height, faceIndex, level, buffer, flushRenderer, noDataConversion, x, y));
};
}
RegisterEnginesExtensionsEngineReadTexture();
RegisterEngineDynamicBuffer();
/** This file must only contain pure code and pure imports */
let _Registered$6 = false;
/**
* Register side effects for enginesExtensionsEngineCubeTexture.
* Safe to call multiple times; only the first call has an effect.
*/
function RegisterEnginesExtensionsEngineCubeTexture() {
if (_Registered$6) {
return;
}
_Registered$6 = true;
ThinEngine.prototype._createDepthStencilCubeTexture = function (size, options) {
const internalTexture = new InternalTexture(this, 12 /* InternalTextureSource.DepthStencil */);
internalTexture.isCube = true;
if (this.webGLVersion === 1) {
Logger.Error("Depth cube texture is not supported by WebGL 1.");
return internalTexture;
}
const internalOptions = {
bilinearFiltering: false,
comparisonFunction: 0,
generateStencil: false,
...options,
};
const gl = this._gl;
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, internalTexture, true);
this._setupDepthStencilTexture(internalTexture, size, internalOptions.bilinearFiltering, internalOptions.comparisonFunction);
// Create the depth/stencil buffer
for (let face = 0; face < 6; face++) {
if (internalOptions.generateStencil) {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, gl.DEPTH24_STENCIL8, size, size, 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, null);
}
else {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, gl.DEPTH_COMPONENT24, size, size, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
}
}
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
this._internalTexturesCache.push(internalTexture);
return internalTexture;
};
ThinEngine.prototype._setCubeMapTextureParams = function (texture, loadMipmap, maxLevel) {
const gl = this._gl;
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
texture.samplingMode = loadMipmap ? Constants.TEXTURE_TRILINEAR_SAMPLINGMODE : Constants.TEXTURE_LINEAR_LINEAR;
if (loadMipmap && this.getCaps().textureMaxLevel && maxLevel !== undefined && maxLevel > 0) {
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAX_LEVEL, maxLevel);
texture._maxLodLevel = maxLevel;
}
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
};
ThinEngine.prototype.createCubeTexture = function (rootUrl, scene, files, noMipmap, onLoad = null, onError = null, format, forcedExtension = null, createPolynomials = false, lodScale = 0, lodOffset = 0, fallback = null, loaderOptions, useSRGBBuffer = false, buffer = null) {
const gl = this._gl;
return this.createCubeTextureBase(rootUrl, scene, files, !!noMipmap, onLoad, onError, format, forcedExtension, createPolynomials, lodScale, lodOffset, fallback, (texture) => this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true), (texture, imgs) => {
const width = this.needPOTTextures ? GetExponentOfTwo(imgs[0].width, this._caps.maxCubemapTextureSize) : imgs[0].width;
const height = width;
const faces = [
gl.TEXTURE_CUBE_MAP_POSITIVE_X,
gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
];
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
this._unpackFlipY(false);
const internalFormat = format
? this._getInternalFormat(format, texture._useSRGBBuffer)
: texture._useSRGBBuffer
? this._glSRGBExtensionValues.SRGB8_ALPHA8
: gl.RGBA;
let texelFormat = format ? this._getInternalFormat(format) : gl.RGBA;
if (texture._useSRGBBuffer && this.webGLVersion === 1) {
texelFormat = internalFormat;
}
for (let index = 0; index < faces.length; index++) {
if (imgs[index].width !== width || imgs[index].height !== height) {
this._prepareWorkingCanvas();
if (!this._workingCanvas || !this._workingContext) {
Logger.Warn("Cannot create canvas to resize texture.");
return;
}
this._workingCanvas.width = width;
this._workingCanvas.height = height;
this._workingContext.drawImage(imgs[index], 0, 0, imgs[index].width, imgs[index].height, 0, 0, width, height);
gl.texImage2D(faces[index], 0, internalFormat, texelFormat, gl.UNSIGNED_BYTE, this._workingCanvas);
}
else {
gl.texImage2D(faces[index], 0, internalFormat, texelFormat, gl.UNSIGNED_BYTE, imgs[index]);
}
}
if (!noMipmap) {
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
}
this._setCubeMapTextureParams(texture, !noMipmap);
texture.width = width;
texture.height = height;
texture.isReady = true;
if (format) {
texture.format = format;
}
texture.onLoadedObservable.notifyObservers(texture);
texture.onLoadedObservable.clear();
if (onLoad) {
onLoad();
}
}, !!useSRGBBuffer, buffer);
};
ThinEngine.prototype.generateMipMapsForCubemap = function (texture, unbind = true) {
if (texture.generateMipMaps) {
const gl = this._gl;
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
if (unbind) {
this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
}
}
};
}
RegisterEnginesExtensionsEngineCubeTexture();
/** @internal */
class WebGLRenderTargetWrapper extends RenderTargetWrapper {
setDepthStencilTexture(texture, disposeExisting = true) {
super.setDepthStencilTexture(texture, disposeExisting);
if (!texture) {
return;
}
const engine = this._engine;
const gl = this._context;
const hardwareTexture = texture._hardwareTexture;
if (hardwareTexture && texture._autoMSAAManagement && this._MSAAFramebuffer) {
const currentFb = engine._currentFramebuffer;
engine._bindUnboundFramebuffer(this._MSAAFramebuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, HasStencilAspect(texture.format) ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, hardwareTexture.getMSAARenderBuffer());
engine._bindUnboundFramebuffer(currentFb);
}
}
constructor(isMulti, isCube, size, engine, context) {
super(isMulti, isCube, size, engine);
/**
* @internal
*/
this._framebuffer = null;
/**
* @internal
*/
this._depthStencilBuffer = null;
// eslint-disable-next-line @typescript-eslint/naming-convention
/**
* @internal
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
this._MSAAFramebuffer = null;
// Multiview
/**
* @internal
*/
this._colorTextureArray = null;
/**
* @internal
*/
this._depthStencilTextureArray = null;
/**
* @internal
*/
this._disposeOnlyFramebuffers = false;
/**
* @internal
*/
this._currentLOD = 0;
this._context = context;
}
_cloneRenderTargetWrapper() {
let rtw;
if (this._colorTextureArray && this._depthStencilTextureArray) {
rtw = this._engine.createMultiviewRenderTargetTexture(this.width, this.height);
rtw.texture.isReady = true;
}
else {
rtw = super._cloneRenderTargetWrapper();
}
return rtw;
}
_swapRenderTargetWrapper(target) {
super._swapRenderTargetWrapper(target);
target._framebuffer = this._framebuffer;
target._depthStencilBuffer = this._depthStencilBuffer;
target._MSAAFramebuffer = this._MSAAFramebuffer;
target._colorTextureArray = this._colorTextureArray;
target._depthStencilTextureArray = this._depthStencilTextureArray;
this._framebuffer = this._depthStencilBuffer = this._MSAAFramebuffer = this._colorTextureArray = this._depthStencilTextureArray = null;
}
/**
* Creates the depth/stencil texture
* @param comparisonFunction Comparison function to use for the texture
* @param bilinearFiltering true if bilinear filtering should be used when sampling the texture
* @param generateStencil true if the stencil aspect should also be created
* @param samples sample count to use when creating the texture
* @param format format of the depth texture
* @param label defines the label to use for the texture (for debugging purpose only)
* @returns the depth/stencil created texture
*/
createDepthStencilTexture(comparisonFunction = 0, bilinearFiltering = true, generateStencil = false, samples = 1, format = Constants.TEXTUREFORMAT_DEPTH32_FLOAT, label) {
if (this._depthStencilBuffer) {
const engine = this._engine;
// Dispose previous depth/stencil render buffers and clear the corresponding attachment.
// Next time this framebuffer is bound, the new depth/stencil texture will be attached.
const currentFrameBuffer = engine._currentFramebuffer;
const gl = this._context;
engine._bindUnboundFramebuffer(this._framebuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
engine._bindUnboundFramebuffer(currentFrameBuffer);
gl.deleteRenderbuffer(this._depthStencilBuffer);
this._depthStencilBuffer = null;
}
return super.createDepthStencilTexture(comparisonFunction, bilinearFiltering, generateStencil, samples, format, label);
}
/**
* Shares the depth buffer of this render target with another render target.
* @param renderTarget Destination renderTarget
*/
shareDepth(renderTarget) {
super.shareDepth(renderTarget);
const gl = this._context;
const depthbuffer = this._depthStencilBuffer;
const framebuffer = renderTarget._MSAAFramebuffer || renderTarget._framebuffer;
const engine = this._engine;
if (renderTarget._depthStencilBuffer && renderTarget._depthStencilBuffer !== depthbuffer) {
gl.deleteRenderbuffer(renderTarget._depthStencilBuffer);
}
renderTarget._depthStencilBuffer = depthbuffer;
const attachment = renderTarget._generateStencilBuffer ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
engine._bindUnboundFramebuffer(framebuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, depthbuffer);
engine._bindUnboundFramebuffer(null);
}
/**
* Binds a texture to this render target on a specific attachment
* @param texture The texture to bind to the framebuffer
* @param attachmentIndex Index of the attachment
* @param faceIndexOrLayer The face or layer of the texture to render to in case of cube texture or array texture
* @param lodLevel defines the lod level to bind to the frame buffer
*/
_bindTextureRenderTarget(texture, attachmentIndex = 0, faceIndexOrLayer, lodLevel = 0) {
const hardwareTexture = texture._hardwareTexture;
if (!hardwareTexture) {
return;
}
const framebuffer = this._framebuffer;
const engine = this._engine;
const currentFb = engine._currentFramebuffer;
engine._bindUnboundFramebuffer(framebuffer);
let attachment;
if (engine.webGLVersion > 1) {
const gl = this._context;
attachment = gl["COLOR_ATTACHMENT" + attachmentIndex];
if (texture.is2DArray || texture.is3D) {
faceIndexOrLayer = faceIndexOrLayer ?? this.layerIndices?.[attachmentIndex] ?? 0;
gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachment, hardwareTexture.underlyingResource, lodLevel, faceIndexOrLayer);
}
else if (texture.isCube) {
// if face index is not specified, try to query it from faceIndices
// default is face 0
faceIndexOrLayer = faceIndexOrLayer ?? this.faceIndices?.[attachmentIndex] ?? 0;
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndexOrLayer, hardwareTexture.underlyingResource, lodLevel);
}
else {
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, hardwareTexture.underlyingResource, lodLevel);
}
}
else {
// Default behavior (WebGL)
const gl = this._context;
attachment = gl["COLOR_ATTACHMENT" + attachmentIndex + "_WEBGL"];
const target = faceIndexOrLayer !== undefined ? gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndexOrLayer : gl.TEXTURE_2D;
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, target, hardwareTexture.underlyingResource, lodLevel);
}
if (texture._autoMSAAManagement && this._MSAAFramebuffer) {
const gl = this._context;
engine._bindUnboundFramebuffer(this._MSAAFramebuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, hardwareTexture.getMSAARenderBuffer());
}
engine._bindUnboundFramebuffer(currentFb);
}
/**
* Set a texture in the textures array
* @param texture the texture to set
* @param index the index in the textures array to set
* @param disposePrevious If this function should dispose the previous texture
*/
setTexture(texture, index = 0, disposePrevious = true) {
super.setTexture(texture, index, disposePrevious);
this._bindTextureRenderTarget(texture, index);
}
/**
* Sets the layer and face indices of every render target texture
* @param layers The layer of the texture to be set (make negative to not modify)
* @param faces The face of the texture to be set (make negative to not modify)
*/
setLayerAndFaceIndices(layers, faces) {
super.setLayerAndFaceIndices(layers, faces);
if (!this.textures || !this.layerIndices || !this.faceIndices) {
return;
}
// the length of this._attachments is the right one as it does not count the depth texture, in case we generated it
const textureCount = this._attachments?.length ?? this.textures.length;
for (let index = 0; index < textureCount; index++) {
const texture = this.textures[index];
if (!texture) {
// The target type was probably -1 at creation time and setTexture has not been called yet for this index
continue;
}
if (texture.is2DArray || texture.is3D) {
this._bindTextureRenderTarget(texture, index, this.layerIndices[index]);
}
else if (texture.isCube) {
this._bindTextureRenderTarget(texture, index, this.faceIndices[index]);
}
else {
this._bindTextureRenderTarget(texture, index);
}
}
}
/**
* Set the face and layer indices of a texture in the textures array
* @param index The index of the texture in the textures array to modify
* @param layer The layer of the texture to be set
* @param face The face of the texture to be set
*/
setLayerAndFaceIndex(index = 0, layer, face) {
super.setLayerAndFaceIndex(index, layer, face);
if (!this.textures || !this.layerIndices || !this.faceIndices) {
return;
}
const texture = this.textures[index];
if (texture.is2DArray || texture.is3D) {
this._bindTextureRenderTarget(this.textures[index], index, this.layerIndices[index]);
}
else if (texture.isCube) {
this._bindTextureRenderTarget(this.textures[index], index, this.faceIndices[index]);
}
}
resolveMSAATextures() {
const engine = this._engine;
const currentFramebuffer = engine._currentFramebuffer;
engine._bindUnboundFramebuffer(this._MSAAFramebuffer);
super.resolveMSAATextures();
engine._bindUnboundFramebuffer(currentFramebuffer);
}
dispose(disposeOnlyFramebuffers = this._disposeOnlyFramebuffers) {
const gl = this._context;
if (!disposeOnlyFramebuffers) {
if (this._colorTextureArray) {
this._context.deleteTexture(this._colorTextureArray);
this._colorTextureArray = null;
}
if (this._depthStencilTextureArray) {
this._context.deleteTexture(this._depthStencilTextureArray);
this._depthStencilTextureArray = null;
}
}
if (this._framebuffer) {
gl.deleteFramebuffer(this._framebuffer);
this._framebuffer = null;
}
if (this._depthStencilBuffer) {
gl.deleteRenderbuffer(this._depthStencilBuffer);
this._depthStencilBuffer = null;
}
if (this._MSAAFramebuffer) {
gl.deleteFramebuffer(this._MSAAFramebuffer);
this._MSAAFramebuffer = null;
}
super.dispose(disposeOnlyFramebuffers);
}
}
/** This file must only contain pure code and pure imports */
let _Registered$5 = false;
/**
* Register side effects for enginesExtensionsEngineRenderTarget.
* Safe to call multiple times; only the first call has an effect.
*/
function RegisterEnginesExtensionsEngineRenderTarget() {
if (_Registered$5) {
return;
}
_Registered$5 = true;
ThinEngine.prototype._createHardwareRenderTargetWrapper = function (isMulti, isCube, size) {
const rtWrapper = new WebGLRenderTargetWrapper(isMulti, isCube, size, this, this._gl);
this._renderTargetWrapperCache.push(rtWrapper);
return rtWrapper;
};
ThinEngine.prototype.createRenderTargetTexture = function (size, options) {
const rtWrapper = this._createHardwareRenderTargetWrapper(false, false, size);
let generateDepthBuffer = true;
let generateStencilBuffer = false;
let noColorAttachment = false;
let colorAttachment = undefined;
let samples = 1;
let label = undefined;
if (options !== undefined && typeof options === "object") {
generateDepthBuffer = options.generateDepthBuffer ?? true;
generateStencilBuffer = !!options.generateStencilBuffer;
noColorAttachment = !!options.noColorAttachment;
colorAttachment = options.colorAttachment;
samples = options.samples ?? 1;
label = options.label;
}
const texture = colorAttachment || (noColorAttachment ? null : this._createInternalTexture(size, options, true, 5 /* InternalTextureSource.RenderTarget */));
const width = size.width || size;
const height = size.height || size;
const currentFrameBuffer = this._currentFramebuffer;
const gl = this._gl;
// Create the framebuffer
const framebuffer = gl.createFramebuffer();
this._bindUnboundFramebuffer(framebuffer);
rtWrapper._depthStencilBuffer = this._setupFramebufferDepthAttachments(generateStencilBuffer, generateDepthBuffer, width, height);
// No need to rebind on every frame
if (texture && !texture.is2DArray && !texture.is3D) {
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture._hardwareTexture.underlyingResource, 0);
}
this._bindUnboundFramebuffer(currentFrameBuffer);
rtWrapper.label = label ?? "RenderTargetWrapper";
rtWrapper._framebuffer = framebuffer;
rtWrapper._generateDepthBuffer = generateDepthBuffer;
rtWrapper._generateStencilBuffer = generateStencilBuffer;
rtWrapper.setTextures(texture);
if (!colorAttachment) {
this.updateRenderTargetTextureSampleCount(rtWrapper, samples);
}
else {
rtWrapper._samples = colorAttachment.samples;
if (colorAttachment.samples > 1) {
const msaaRenderBuffer = colorAttachment._hardwareTexture.getMSAARenderBuffer(0);
rtWrapper._MSAAFramebuffer = gl.createFramebuffer();
this._bindUnboundFramebuffer(rtWrapper._MSAAFramebuffer);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, msaaRenderBuffer);
this._bindUnboundFramebuffer(null);
}
}
return rtWrapper;
};
ThinEngine.prototype._createDepthStencilTexture = function (size, options, rtWrapper) {
const gl = this._gl;
const layers = size.layers || 0;
const depth = size.depth || 0;
let target = gl.TEXTURE_2D;
if (layers !== 0) {
target = gl.TEXTURE_2D_ARRAY;
}
else if (depth !== 0) {
target = gl.TEXTURE_3D;
}
const internalTexture = new InternalTexture(this, 12 /* InternalTextureSource.DepthStencil */);
internalTexture.label = options.label;
if (!this._caps.depthTextureExtension) {
Logger.Error("Depth texture is not supported by your browser or hardware.");
return internalTexture;
}
const internalOptions = {
bilinearFiltering: false,
comparisonFunction: 0,
generateStencil: false,
...options,
};
this._bindTextureDirectly(target, internalTexture, true);
this._setupDepthStencilTexture(internalTexture, size, internalOptions.comparisonFunction === 0 ? false : internalOptions.bilinearFiltering, internalOptions.comparisonFunction, internalOptions.samples);
if (internalOptions.depthTextureFormat !== undefined) {
if (internalOptions.depthTextureFormat !== Constants.TEXTUREFORMAT_DEPTH16 &&
internalOptions.depthTextureFormat !== Constants.TEXTUREFORMAT_DEPTH24 &&
internalOptions.depthTextureFormat !== Constants.TEXTUREFORMAT_DEPTH24UNORM_STENCIL8 &&
internalOptions.depthTextureFormat !== Constants.TEXT