UNPKG

@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
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