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.

3,066 lines 136 kB
import { b1 as Color4, O as Observable, M as Matrix, y as Logger, c6 as EffectRenderer, c5 as EffectWrapper, aA as RenderTargetTexture, C as Constants, m as Texture, be as MultiRenderTarget, c as ShaderMaterial, bd as IsGaussianSplattingClassName, V as Vector3, aP as PostProcess, bc as Vector4, z as __classPrivateFieldGet, D as __classPrivateFieldSet, l as __runInitializers, q as __esDecorate, aw as MaterialDefines, s as serialize, H as expandToProperty, a as EngineStore, aD as Quaternion, aH as RawTexture, aY as Tools } from './index-HyNDfLMI.esm.js';
import { P as ProceduralTexture } from './proceduralTexture.pure-Csq5aHN9.esm.js';
import { G as GeometryBufferRenderer, R as RegisterGeometryBufferRendererSceneComponent } from './geometryBufferRenderer.pure-CaK79VQE.esm.js';
import { a as PostProcessRenderPipeline, P as PostProcessRenderEffect } from './postProcessRenderEffect-D3mUm7-6.esm.js';
import { R as RegisterIblCdfGeneratorSceneComponent, I as IblCdfGenerator } from './iblCdfGenerator-C0XgwuZV.esm.js';
import { M as MaterialPluginBase } from './material.detailMapConfiguration-C33hA3Hm.esm.js';
import { P as PBRBaseMaterial } from './pbrBaseMaterial.pure-5Eirh_Zs.esm.js';
import { S as StandardMaterial } from './standardMaterial.pure-B45jJn5x.esm.js';
import { OpenPBRMaterial } from './openpbrMaterial.pure-ChcQZB2S.esm.js';
import './postProcessRenderPipelineManager-DPqTdabq.esm.js';
import './cubeTexture.pure-0fDNg7hw.esm.js';
import './abstractEngine.cubeTexture.pure-CoMRqdbN.esm.js';
import './textureLoaderManager-GZAmSsDa.esm.js';
import './brdfTextureTools-CrS8Z4G9.esm.js';
import './prepass.defines-NGwPAvhm.esm.js';

/** This file must only contain pure code and pure imports */
// Max frames _renderVoxelGrid waits for splat depth sorts to settle before voxelizing anyway
// (~3s at 60fps). Bounds the wait so a continuously re-sorting (orbiting) splat can't block it.
const _MaxSortSettleWaitFrames = 180;
/**
 * Voxel-based shadow rendering for IBL's.
 * This should not be instanciated directly, as it is part of a scene component
 * @internal
 * @see https://playground.babylonjs.com/#8R5SSE#222
 */
class _IblShadowsVoxelRenderer {
    /**
     * Return the voxel grid texture.
     * @returns The voxel grid texture.
     */
    getVoxelGrid() {
        if (this._engine.isWebGPU) {
            return this._voxelGrid;
        }
        else if (this._triPlanarVoxelization) {
            return this._combinedVoxelGridPT;
        }
        else {
            return this._voxelGridZaxis;
        }
    }
    /**
     * Return the voxel render target used during voxelization.
     * @returns The voxel render target.
     */
    getRT() {
        if (this._engine.isWebGPU) {
            return this._voxelGridRT;
        }
        else if (this._triPlanarVoxelization) {
            return this._combinedVoxelGridPT;
        }
        else {
            return this._voxelGridZaxis;
        }
    }
    /**
     * Whether to use tri-planar voxelization. More expensive, but can help with artifacts.
     */
    get triPlanarVoxelization() {
        return this._triPlanarVoxelization;
    }
    /**
     * Whether to use tri-planar voxelization. More expensive, but can help with artifacts.
     */
    set triPlanarVoxelization(enabled) {
        if (this._engine.isWebGPU) {
            // WebGPU only supports tri-planar voxelization.
            this._triPlanarVoxelization = true;
            return;
        }
        if (this._triPlanarVoxelization === enabled) {
            return;
        }
        this._disposeVoxelTextures();
        this._triPlanarVoxelization = enabled;
        this._createTextures();
    }
    /**
     * Set the matrix to use for scaling the world space to voxel space
     * @param matrix The matrix to use for scaling the world space to voxel space
     */
    setWorldScaleMatrix(matrix) {
        this._invWorldScaleMatrix = matrix;
    }
    /**
     * @returns Whether voxelization is currently happening.
     */
    isVoxelizationInProgress() {
        return this._voxelizationInProgress;
    }
    /**
     * Resolution of the voxel grid. The final resolution will be 2^resolutionExp.
     */
    get voxelResolutionExp() {
        return this._voxelResolutionExp;
    }
    /**
     * Resolution of the voxel grid. The final resolution will be 2^resolutionExp.
     */
    set voxelResolutionExp(resolutionExp) {
        if (this._voxelResolutionExp === resolutionExp && this._voxelGridZaxis) {
            return;
        }
        this._voxelResolutionExp = Math.round(Math.min(Math.max(resolutionExp, 3), 9));
        this._voxelResolution = Math.pow(2.0, this._voxelResolutionExp);
        this._disposeVoxelTextures();
        this._createTextures();
    }
    /**
     * Instanciates the voxel renderer
     * @param scene Scene to attach to
     * @param iblShadowsRenderPipeline The render pipeline this pass is associated with
     * @param resolutionExp Resolution of the voxel grid. The final resolution will be 2^resolutionExp.
     * @param triPlanarVoxelization Whether to use tri-planar voxelization. Only applies to WebGL. Voxelization will take longer but will reduce missing geometry.
     * @returns The voxel renderer
     */
    constructor(scene, iblShadowsRenderPipeline, resolutionExp = 6, triPlanarVoxelization = true) {
        this._voxelMrtsXaxis = [];
        this._voxelMrtsYaxis = [];
        this._voxelMrtsZaxis = [];
        this._voxelClearColor = new Color4(0, 0, 0, 1);
        /**
         * Observable that triggers when the voxelization is complete
         */
        this.onVoxelizationCompleteObservable = new Observable();
        this._renderTargets = [];
        /** Per-mesh voxel ShaderMaterials for GaussianSplattingMesh, keyed by mesh uniqueId. */
        this._gsVoxelMaterialCache = new Map();
        this._triPlanarVoxelization = true;
        this._voxelizationInProgress = false;
        // Frames spent waiting for shadow-casting splats' depth sort to settle before voxelizing; see
        // _renderVoxelGrid. Capped so a continuously re-sorting (e.g. orbiting) splat can't starve it.
        this._sortSettleWaitFrames = 0;
        this._invWorldScaleMatrix = Matrix.Identity();
        this._voxelResolution = 64;
        this._voxelResolutionExp = 6;
        this._copyMipLayer = 0;
        this._mipArray = [];
        this._scene = scene;
        this._engine = scene.getEngine();
        this._triPlanarVoxelization = this._engine.isWebGPU || triPlanarVoxelization;
        if (!this._engine.getCaps().drawBuffersExtension) {
            Logger.Error("Can't do voxel rendering without the draw buffers extension.");
        }
        const isWebGPU = this._engine.isWebGPU;
        // Round down to a power of 2 so it evenly divides the power-of-2 voxel resolution,
        // preventing out-of-bounds layer indices in the last MRT slab.
        // This shader implementation writes up to 16 MRT outputs, so clamp to 16 to keep
        // active draw buffers aligned with declared/written fragment outputs.
        const rawMaxDrawBuffers = this._engine.getCaps().maxDrawBuffers || 0;
        const cappedMaxDrawBuffers = Math.min(rawMaxDrawBuffers, 16);
        this._maxDrawBuffers = cappedMaxDrawBuffers >= 1 ? 1 << Math.floor(Math.log2(cappedMaxDrawBuffers)) : 0;
        this._copyMipEffectRenderer = new EffectRenderer(this._engine);
        this._copyMipEffectWrapper = new EffectWrapper({
            engine: this._engine,
            fragmentShader: "copyTexture3DLayerToTexture",
            useShaderStore: true,
            uniformNames: ["layerNum"],
            samplerNames: ["textureSampler"],
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await import('./copyTexture3DLayerToTexture.fragment-B8JTk03Y.esm.js');
                }
                else {
                    await import('./copyTexture3DLayerToTexture.fragment-CifDzCTr.esm.js');
                }
            },
        });
        this._copyMipEffectWrapper.onApplyObservable.add(() => {
            const effect = this._copyMipEffectWrapper.effect;
            if (!effect || !this._copyMipSourceTexture) {
                return;
            }
            effect.setTexture("textureSampler", this._copyMipSourceTexture);
            effect.setInt("layerNum", this._copyMipLayer);
        });
        this.voxelResolutionExp = resolutionExp;
    }
    _generateMipMaps() {
        const iterations = Math.ceil(Math.log2(this._voxelResolution));
        for (let i = 1; i < iterations + 1; i++) {
            this._generateMipMap(i);
        }
    }
    _generateMipMap(lodLevel) {
        // Generate a mip map for the given level by triggering the render of the procedural mip texture.
        const mipTarget = this._mipArray[lodLevel - 1];
        if (!mipTarget) {
            return;
        }
        mipTarget.setTexture("srcMip", lodLevel === 1 ? this.getVoxelGrid() : this._mipArray[lodLevel - 2]);
        mipTarget.render();
    }
    _copyMipMaps() {
        const iterations = Math.ceil(Math.log2(this._voxelResolution));
        for (let i = 1; i < iterations + 1; i++) {
            this._copyMipMap(i);
        }
    }
    _copyMipMap(lodLevel) {
        // Now, copy this mip into the mip chain of the voxel grid.
        const mipTarget = this._mipArray[lodLevel - 1];
        if (!mipTarget) {
            return;
        }
        const voxelGrid = this.getVoxelGrid();
        let rt;
        if (voxelGrid instanceof RenderTargetTexture && voxelGrid.renderTarget) {
            rt = voxelGrid.renderTarget;
        }
        else {
            rt = voxelGrid._rtWrapper;
        }
        if (rt) {
            this._copyMipEffectRenderer.saveStates();
            const previousColorWrite = this._engine.getColorWrite();
            const previousDepthBuffer = this._engine.getDepthBuffer();
            const previousDepthWrite = this._engine.getDepthWrite();
            const previousAlphaMode = this._engine.getAlphaMode();
            this._engine.setColorWrite(true);
            this._engine.setDepthBuffer(false);
            this._engine.setDepthWrite(false);
            this._engine.setAlphaMode(Constants.ALPHA_DISABLE);
            const bindSize = mipTarget.getSize().width;
            let sourceDepth = mipTarget.getInternalTexture()?.depth;
            sourceDepth = Math.max(1, sourceDepth || bindSize);
            const destinationMipDepth = Math.max(1, this._voxelResolution >> lodLevel);
            const layersToCopy = Math.min(sourceDepth, destinationMipDepth);
            const destinationTexture = rt.texture;
            const previousGenerateMipMaps = destinationTexture?.generateMipMaps;
            if (destinationTexture) {
                destinationTexture.generateMipMaps = false;
            }
            try {
                // Render to each layer of the voxel grid.
                for (let layer = 0; layer < layersToCopy; layer++) {
                    this._engine.bindFramebuffer(rt, 0, bindSize, bindSize, true, lodLevel, layer);
                    this._copyMipSourceTexture = mipTarget;
                    this._copyMipLayer = layer;
                    this._copyMipEffectRenderer.applyEffectWrapper(this._copyMipEffectWrapper);
                    this._copyMipEffectRenderer.draw();
                    this._engine.unBindFramebuffer(rt, true);
                }
                if (!this._engine.isWebGPU) {
                    this._engine.unbindAllTextures();
                }
            }
            finally {
                if (destinationTexture && previousGenerateMipMaps !== undefined) {
                    destinationTexture.generateMipMaps = previousGenerateMipMaps;
                }
                this._engine.setAlphaMode(previousAlphaMode);
                this._engine.setDepthWrite(previousDepthWrite);
                this._engine.setDepthBuffer(previousDepthBuffer);
                this._engine.setColorWrite(previousColorWrite);
            }
            this._copyMipSourceTexture = undefined;
            this._copyMipEffectRenderer.restoreStates();
        }
    }
    _computeNumberOfSlabs() {
        return Math.ceil(this._voxelResolution / this._maxDrawBuffers);
    }
    _createTextures() {
        const isWebGPU = this._engine.isWebGPU;
        const size = {
            width: this._voxelResolution,
            height: this._voxelResolution,
            depth: this._voxelResolution,
        };
        const voxelAxisOptions = {
            generateDepthBuffer: false,
            generateMipMaps: false,
            type: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            format: Constants.TEXTUREFORMAT_R,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
        };
        // We can render up to maxDrawBuffers voxel slices of the grid per render.
        // We call this a slab.
        const numSlabs = this._computeNumberOfSlabs();
        const voxelCombinedOptions = {
            generateDepthBuffer: false,
            generateMipMaps: true,
            type: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            format: Constants.TEXTUREFORMAT_R,
            samplingMode: Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await import('./iblCombineVoxelGrids.fragment-CSkA84bA.esm.js');
                }
                else {
                    await import('./iblCombineVoxelGrids.fragment-CDBHHq70.esm.js');
                }
            },
        };
        if (this._engine.isWebGPU) {
            this._voxelGrid = new RenderTargetTexture("voxelGrid", size, this._scene, {
                ...voxelCombinedOptions,
                format: Constants.TEXTUREFORMAT_R,
                creationFlags: Constants.TEXTURE_CREATIONFLAG_STORAGE,
            });
            this._voxelGridRT = new RenderTargetTexture("voxelGridRT", { width: Math.min(size.width * 2.0, 2048), height: Math.min(size.height * 2.0, 2048) }, this._scene, voxelAxisOptions);
        }
        else if (this._triPlanarVoxelization) {
            this._voxelGridXaxis = new RenderTargetTexture("voxelGridXaxis", size, this._scene, voxelAxisOptions);
            this._voxelGridYaxis = new RenderTargetTexture("voxelGridYaxis", size, this._scene, voxelAxisOptions);
            this._voxelGridZaxis = new RenderTargetTexture("voxelGridZaxis", size, this._scene, voxelAxisOptions);
            this._voxelMrtsXaxis = this._createVoxelMRTs("x_axis_", this._voxelGridXaxis, numSlabs);
            this._voxelMrtsYaxis = this._createVoxelMRTs("y_axis_", this._voxelGridYaxis, numSlabs);
            this._voxelMrtsZaxis = this._createVoxelMRTs("z_axis_", this._voxelGridZaxis, numSlabs);
            this._combinedVoxelGridPT = new ProceduralTexture("combinedVoxelGrid", size, "iblCombineVoxelGrids", this._scene, voxelCombinedOptions, false);
            this._scene.proceduralTextures.splice(this._scene.proceduralTextures.indexOf(this._combinedVoxelGridPT), 1);
            this._combinedVoxelGridPT.setFloat("layer", 0.0);
            this._combinedVoxelGridPT.setTexture("voxelXaxisSampler", this._voxelGridXaxis);
            this._combinedVoxelGridPT.setTexture("voxelYaxisSampler", this._voxelGridYaxis);
            this._combinedVoxelGridPT.setTexture("voxelZaxisSampler", this._voxelGridZaxis);
            // We will render this only after voxelization is completed for the 3 axes.
            this._combinedVoxelGridPT.autoClear = false;
            this._combinedVoxelGridPT.wrapU = Texture.CLAMP_ADDRESSMODE;
            this._combinedVoxelGridPT.wrapV = Texture.CLAMP_ADDRESSMODE;
        }
        else {
            this._voxelGridZaxis = new RenderTargetTexture("voxelGridZaxis", size, this._scene, voxelCombinedOptions);
            this._voxelMrtsZaxis = this._createVoxelMRTs("z_axis_", this._voxelGridZaxis, numSlabs);
        }
        const generateVoxelMipOptions = {
            generateDepthBuffer: false,
            generateMipMaps: false,
            type: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            format: Constants.TEXTUREFORMAT_R,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await import('./iblGenerateVoxelMip.fragment-C3f2_BRC.esm.js');
                }
                else {
                    await import('./iblGenerateVoxelMip.fragment-Bj5_XXuq.esm.js');
                }
            },
        };
        this._mipArray = new Array(Math.ceil(Math.log2(this._voxelResolution)));
        for (let mipIdx = 1; mipIdx <= this._mipArray.length; mipIdx++) {
            const mipDim = this._voxelResolution >> mipIdx;
            const mipSize = { width: mipDim, height: mipDim, depth: mipDim };
            this._mipArray[mipIdx - 1] = new ProceduralTexture("voxelMip" + mipIdx, mipSize, "iblGenerateVoxelMip", this._scene, generateVoxelMipOptions, false);
            this._scene.proceduralTextures.splice(this._scene.proceduralTextures.indexOf(this._mipArray[mipIdx - 1]), 1);
            const mipTarget = this._mipArray[mipIdx - 1];
            mipTarget.autoClear = false;
            mipTarget.wrapU = Texture.CLAMP_ADDRESSMODE;
            mipTarget.wrapV = Texture.CLAMP_ADDRESSMODE;
            mipTarget.setTexture("srcMip", mipIdx > 1 ? this._mipArray[mipIdx - 2] : this.getVoxelGrid());
            mipTarget.setInt("layerNum", 0);
        }
        this._createVoxelMaterials();
    }
    _createVoxelMRTs(name, voxelRT, numSlabs) {
        voxelRT.wrapU = Texture.CLAMP_ADDRESSMODE;
        voxelRT.wrapV = Texture.CLAMP_ADDRESSMODE;
        voxelRT.noPrePassRenderer = true;
        const mrtArray = [];
        const targetTypes = new Array(this._maxDrawBuffers).fill(Constants.TEXTURE_3D);
        for (let mrtIndex = 0; mrtIndex < numSlabs; mrtIndex++) {
            let layerIndices = new Array(this._maxDrawBuffers).fill(0);
            layerIndices = layerIndices.map((value, index) => mrtIndex * this._maxDrawBuffers + index);
            let textureNames = new Array(this._maxDrawBuffers).fill("");
            textureNames = textureNames.map((value, index) => "voxel_grid_" + name + (mrtIndex * this._maxDrawBuffers + index));
            const mrt = new MultiRenderTarget("mrt_" + name + mrtIndex, { width: this._voxelResolution, height: this._voxelResolution, depth: this._voxelResolution }, this._maxDrawBuffers, // number of draw buffers
            this._scene, {
                types: new Array(this._maxDrawBuffers).fill(Constants.TEXTURETYPE_UNSIGNED_BYTE),
                samplingModes: new Array(this._maxDrawBuffers).fill(Constants.TEXTURE_TRILINEAR_SAMPLINGMODE),
                generateMipMaps: false,
                targetTypes,
                formats: new Array(this._maxDrawBuffers).fill(Constants.TEXTUREFORMAT_R),
                faceIndex: new Array(this._maxDrawBuffers).fill(0),
                layerIndex: layerIndices,
                layerCounts: new Array(this._maxDrawBuffers).fill(this._voxelResolution),
                generateDepthBuffer: false,
                generateStencilBuffer: false,
            }, textureNames);
            mrt.clearColor = new Color4(0, 0, 0, 1);
            mrt.noPrePassRenderer = true;
            for (let i = 0; i < this._maxDrawBuffers; i++) {
                mrt.setInternalTexture(voxelRT.getInternalTexture(), i);
            }
            mrtArray.push(mrt);
        }
        return mrtArray;
    }
    _disposeVoxelTextures() {
        this._stopVoxelization();
        for (let i = 0; i < this._voxelMrtsZaxis.length; i++) {
            if (this._triPlanarVoxelization) {
                this._voxelMrtsXaxis[i].dispose(true);
                this._voxelMrtsYaxis[i].dispose(true);
            }
            this._voxelMrtsZaxis[i].dispose(true);
        }
        if (this._triPlanarVoxelization) {
            this._voxelGridXaxis?.dispose();
            this._voxelGridYaxis?.dispose();
            this._combinedVoxelGridPT?.dispose();
        }
        this._voxelGridZaxis?.dispose();
        for (const mip of this._mipArray) {
            mip.dispose();
        }
        this._voxelMaterial?.dispose();
        this._mipArray = [];
        this._voxelMrtsXaxis = [];
        this._voxelMrtsYaxis = [];
        this._voxelMrtsZaxis = [];
    }
    _createVoxelMaterials() {
        const isWebGPU = this._engine.isWebGPU;
        this._voxelMaterial = new ShaderMaterial("voxelization", this._scene, "iblVoxelGrid", {
            uniforms: ["world", "viewMatrix", "invTransWorld", "invWorldScale", "nearPlane", "farPlane", "stepSize"],
            defines: ["MAX_DRAW_BUFFERS " + this._maxDrawBuffers],
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./iblVoxelGrid.fragment-D4DhzdOP.esm.js'), import('./iblVoxelGrid.vertex-DNTUIWV3.esm.js')]);
                }
                else {
                    await Promise.all([import('./iblVoxelGrid.fragment-CDasKFAt.esm.js'), import('./iblVoxelGrid.vertex-B2pLv1Qv.esm.js')]);
                }
            },
        });
        this._voxelMaterial.cullBackFaces = false;
        this._voxelMaterial.backFaceCulling = false;
        this._voxelMaterial.depthFunction = Constants.ALWAYS;
    }
    /**
     * Checks if the voxel renderer is ready to voxelize scene
     * @returns true if the voxel renderer is ready to voxelize scene
     */
    isReady() {
        let allReady = this.getVoxelGrid().isReady();
        for (let i = 0; i < this._mipArray.length; i++) {
            const mipReady = this._mipArray[i].isReady();
            allReady &&= mipReady;
        }
        if (!allReady || this._voxelizationInProgress) {
            return false;
        }
        return true;
    }
    /**
     * If the MRT's are already in the list of render targets, this will
     * remove them so that they don't get rendered again.
     */
    _stopVoxelization() {
        // If the MRT's are already in the list of render targets, remove them.
        this._removeVoxelRTs(this._voxelMrtsXaxis);
        this._removeVoxelRTs(this._voxelMrtsYaxis);
        this._removeVoxelRTs(this._voxelMrtsZaxis);
        this._removeVoxelRTs([this._voxelGridRT]);
    }
    _removeVoxelRTs(rts) {
        // const currentRTs = this._scene.customRenderTargets;
        const rtIdx = this._renderTargets.findIndex((rt) => {
            if (rt === rts[0]) {
                return true;
            }
            return false;
        });
        if (rtIdx >= 0) {
            this._renderTargets.splice(rtIdx, rts.length);
        }
        else {
            const rtIdx = this._scene.customRenderTargets.findIndex((rt) => {
                if (rt === rts[0]) {
                    return true;
                }
                return false;
            });
            if (rtIdx >= 0) {
                this._scene.customRenderTargets.splice(rtIdx, rts.length);
            }
        }
    }
    /**
     * Renders voxel grid of scene for IBL shadows
     * @param includedMeshes
     * @param registerAfterRenderObservable Whether to register scene onAfterRender callback (legacy path).
     */
    updateVoxelGrid(includedMeshes, registerAfterRenderObservable = true) {
        if (this._voxelizationInProgress) {
            return;
        }
        this._stopVoxelization();
        this._voxelizationInProgress = true;
        if (this._engine.isWebGPU) {
            this._voxelGridRT.renderList = includedMeshes;
            this._addRTsForRender([this._voxelGridRT], includedMeshes, 0);
        }
        else if (this._triPlanarVoxelization) {
            this._addRTsForRender(this._voxelMrtsXaxis, includedMeshes, 0);
            this._addRTsForRender(this._voxelMrtsYaxis, includedMeshes, 1);
            this._addRTsForRender(this._voxelMrtsZaxis, includedMeshes, 2);
        }
        else {
            this._addRTsForRender(this._voxelMrtsZaxis, includedMeshes, 2);
        }
        if (registerAfterRenderObservable) {
            this._renderVoxelGridBound = this._renderVoxelGrid.bind(this);
            this._scene.onAfterRenderObservable.add(this._renderVoxelGridBound);
        }
    }
    /**
     * Advances voxelization work when running in custom render loops (for example FrameGraph tasks)
     * where scene onAfterRender timing may differ from classic pipeline flow.
     */
    processVoxelization() {
        this._renderVoxelGrid();
    }
    _renderVoxelGrid() {
        if (this._voxelizationInProgress) {
            // Wait for shadow-casting GaussianSplatting meshes' depth sort to settle before
            // rasterizing. Voxelization is order-independent — the order splats are drawn never
            // affects the grid — but the GS voxel draw still *indexes* each splat through the
            // `splatIndex` thin-instance buffer, and that buffer is (re)sized, filled and rebound by
            // the sort worker's callback (the same callback that flips `_isDepthSortSettled`). After
            // addPart() merges a splat, `forcedInstanceCount` jumps to the new total synchronously
            // but the index buffer is only finalized when the sort lands; rasterizing in between
            // indexes a stale/mismatched buffer and writes an empty grid (no IBL shadow until a
            // later refresh). Frame-capped so a continuously re-sorting (orbiting) splat still
            // voxelizes eventually rather than blocking forever.
            let gsSortPending = false;
            for (let i = 0; i < this._renderTargets.length && !gsSortPending; i++) {
                const renderList = this._renderTargets[i].renderList;
                if (!renderList) {
                    continue;
                }
                for (const mesh of renderList) {
                    if (IsGaussianSplattingClassName(mesh.getClassName()) && !mesh._isDepthSortSettled) {
                        gsSortPending = true;
                        break;
                    }
                }
            }
            if (gsSortPending && this._sortSettleWaitFrames < _MaxSortSettleWaitFrames) {
                this._sortSettleWaitFrames++;
                return;
            }
            this._sortSettleWaitFrames = 0;
            let allReady = this.getVoxelGrid().isReady();
            for (let i = 0; i < this._mipArray.length; i++) {
                const mipReady = this._mipArray[i].isReady();
                allReady &&= mipReady;
            }
            for (let i = 0; i < this._renderTargets.length; i++) {
                const rttReady = this._renderTargets[i].isReadyForRendering();
                allReady &&= rttReady;
            }
            for (const gsVoxelMat of Array.from(this._gsVoxelMaterialCache.values())) {
                allReady &&= gsVoxelMat.isReady();
            }
            if (!allReady) {
                return;
            }
            const copyMipEffect = this._copyMipEffectWrapper.effect;
            if (!copyMipEffect.isReady()) {
                return;
            }
            if (this._engine.isWebGPU) {
                // Clear the voxel grid storage texture.
                // Need to clear each layer individually.
                // Would a compute shader be faster here to clear all layers in one go?
                if (this._voxelGrid && this._voxelGrid.renderTarget) {
                    for (let layer = 0; layer < this._voxelResolution; layer++) {
                        this._engine.bindFramebuffer(this._voxelGrid.renderTarget, 0, undefined, undefined, true, 0, layer);
                        this._engine.clear(this._voxelClearColor, true, false, false);
                        this._engine.unBindFramebuffer(this._voxelGrid.renderTarget, true);
                    }
                }
            }
            for (const rt of this._renderTargets) {
                rt.render();
            }
            this._stopVoxelization();
            if (this._triPlanarVoxelization && !this._engine.isWebGPU) {
                this._combinedVoxelGridPT.render();
            }
            this._generateMipMaps();
            this._copyMipMaps();
            this._scene.onAfterRenderObservable.removeCallback(this._renderVoxelGridBound);
            this._voxelizationInProgress = false;
            this.onVoxelizationCompleteObservable.notifyObservers();
        }
    }
    /**
     * Splits rendering for every voxel RT: non–Gaussian splatting meshes use subMesh.render
     * (material override from setMaterialForRendering); GaussianSplattingMesh uses a custom draw path with its cached voxel ShaderMaterial.
     * @param rtt - the render target texture to install the custom render function on
     */
    _installVoxelMixedCustomRender(rtt) {
        const scene = this._scene;
        const engine = scene.getEngine();
        const renderGsSplat = (sm) => {
            const renderingMesh = sm.getRenderingMesh();
            const effectiveMesh = sm.getEffectiveMesh();
            const gsVoxelMaterial = this._gsVoxelMaterialCache.get(effectiveMesh.uniqueId);
            if (!gsVoxelMaterial || !gsVoxelMaterial.isReady()) {
                return;
            }
            const drawWrapper = gsVoxelMaterial._getDrawWrapper();
            if (!drawWrapper?.effect) {
                return;
            }
            const effect = drawWrapper.effect;
            const batch = renderingMesh._getInstancesRenderList(sm._id, !!sm.getReplacementMesh());
            if (batch.mustReturn) {
                return;
            }
            const hardwareInstancedRendering = engine.getCaps().instancedArrays && ((batch.visibleInstances[sm._id] !== null && batch.visibleInstances[sm._id] !== undefined) || renderingMesh.hasThinInstances);
            const fillMode = sm.getMaterial()?.fillMode ?? Constants.MATERIAL_TriangleFillMode;
            engine.enableEffect(drawWrapper);
            renderingMesh._bind(sm, effect, fillMode);
            gsVoxelMaterial._preBind(drawWrapper);
            gsVoxelMaterial.bind(effectiveMesh.getWorldMatrix(), effectiveMesh, effect);
            // If rotation/scale textures are missing, bind() logged the warning; skip the draw to avoid GPU errors.
            if (!effectiveMesh.rotationsATexture) {
                gsVoxelMaterial.unbind();
                return;
            }
            if (engine.isWebGPU) {
                // A GSplat is a 3D Gaussian ellipsoid. To approximate its volume in the voxel grid
                // we rasterize three planar cross-section quads — one per principal axis — each
                // spanning the ellipsoid cross-section perpendicular to that axis. A quad rendered
                // edge-on produces zero fragments, so we draw once per world axis: each draw lets
                // computeVoxelSplatWorldPos pick the quad whose normal best aligns with that view,
                // guaranteeing every splat is captured face-on from at least one direction.
                const viewMatrices = _IblShadowsVoxelRenderer._VOXEL_VIEW_MATRICES;
                for (let axisIdx = 0; axisIdx < 3; axisIdx++) {
                    effect.setMatrix("viewMatrix", viewMatrices[axisIdx]);
                    renderingMesh._processRendering(effectiveMesh, sm, effect, fillMode, batch, hardwareInstancedRendering, (_isInstance, world) => {
                        effect.setMatrix("world", world);
                    });
                }
            }
            else {
                renderingMesh._processRendering(effectiveMesh, sm, effect, fillMode, batch, hardwareInstancedRendering, (_isInstance, world) => effect.setMatrix("world", world));
            }
            gsVoxelMaterial.unbind();
        };
        const processBucket = (subMeshes, enableAlphaMode) => {
            for (let i = 0; i < subMeshes.length; i++) {
                const sm = subMeshes.data[i];
                const effective = sm.getEffectiveMesh();
                if (IsGaussianSplattingClassName(effective.getClassName())) {
                    renderGsSplat(sm);
                }
                else {
                    sm.render(enableAlphaMode);
                }
            }
        };
        rtt.customRenderFunction = (opaqueSubMeshes, alphaTestSubMeshes, transparentSubMeshes, depthOnlySubMeshes) => {
            if (depthOnlySubMeshes.length) {
                engine.setColorWrite(false);
                processBucket(depthOnlySubMeshes, false);
                engine.setColorWrite(true);
            }
            processBucket(opaqueSubMeshes, false);
            processBucket(alphaTestSubMeshes, false);
            processBucket(transparentSubMeshes, true);
        };
    }
    _addGsMeshToVoxelRT(mrt, mesh) {
        let gsVoxelMaterial = this._gsVoxelMaterialCache.get(mesh.uniqueId);
        if (!gsVoxelMaterial) {
            const gsMaterial = mesh.material;
            if (!gsMaterial) {
                return;
            }
            const shaderLanguage = this._engine.isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */;
            gsVoxelMaterial = gsMaterial.makeVoxelRenderingMaterial(this._scene, shaderLanguage, this._maxDrawBuffers, mesh.isCompound);
            this._gsVoxelMaterialCache.set(mesh.uniqueId, gsVoxelMaterial);
        }
        mrt.renderList?.push(mesh);
        mrt.setMaterialForRendering(mesh, gsVoxelMaterial);
    }
    _addRTsForRender(mrts, includedMeshes, axis) {
        const slabSize = 1.0 / this._computeNumberOfSlabs();
        const voxelMaterial = this._voxelMaterial;
        // We need to update the world scale uniform for every mesh being rendered to the voxel grid.
        for (let mrtIndex = 0; mrtIndex < mrts.length; mrtIndex++) {
            const mrt = mrts[mrtIndex];
            mrt._disableEngineStages = true;
            mrt.useCameraPostProcesses = false;
            mrt.renderParticles = false;
            mrt.renderSprites = false;
            mrt.enableOutlineRendering = false;
            const renderBucket = (bucket) => {
                for (let index = 0; index < bucket.length; index++) {
                    const subMesh = bucket.data[index];
                    if (subMesh.getMaterial() !== voxelMaterial) {
                        continue;
                    }
                    subMesh.render(false);
                }
            };
            mrt.customRenderFunction = (opaqueSubMeshes, alphaTestSubMeshes, transparentSubMeshes, depthOnlySubMeshes) => {
                renderBucket(depthOnlySubMeshes);
                renderBucket(opaqueSubMeshes);
                renderBucket(alphaTestSubMeshes);
                renderBucket(transparentSubMeshes);
            };
            mrt.renderList = [];
            const nearPlane = mrtIndex * slabSize;
            const farPlane = (mrtIndex + 1) * slabSize;
            const stepSize = slabSize / this._maxDrawBuffers;
            const viewMatrix = _IblShadowsVoxelRenderer._VOXEL_VIEW_MATRICES[axis];
            mrt.onBeforeRenderObservable.clear();
            mrt.onBeforeRenderObservable.add(() => {
                voxelMaterial.setMatrix("viewMatrix", viewMatrix);
                voxelMaterial.setMatrix("invWorldScale", this._invWorldScaleMatrix);
                voxelMaterial.setFloat("nearPlane", nearPlane);
                voxelMaterial.setFloat("farPlane", farPlane);
                voxelMaterial.setFloat("stepSize", stepSize);
                if (this._engine.isWebGPU) {
                    this._voxelMaterial.useVertexPulling = true;
                    this._voxelMaterial.setTexture("voxel_storage", this.getVoxelGrid());
                }
                // Push per-slab uniforms to each GS voxel material in this MRT's render list.
                for (const m of mrt.renderList ?? []) {
                    if (IsGaussianSplattingClassName(m.getClassName())) {
                        const gsVoxelMat = this._gsVoxelMaterialCache.get(m.uniqueId);
                        if (gsVoxelMat) {
                            gsVoxelMat.setMatrix("invWorldScale", this._invWorldScaleMatrix);
                            if (this._engine.isWebGPU) {
                                // WGSL GS voxel shader uses the same viewMatrix approach as WebGL; the per-axis viewMatrix is set per-draw in renderGsSplat.
                                gsVoxelMat.setTexture("voxel_storage", this.getVoxelGrid());
                            }
                            else {
                                gsVoxelMat.setMatrix("viewMatrix", viewMatrix);
                                gsVoxelMat.setFloat("nearPlane", nearPlane);
                                gsVoxelMat.setFloat("farPlane", farPlane);
                                gsVoxelMat.setFloat("stepSize", stepSize);
                            }
                        }
                    }
                }
            });
            // Set this material on every mesh in the scene (for this RT)
            if (includedMeshes.length === 0) {
                return;
            }
            for (const mesh of includedMeshes) {
                if (!mesh) {
                    continue;
                }
                if (IsGaussianSplattingClassName(mesh.getClassName())) {
                    this._addGsMeshToVoxelRT(mrt, mesh);
                }
                else if (mesh.subMeshes && mesh.subMeshes.length > 0) {
                    mrt.renderList?.push(mesh);
                    mrt.setMaterialForRendering(mesh, voxelMaterial);
                }
                const meshes = mesh.getChildMeshes();
                for (const childMesh of meshes) {
                    if (IsGaussianSplattingClassName(childMesh.getClassName())) {
                        this._addGsMeshToVoxelRT(mrt, childMesh);
                    }
                    else if (childMesh.subMeshes && childMesh.subMeshes.length > 0) {
                        mrt.renderList?.push(childMesh);
                        mrt.setMaterialForRendering(childMesh, voxelMaterial);
                    }
                }
            }
            this._installVoxelMixedCustomRender(mrt);
        }
        this._renderTargets = this._renderTargets.concat(mrts);
    }
    /**
     * Called by the pipeline to resize resources.
     */
    resize() { }
    /**
     * Disposes the voxel renderer and associated resources
     */
    dispose() {
        this._disposeVoxelTextures();
        for (const mat of Array.from(this._gsVoxelMaterialCache.values())) {
            mat.dispose();
        }
        this._gsVoxelMaterialCache.clear();
    }
}
// View matrices for the three voxelization axes.
_IblShadowsVoxelRenderer._VOXEL_VIEW_MATRICES = [
    Matrix.LookAtLH(Vector3.Zero(), new Vector3(1, 0, 0), Vector3.Up()),
    Matrix.LookAtLH(Vector3.Zero(), new Vector3(0, 1, 0), new Vector3(1, 0, 0)),
    Matrix.LookAtLH(Vector3.Zero(), new Vector3(0, 0, 1), Vector3.Up()),
];

/**
 * Build cdf maps for IBL importance sampling during IBL shadow computation.
 * This should not be instantiated directly, as it is part of a scene component
 * @internal
 */
class _IblShadowsVoxelTracingPass {
    /**
     * The opacity of the shadow cast from the voxel grid
     */
    get voxelShadowOpacity() {
        return this._voxelShadowOpacity;
    }
    /**
     * The opacity of the shadow cast from the voxel grid
     */
    set voxelShadowOpacity(value) {
        this._voxelShadowOpacity = value;
    }
    /**
     * The opacity of the screen-space shadow
     */
    get ssShadowOpacity() {
        return this._ssShadowOpacity;
    }
    /**
     * The opacity of the screen-space shadow
     */
    set ssShadowOpacity(value) {
        this._ssShadowOpacity = value;
    }
    /**
     * The number of samples used in the screen space shadow pass.
     */
    get sssSamples() {
        return this._sssSamples;
    }
    /**
     * The number of samples used in the screen space shadow pass.
     */
    set sssSamples(value) {
        this._sssSamples = value;
    }
    /**
     * The stride used in the screen space shadow pass. This controls the distance between samples.
     */
    get sssStride() {
        return this._sssStride;
    }
    /**
     * The stride used in the screen space shadow pass. This controls the distance between samples.
     */
    set sssStride(value) {
        this._sssStride = value;
    }
    /**
     * The maximum distance that the screen-space shadow will be able to occlude.
     */
    get sssMaxDist() {
        return this._sssMaxDist;
    }
    /**
     * The maximum distance that the screen-space shadow will be able to occlude.
     */
    set sssMaxDist(value) {
        this._sssMaxDist = value;
    }
    /**
     * The thickness of the screen-space shadow
     */
    get sssThickness() {
        return this._sssThickness;
    }
    /**
     * The thickness of the screen-space shadow
     */
    set sssThickness(value) {
        this._sssThickness = value;
    }
    /**
     * The bias to apply to the voxel sampling in the direction of the surface normal of the geometry.
     */
    get voxelNormalBias() {
        return this._voxelNormalBias;
    }
    set voxelNormalBias(value) {
        this._voxelNormalBias = value;
    }
    /**
     * The bias to apply to the voxel sampling in the direction of the light.
     */
    get voxelDirectionBias() {
        return this._voxelDirectionBias;
    }
    set voxelDirectionBias(value) {
        this._voxelDirectionBias = value;
    }
    /**
     * Is the effect enabled
     */
    get enabled() {
        return this._enabled;
    }
    set enabled(value) {
        this._enabled = value;
        // _render() already gates on `enabled`, but also disable the underlying ProceduralTexture
        // directly so it can't be rendered through any other path (e.g. Babylon's generic
        // per-frame ProceduralTexture refresh loop) while the pass is toggled off.
        if (this._outputTexture) {
            this._outputTexture.isEnabled = value;
        }
    }
    /**
     * The number of directions to sample for the voxel tracing.
     */
    get sampleDirections() {
        return this._sampleDirections;
    }
    /**
     * The number of directions to sample for the voxel tracing.
     */
    set sampleDirections(value) {
        this._sampleDirections = value;
    }
    /**
     * The current rotation of the environment map, in radians.
     */
    get envRotation() {
        return this._envRotation;
    }
    /**
     * The current rotation of the environment map, in radians.
     */
    set envRotation(value) {
        this._envRotation = value;
    }
    /**
     * Returns the output texture of the pass.
     * @returns The output texture.
     */
    getOutputTexture() {
        return this._outputTexture;
    }
    /**
     * Gets the debug pass post process. This will create the resources for the pass
     * if they don't already exist.
     * @returns The post process
     */
    getDebugPassPP() {
        if (!this._debugPassPP) {
            this._createDebugPass();
        }
        return this._debugPassPP;
    }
    /**
     * The name of the debug pass
     */
    get debugPassName() {
        return this._debugPassName;
    }
    /**
     * Set the matrix to use for scaling the world space to voxel space
     * @param matrix The matrix to use for scaling the world space to voxel space
     */
    setWorldScaleMatrix(matrix) {
        this._invWorldScaleMatrix = matrix;
    }
    /**
     * Render the shadows in color rather than black and white.
     * This is slightly more expensive than black and white shadows but can be much
     * more accurate when the strongest lights in the IBL are non-white.
     */
    set coloredShadows(value) {
        this._coloredShadows = value;
    }
    get coloredShadows() {
        return this._coloredShadows;
    }
    /**
     * Sets params that control the position and scaling of the debug display on the screen.
     * @param x Screen X offset of the debug display (0-1)
     * @param y Screen Y offset of the debug display (0-1)
     * @param widthScale X scale of the debug display (0-1)
     * @param heightScale Y scale of the debug display (0-1)
     */
    setDebugDisplayParams(x, y, widthScale, heightScale) {
        this._debugSizeParams.set(x, y, widthScale, heightScale);
    }
    /**
     * Creates the debug post process effect for this pass
     */
    _createDebugPass() {
        const isWebGPU = this._engine.isWebGPU;
        if (!this._debugPassPP) {
            const debugOptions = {
                width: this._engine.getRenderWidth(),
                height: this._engine.getRenderHeight(),
                uniforms: ["sizeParams"],
                samplers: ["debugSampler"],
                engine: this._engine,
                reusable: true,
                shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
                extraInitializations: (useWebGPU, list) => {
                    if (useWebGPU) {
                        list.push(import('./iblShadowDebug.fragment-f5MbNzgY.esm.js'));
                    }
                    else {
                        list.push(import('./iblShadowDebug.fragment-CAvtv_Yy.esm.js'));
                    }
                },
            };
            this._debugPassPP = new PostProcess(this.debugPassName, "iblShadowDebug", debugOptions);
            this._debugPassPP.autoClear = false;
            this._debugPassPP.onApplyObservable.add((effect) => {
                // update the caustic texture with what we just rendered.
                effect.setTexture("debugSampler", this._outputTexture);
                effect.setVector4("sizeParams", this._debugSizeParams);
            });
        }
    }
    /**
     * Instantiates the shadow voxel-tracing pass
     * @param scene Scene to attach to
     * @param iblShadowsRenderPipeline The IBL shadows render pipeline
     * @returns The shadow voxel-tracing pass
     */
    constructor(scene, iblShadowsRenderPipeline) {
        this._voxelShadowOpacity = 1.0;
        this._sssSamples = 16;
        this._sssStride = 8;
        this._sssMaxDist = 0.05;
        this._sssThickness = 0.5;
        this._ssShadowOpacity = 1.0;
        this._cameraInvView = Matrix.Identity();
        this._cameraInvProj = Matrix.Identity();
        this._invWorldScaleMatrix = Matrix.Identity();
        this._frameId = 0;
        this._sampleDirections = 4;
        this._shadowParameters = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._sssParameters = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._opacityParameters = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._voxelBiasParameters = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._voxelNormalBias = 1.4;
        this._voxelDirectionBias = 1.75;
        this._enabled = true;
        /** Enable the debug view for this pass */
        this.debugEnabled = false;
        this._debugPassName = "Voxel Tracing Debug Pass";
        /** The default rotation of the environment map will align the shadows with the default lighting orientation */
        this._envRotation = 0.0;
        this._coloredShadows = false;
        this._debugVoxelMarchEnabled = false;
        this._debugSizeParams = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._renderWhenGBufferReady = null;
        this._scene = scene;
        this._engine = scene.getEngine();
        this._renderPipeline = iblShadowsRenderPipeline;
        this._createTextures();
    }
    _createTextures() {
        const defines = this._createDefines();
        const isWebGPU = this._engine.isWebGPU;
        const textureOptions = {
            type: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            format: Constants.TEXTUREFORMAT_RGBA,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            generateDepthBuffer: false,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./iblShadowVoxelTracing.fragment-DUXh6c9U.esm.js')]);
                }
                else {
                    await Promise.all([import('./iblShadowVoxelTracing.fragment-_94Wdqp2.esm.js')]);
                }
            },
        };
        this._outputTexture = new ProceduralTexture("voxelTracingPass", {
            width: this._engine.getRenderWidth(),
            height: this._engine.getRenderHeight(),
        }, "iblShadowVoxelTracing", this._scene, textureOptions);
        this._outputTexture.refreshRate = -1;
        this._outputTexture.autoClear = false;
        this._outputTexture.defines = defines;
        // Need to set all the textures first so that the effect gets created with the proper uniforms.
        this._setBindings(this._scene.activeCamera);
        this._renderWhenGBufferReady = this._render.bind(this);
        // Don't start rendering until the first vozelization is done.
        this._renderPipeline.onVoxelizationCompleteObservable.addOnce(() => {
            if (this._scene.geometryBufferRenderer) {
                this._scene.geometryBufferRenderer.getGBuffer().onAfterRenderObservable.add(this._renderWhenGBufferReady);
            }
        });
    }
    _createDefines() {
        let defines = "";
        if (this._scene.useRightHandedSystem) {
            defines += "#define RIGHT_HANDED\n";
        }
        if (this._debugVoxelMarchEnabled) {
            defines += "#define VOXEL_MARCH_DIAGNOSTIC_INFO_OPTION 1u\n";
        }
        if (this._coloredShadows) {
            defines += "#define COLOR_SHADOWS 1u\n";
        }
        if (this._scene.geometryBufferRenderer?.normalsAreUnsigned) {
            defines += "#define WORLD_NORMAL_UNSIGNED\n";
        }
        return defines;
    }
    _setBindings(camera) {
        this._outputTexture.defines = this._createDefines();
        this._outputTexture.setMatrix("viewMtx", camera.getViewMatrix());
        this._outputTexture.setMatrix("projMtx", camera.getProjectionMatrix());
        camera.getProjectionMatrix().invertToRef(this._cameraInvProj);
        camera.getViewMatrix().invertToRef(this._cameraInvView);
        this._outputTexture.setMatrix("invProjMtx", this._cameraInvProj);
        this._outputTexture.setMatrix("invViewMtx", this._cameraInvView);
        this._outputTexture.setMatrix("wsNormalizationMtx", this._invWorldScaleMatrix);
        this._frameId++;
        let rotation = 0.0;
        if (this._scene.environmentTexture) {
            rotation = this._scene.environmentTexture.rotationY ?? 0;
        }
        rotation = this._scene.useRightHandedSystem ? -(rotation + 0.5 * Math.PI) : rotation - 0.5 * Math.PI;
        rotation = rotation % (2.0 * Math.PI);
        this._shadowParameters.set(this._sampleDirections, this._frameId, 1.0, rotation);
        this._outputTexture.setVector4("shadowParameters", this._shadowParameters);
        const voxelGrid = this._renderPipeline._getVoxelGridTexture();
        const highestMip = Math.floor(Math.log2(voxelGrid.getSize().width));
        this._voxelBiasParameters.set(this._voxelNormalBias, this._voxelDirectionBias, highestMip, 0.0);
        this._outputTexture.setVector4("voxelBiasParameters", this._voxelBiasParameters);
        // SSS Options.
        this._sssParameters.set(this._sssSamples, this._sssStride, this._sssMaxDist, this._sssThickness);
        this._outputTexture.setVector4("sssParameters", this._sssParameters);
        this._opacityParameters.set(this._voxelShadowOpacity, this._ssShadowOpacity, 0.0, 0.0);
        this._outputTexture.setVector4("shadowOpacity", this._opacityParameters);
        this._outputTexture.setTexture("voxelGridSampler", voxelGrid);
        this._outputTexture.setTexture("blueNoiseSampler", this._renderPipeline._getNoiseTexture());
        const cdfGenerator = this._scene.iblCdfGenerator;
        if (!cdfGenerator) {
            Logger.Warn("IBLShadowsVoxelTracingPass: Can't bind for render because iblCdfGenerator is not enabled.");
            return false;
        }
        this._outputTexture.setTexture("icdfSampler", cdfGenerator.getIcdfTexture());
        if (this._coloredShadows && this._scene.environmentTexture) {
            this._outputTexture.setTexture("iblSampler", this._scene.environmentTexture);
        }
        const geometryBufferRenderer = this._scene.geometryBufferRenderer;
        if (!geometryBufferRenderer) {
            Logger.Warn("IBLShadowsVoxelTracingPass: Can't bind for render because GeometryBufferRenderer is not enabled.");
            return false;
        }
        const depthIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.SCREENSPACE_DEPTH_TEXTURE_TYPE);
        this._outputTexture.setTexture("depthSampler", geometryBufferRenderer.getGBuffer().textures[depthIndex]);
        const wnormalIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.NORMAL_TEXTURE_TYPE);
        this._outputTexture.setTexture("worldNormalSampler", geometryBufferRenderer.getGBuffer().textures[wnormalIndex]);
        return true;
    }
    _render() {
        if (this.enabled && this._outputTexture.isReady() && this._outputTexture.getEffect()?.isReady()) {
            if (this._setBindings(this._scene.activeCamera)) {
                this._outputTexture.render();
            }
        }
    }
    /**
     * Called by render pipeline when canvas resized.
     * @param scaleFactor The factor by which to scale the canvas size.
     */
    resize(scaleFactor = 1.0) {
        const newSize = {
            width: Math.max(1.0, Math.floor(this._engine.getRenderWidth() * scaleFactor)),
            height: Math.max(1.0, Math.floor(this._engine.getRenderHeight() * scaleFactor)),
        };
        // Don't resize if the size is the same as the current size.
        if (this._outputTexture.getSize().width === newSize.width && this._outputTexture.getSize().height === newSize.height) {
            return;
        }
        this._outputTexture.resize(newSize, false);
    }
    /**
     * Checks if the pass is ready
     * @returns true if the pass is ready
     */
    isReady() {
        return (this._outputTexture.isReady() &&
            !(this._debugPassPP && !this._debugPassPP.isReady()) &&
            this._scene.iblCdfGenerator &&
            this._scene.iblCdfGenerator.getIcdfTexture().isReady() &&
            this._renderPipeline._getVoxelGridTexture().isReady());
    }
    /**
     * Disposes the associated resources
     */
    dispose() {
        if (this._scene.geometryBufferRenderer && this._renderWhenGBufferReady) {
            const gBuffer = this._scene.geometryBufferRenderer.getGBuffer();
            gBuffer.onAfterRenderObservable.removeCallback(this._renderWhenGBufferReady);
        }
        this._outputTexture.dispose();
        if (this._debugPassPP) {
            this._debugPassPP.dispose();
        }
    }
}

/**
 * This should not be instanciated directly, as it is part of a scene component
 * @internal
 */
class _IblShadowsSpatialBlurPass {
    /**
     * Is the effect enabled
     */
    get enabled() {
        return this._enabled;
    }
    set enabled(value) {
        this._enabled = value;
        // _render() already gates on `enabled`, but also disable the underlying ProceduralTexture
        // directly so it can't be rendered through any other path (e.g. Babylon's generic
        // per-frame ProceduralTexture refresh loop) while the pass is toggled off.
        if (this._outputTexture) {
            this._outputTexture.isEnabled = value;
        }
    }
    /**
     * Returns the output texture of the pass.
     * @returns The output texture.
     */
    getOutputTexture() {
        return this._outputTexture;
    }
    /**
     * Gets the debug pass post process
     * @returns The post process
     */
    getDebugPassPP() {
        if (!this._debugPassPP) {
            this._createDebugPass();
        }
        return this._debugPassPP;
    }
    /**
     * Sets the name of the debug pass
     */
    get debugPassName() {
        return this._debugPassName;
    }
    /**
     * The scale of the voxel grid in world space. This is used to scale the blur radius in world space.
     * @param scale The scale of the voxel grid in world space.
     */
    setWorldScale(scale) {
        this._worldScale = scale;
    }
    /**
     * Sets params that control the position and scaling of the debug display on the screen.
     * @param x Screen X offset of the debug display (0-1)
     * @param y Screen Y offset of the debug display (0-1)
     * @param widthScale X scale of the debug display (0-1)
     * @param heightScale Y scale of the debug display (0-1)
     */
    setDebugDisplayParams(x, y, widthScale, heightScale) {
        this._debugSizeParams.set(x, y, widthScale, heightScale);
    }
    /**
     * Creates the debug post process effect for this pass
     */
    _createDebugPass() {
        if (!this._debugPassPP) {
            const isWebGPU = this._engine.isWebGPU;
            const debugOptions = {
                width: this._engine.getRenderWidth(),
                height: this._engine.getRenderHeight(),
                textureFormat: Constants.TEXTUREFORMAT_RGBA,
                textureType: Constants.TEXTURETYPE_UNSIGNED_BYTE,
                samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
                uniforms: ["sizeParams"],
                samplers: ["debugSampler"],
                engine: this._engine,
                reusable: false,
                shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
                extraInitializations: (useWebGPU, list) => {
                    if (useWebGPU) {
                        list.push(import('./iblShadowDebug.fragment-f5MbNzgY.esm.js'));
                    }
                    else {
                        list.push(import('./iblShadowDebug.fragment-CAvtv_Yy.esm.js'));
                    }
                },
            };
            this._debugPassPP = new PostProcess(this.debugPassName, "iblShadowDebug", debugOptions);
            this._debugPassPP.autoClear = false;
            this._debugPassPP.onApplyObservable.add((effect) => {
                // update the caustic texture with what we just rendered.
                effect.setTexture("debugSampler", this._outputTexture);
                effect.setVector4("sizeParams", this._debugSizeParams);
            });
        }
    }
    /**
     * Instanciates the importance sampling renderer
     * @param scene Scene to attach to
     * @param iblShadowsRenderPipeline The IBL shadows render pipeline
     * @returns The importance sampling renderer
     */
    constructor(scene, iblShadowsRenderPipeline) {
        this._worldScale = 1.0;
        this._blurParameters = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._enabled = true;
        this._debugPassName = "Spatial Blur Debug Pass";
        /** Enable the debug view for this pass */
        this.debugEnabled = false;
        this._debugSizeParams = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._renderWhenGBufferReady = null;
        this._scene = scene;
        this._engine = scene.getEngine();
        this._renderPipeline = iblShadowsRenderPipeline;
        this._createTextures();
    }
    _createTextures() {
        const isWebGPU = this._engine.isWebGPU;
        const textureOptions = {
            type: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            format: Constants.TEXTUREFORMAT_RGBA,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            generateDepthBuffer: false,
            generateMipMaps: false,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./iblShadowSpatialBlur.fragment-B2VRNC1_.esm.js')]);
                }
                else {
                    await Promise.all([import('./iblShadowSpatialBlur.fragment-Bm2uWJvs.esm.js')]);
                }
            },
        };
        this._outputTexture = new ProceduralTexture("spatialBlurPass", {
            width: this._engine.getRenderWidth(),
            height: this._engine.getRenderHeight(),
        }, "iblShadowSpatialBlur", this._scene, textureOptions, false, false, Constants.TEXTURETYPE_UNSIGNED_BYTE);
        this._outputTexture.refreshRate = -1;
        this._outputTexture.autoClear = false;
        // Need to set all the textures first so that the effect gets created with the proper uniforms.
        this._setBindings();
        this._renderWhenGBufferReady = this._render.bind(this);
        // Don't start rendering until the first vozelization is done.
        this._renderPipeline.onVoxelizationCompleteObservable.addOnce(() => {
            if (this._scene.geometryBufferRenderer) {
                this._scene.geometryBufferRenderer.getGBuffer().onAfterRenderObservable.add(this._renderWhenGBufferReady);
            }
        });
    }
    _setBindings() {
        this._outputTexture.setTexture("voxelTracingSampler", this._renderPipeline._getVoxelTracingTexture());
        const iterationCount = 1;
        this._blurParameters.set(iterationCount, this._worldScale, 0.0, 0.0);
        this._outputTexture.setVector4("blurParameters", this._blurParameters);
        const geometryBufferRenderer = this._scene.geometryBufferRenderer;
        if (!geometryBufferRenderer) {
            return false;
        }
        const depthIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.SCREENSPACE_DEPTH_TEXTURE_TYPE);
        this._outputTexture.setTexture("depthSampler", geometryBufferRenderer.getGBuffer().textures[depthIndex]);
        const wnormalIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.NORMAL_TEXTURE_TYPE);
        this._outputTexture.setTexture("worldNormalSampler", geometryBufferRenderer.getGBuffer().textures[wnormalIndex]);
        return true;
    }
    _render() {
        if (this.enabled && this._outputTexture.isReady() && this._outputTexture.getEffect()?.isReady()) {
            if (this._setBindings()) {
                this._outputTexture.render();
            }
        }
    }
    /**
     * Called by render pipeline when canvas resized.
     * @param scaleFactor The factor by which to scale the canvas size.
     */
    resize(scaleFactor = 1.0) {
        const newSize = {
            width: Math.max(1.0, Math.floor(this._engine.getRenderWidth() * scaleFactor)),
            height: Math.max(1.0, Math.floor(this._engine.getRenderHeight() * scaleFactor)),
        };
        // Don't resize if the size is the same as the current size.
        if (this._outputTexture.getSize().width === newSize.width && this._outputTexture.getSize().height === newSize.height) {
            return;
        }
        this._outputTexture.resize(newSize, false);
    }
    /**
     * Checks if the pass is ready
     * @returns true if the pass is ready
     */
    isReady() {
        return this._outputTexture.isReady() && !(this._debugPassPP && !this._debugPassPP.isReady());
    }
    /**
     * Disposes the associated resources
     */
    dispose() {
        if (this._scene.geometryBufferRenderer && this._renderWhenGBufferReady) {
            const gBuffer = this._scene.geometryBufferRenderer.getGBuffer();
            gBuffer.onAfterRenderObservable.removeCallback(this._renderWhenGBufferReady);
        }
        this._outputTexture.dispose();
        if (this._debugPassPP) {
            this._debugPassPP.dispose();
        }
    }
}

/**
 * This should not be instantiated directly, as it is part of a scene component
 * @internal
 */
class _IblShadowsAccumulationPass {
    /**
     * Is the effect enabled
     */
    get enabled() {
        return this._enabled;
    }
    set enabled(value) {
        this._enabled = value;
        // _render() already gates on `enabled` for _outputTexture, but _oldAccumulationCopy and
        // _oldPositionCopy have refreshRate = 1 and are rendered by Babylon's generic per-frame
        // ProceduralTexture refresh loop, entirely independent of `enabled` / `_render()`. Without
        // disabling them directly here, they keep rendering (and paying a real per-frame texture-view
        // creation cost) every frame forever, even when the whole pass — and the rest of the IBL
        // shadows pipeline — has been toggled off (e.g. no shadow casters or receivers in the scene).
        if (this._outputTexture) {
            this._outputTexture.isEnabled = value;
        }
        if (this._oldAccumulationCopy) {
            this._oldAccumulationCopy.isEnabled = value;
        }
        if (this._oldPositionCopy) {
            this._oldPositionCopy.isEnabled = value;
        }
    }
    /**
     * Returns the output texture of the pass.
     * @returns The output texture.
     */
    getOutputTexture() {
        return this._outputTexture;
    }
    /**
     * Gets the debug pass post process
     * @returns The post process
     */
    getDebugPassPP() {
        if (!this._debugPassPP) {
            this._createDebugPass();
        }
        return this._debugPassPP;
    }
    /**
     * Gets the name of the debug pass
     * @returns The name of the debug pass
     */
    get debugPassName() {
        return this._debugPassName;
    }
    /**
     * A value that controls how much of the previous frame's accumulation to keep.
     * The higher the value, the faster the shadows accumulate but the more potential ghosting you'll see.
     */
    get remanence() {
        return this._remanence;
    }
    /**
     * A value that controls how much of the previous frame's accumulation to keep.
     * The higher the value, the faster the shadows accumulate but the more potential ghosting you'll see.
     */
    set remanence(value) {
        this._remanence = value;
    }
    /**
     * Reset the accumulation.
     */
    get reset() {
        return this._reset;
    }
    /**
     * Reset the accumulation.
     */
    set reset(value) {
        this._reset = value;
    }
    /**
     * Tell the pass that the camera is moving. This will cause the accumulation
     * rate to change.
     */
    set isMoving(value) {
        this._isMoving = value;
    }
    /**
     * Sets params that control the position and scaling of the debug display on the screen.
     * @param x Screen X offset of the debug display (0-1)
     * @param y Screen Y offset of the debug display (0-1)
     * @param widthScale X scale of the debug display (0-1)
     * @param heightScale Y scale of the debug display (0-1)
     */
    setDebugDisplayParams(x, y, widthScale, heightScale) {
        this._debugSizeParams.set(x, y, widthScale, heightScale);
    }
    /**
     * Creates the debug post process effect for this pass
     */
    _createDebugPass() {
        if (!this._debugPassPP) {
            const isWebGPU = this._engine.isWebGPU;
            const debugOptions = {
                width: this._engine.getRenderWidth(),
                height: this._engine.getRenderHeight(),
                textureFormat: Constants.TEXTUREFORMAT_RGBA,
                textureType: Constants.TEXTURETYPE_UNSIGNED_BYTE,
                samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
                uniforms: ["sizeParams"],
                samplers: ["debugSampler"],
                engine: this._engine,
                reusable: false,
                shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
                extraInitializations: (useWebGPU, list) => {
                    if (useWebGPU) {
                        list.push(import('./iblShadowDebug.fragment-f5MbNzgY.esm.js'));
                    }
                    else {
                        list.push(import('./iblShadowDebug.fragment-CAvtv_Yy.esm.js'));
                    }
                },
            };
            this._debugPassPP = new PostProcess(this.debugPassName, "iblShadowDebug", debugOptions);
            this._debugPassPP.autoClear = false;
            this._debugPassPP.onApplyObservable.add((effect) => {
                // update the caustic texture with what we just rendered.
                effect.setTexture("debugSampler", this._outputTexture);
                effect.setVector4("sizeParams", this._debugSizeParams);
            });
        }
    }
    /**
     * Instantiates the accumulation pass
     * @param scene Scene to attach to
     * @param iblShadowsRenderPipeline The IBL shadows render pipeline
     * @returns The accumulation pass
     */
    constructor(scene, iblShadowsRenderPipeline) {
        this._accumulationParams = new Vector4(0.0, 0.0, 0.0, 0.0);
        /** Enable the debug view for this pass */
        this.debugEnabled = false;
        this._enabled = true;
        /**
         * Observable that triggers when the accumulation texture is ready
         */
        this.onReadyObservable = new Observable();
        this._debugPassName = "Shadow Accumulation Debug Pass";
        this._remanence = 0.9;
        this._reset = true;
        this._isMoving = false;
        this._debugSizeParams = new Vector4(0.0, 0.0, 0.0, 0.0);
        this._renderWhenGBufferReady = null;
        this._scene = scene;
        this._engine = scene.getEngine();
        this._renderPipeline = iblShadowsRenderPipeline;
        this._createTextures();
    }
    _createTextures() {
        const isWebGPU = this._engine.isWebGPU;
        const outputTextureOptions = {
            type: Constants.TEXTURETYPE_HALF_FLOAT,
            format: Constants.TEXTUREFORMAT_RGBA,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            generateDepthBuffer: false,
            generateMipMaps: false,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./iblShadowAccumulation.fragment-BEDpECEq.esm.js')]);
                }
                else {
                    await Promise.all([import('./iblShadowAccumulation.fragment-CTXh-kaj.esm.js')]);
                }
            },
        };
        this._outputTexture = new ProceduralTexture("shadowAccumulationPass", {
            width: this._engine.getRenderWidth(),
            height: this._engine.getRenderHeight(),
        }, "iblShadowAccumulation", this._scene, outputTextureOptions);
        this._outputTexture.refreshRate = -1;
        this._outputTexture.autoClear = false;
        this._outputTexture.onGeneratedObservable.addOnce(() => {
            this.onReadyObservable.notifyObservers();
        });
        // Need to set all the textures first so that the effect gets created with the proper uniforms.
        this._setOutputTextureBindings();
        this._renderWhenGBufferReady = this._render.bind(this);
        // Don't start rendering until the first vozelization is done.
        this._renderPipeline.onVoxelizationCompleteObservable.addOnce(() => {
            if (this._scene.geometryBufferRenderer) {
                this._scene.geometryBufferRenderer.getGBuffer().onAfterRenderObservable.add(this._renderWhenGBufferReady);
            }
        });
        // Create the accumulation texture for the previous frame.
        // We'll copy the output of the accumulation pass to this texture at the start of every frame.
        const accumulationOptions = {
            type: Constants.TEXTURETYPE_HALF_FLOAT,
            format: Constants.TEXTUREFORMAT_RGBA,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            generateDepthBuffer: false,
            generateMipMaps: false,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./pass.fragment-DS6uQAkG.esm.js')]);
                }
                else {
                    await Promise.all([import('./pass.fragment-Bo-QegcY.esm.js')]);
                }
            },
        };
        this._oldAccumulationCopy = new ProceduralTexture("oldAccumulationRT", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, "pass", this._scene, accumulationOptions, false);
        this._oldAccumulationCopy.autoClear = false;
        this._oldAccumulationCopy.refreshRate = 1;
        this._oldAccumulationCopy.onBeforeGenerationObservable.add(this._setAccumulationCopyBindings.bind(this));
        this._setAccumulationCopyBindings();
        // Create the local position texture for the previous frame.
        // We'll copy the previous local position texture to this texture at the start of every frame.
        const localPositionOptions = {
            type: Constants.TEXTURETYPE_HALF_FLOAT,
            format: Constants.TEXTUREFORMAT_RGBA,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            generateDepthBuffer: false,
            generateMipMaps: false,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./pass.fragment-DS6uQAkG.esm.js')]);
                }
                else {
                    await Promise.all([import('./pass.fragment-Bo-QegcY.esm.js')]);
                }
            },
        };
        this._oldPositionCopy = new ProceduralTexture("oldLocalPositionRT", { width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, "pass", this._scene, localPositionOptions, false);
        this._updatePositionCopy();
        this._oldPositionCopy.autoClear = false;
        this._oldPositionCopy.refreshRate = 1;
        this._oldPositionCopy.onBeforeGenerationObservable.add(this._updatePositionCopy.bind(this));
    }
    _setOutputTextureBindings() {
        const remanence = this._isMoving ? this.remanence : 0.99;
        this._accumulationParams.set(remanence, this.reset ? 1.0 : 0.0, this._renderPipeline.voxelGridSize, 0.0);
        this._outputTexture.setTexture("spatialBlurSampler", this._renderPipeline._getSpatialBlurTexture());
        this._outputTexture.setVector4("accumulationParameters", this._accumulationParams);
        this._outputTexture.setTexture("oldAccumulationSampler", this._oldAccumulationCopy ? this._oldAccumulationCopy : this._renderPipeline._dummyTexture2d);
        this._outputTexture.setTexture("prevPositionSampler", this._oldPositionCopy ? this._oldPositionCopy : this._renderPipeline._dummyTexture2d);
        const geometryBufferRenderer = this._scene.geometryBufferRenderer;
        if (!geometryBufferRenderer) {
            return false;
        }
        const velocityIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.VELOCITY_LINEAR_TEXTURE_TYPE);
        this._outputTexture.setTexture("motionSampler", geometryBufferRenderer.getGBuffer().textures[velocityIndex]);
        const wPositionIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE);
        this._outputTexture.setTexture("positionSampler", geometryBufferRenderer.getGBuffer().textures[wPositionIndex]);
        this.reset = false;
        this._isMoving = false;
        return true;
    }
    _updatePositionCopy() {
        const geometryBufferRenderer = this._scene.geometryBufferRenderer;
        if (geometryBufferRenderer) {
            const index = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE);
            this._oldPositionCopy.setTexture("textureSampler", geometryBufferRenderer.getGBuffer().textures[index]);
        }
    }
    _setAccumulationCopyBindings() {
        this._oldAccumulationCopy.setTexture("textureSampler", this._outputTexture);
    }
    _render() {
        if (this.enabled && this._outputTexture.isReady() && this._outputTexture.getEffect()?.isReady()) {
            if (this._setOutputTextureBindings()) {
                this._outputTexture.render();
            }
        }
    }
    /**
     * Called by render pipeline when canvas resized.
     * @param scaleFactor The factor by which to scale the canvas size.
     */
    resize(scaleFactor = 1.0) {
        const newSize = {
            width: Math.max(1.0, Math.floor(this._engine.getRenderWidth() * scaleFactor)),
            height: Math.max(1.0, Math.floor(this._engine.getRenderHeight() * scaleFactor)),
        };
        // Don't resize if the size is the same as the current size.
        if (this._outputTexture.getSize().width === newSize.width && this._outputTexture.getSize().height === newSize.height) {
            return;
        }
        this._outputTexture.resize(newSize, false);
        this._oldAccumulationCopy.resize(newSize, false);
        this._oldPositionCopy.resize({ width: this._engine.getRenderWidth(), height: this._engine.getRenderHeight() }, false);
        this.reset = true;
    }
    _disposeTextures() {
        this._oldAccumulationCopy.dispose();
        this._oldPositionCopy.dispose();
        this._outputTexture.dispose();
    }
    /**
     * Checks if the pass is ready
     * @returns true if the pass is ready
     */
    isReady() {
        return (this._oldAccumulationCopy &&
            this._oldAccumulationCopy.isReady() &&
            this._oldPositionCopy &&
            this._oldPositionCopy.isReady() &&
            this._outputTexture.isReady() &&
            !(this._debugPassPP && !this._debugPassPP.isReady()));
    }
    /**
     * Disposes the associated resources
     */
    dispose() {
        if (this._scene.geometryBufferRenderer && this._renderWhenGBufferReady) {
            const gBuffer = this._scene.geometryBufferRenderer.getGBuffer();
            gBuffer.onAfterRenderObservable.removeCallback(this._renderWhenGBufferReady);
        }
        this._disposeTextures();
        if (this._debugPassPP) {
            this._debugPassPP.dispose();
        }
        this.onReadyObservable.clear();
    }
}

/**
 * Class used to store 3D textures containing user data
 */
class RawTexture3D extends Texture {
    /**
     * Gets the width of the texture
     */
    get width() {
        return this._texture ? this._texture.width : 0;
    }
    /**
     * Gets the height of the texture
     */
    get height() {
        return this._texture ? this._texture.height : 0;
    }
    /**
     * Gets the depth of the texture
     */
    get depth() {
        return this._texture ? this._texture.depth : 0;
    }
    /**
     * Create a new RawTexture3D
     * @param data defines the data of the texture
     * @param width defines the width of the texture
     * @param height defines the height of the texture
     * @param depth defines the depth of the texture
     * @param format defines the texture format to use
     * @param scene defines the hosting scene
     * @param generateMipMaps defines a boolean indicating if mip levels should be generated (true by default)
     * @param invertY defines if texture must be stored with Y axis inverted
     * @param samplingMode defines the sampling mode to use (Texture.TRILINEAR_SAMPLINGMODE by default)
     * @param textureType defines the texture Type (Engine.TEXTURETYPE_UNSIGNED_BYTE, Engine.TEXTURETYPE_FLOAT...)
     * @param creationFlags specific flags to use when creating the texture (Constants.TEXTURE_CREATIONFLAG_STORAGE for storage textures, for eg)
     */
    constructor(data, width, height, depth, 
    /** Gets or sets the texture format to use */
    format, scene, generateMipMaps = true, invertY = false, samplingMode = Texture.TRILINEAR_SAMPLINGMODE, textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE, creationFlags) {
        super(null, scene, !generateMipMaps, invertY);
        this.format = format;
        this._texture = scene.getEngine().createRawTexture3D(data, width, height, depth, format, generateMipMaps, invertY, samplingMode, null, textureType, creationFlags);
        this.is3D = true;
    }
    /**
     * Update the texture with new data
     * @param data defines the data to store in the texture
     */
    update(data) {
        if (!this._texture) {
            return;
        }
        this._getEngine().updateRawTexture3D(this._texture, data, this._texture.format, this._texture.invertY, null, this._texture.type);
    }
}

/** This file must only contain pure code and pure imports */
/**
 * @internal
 */
class MaterialIBLShadowsRenderDefines extends MaterialDefines {
    constructor() {
        super(...arguments);
        this.RENDER_WITH_IBL_SHADOWS = false;
        this.COLORED_IBL_SHADOWS = false;
    }
}
/**
 * Plugin used to render the contribution from IBL shadows.
 */
let IBLShadowsPluginMaterial = (() => {
    var _a, _IBLShadowsPluginMaterial_isEnabled_accessor_storage;
    let _classSuper = MaterialPluginBase;
    let _shadowOpacity_decorators;
    let _shadowOpacity_initializers = [];
    let _shadowOpacity_extraInitializers = [];
    let _isEnabled_decorators;
    let _isEnabled_initializers = [];
    let _isEnabled_extraInitializers = [];
    return _a = class IBLShadowsPluginMaterial extends _classSuper {
            get iblShadowsTexture() {
                return this._iblShadowsTexture;
            }
            set iblShadowsTexture(value) {
                if (this._iblShadowsTexture === value) {
                    return;
                }
                this._iblShadowsTexture = value;
                this._markAllSubMeshesAsTexturesDirty();
            }
            get isColored() {
                return this._isColored;
            }
            set isColored(value) {
                if (this._isColored === value) {
                    return;
                }
                this._isColored = value;
                this._markAllSubMeshesAsTexturesDirty();
            }
            /**
             * Defines if the plugin is enabled in the material.
             */
            get isEnabled() { return __classPrivateFieldGet(this, _IBLShadowsPluginMaterial_isEnabled_accessor_storage, "f"); }
            set isEnabled(value) { __classPrivateFieldSet(this, _IBLShadowsPluginMaterial_isEnabled_accessor_storage, value, "f"); }
            _markAllSubMeshesAsTexturesDirty() {
                this._enable(this._isEnabled);
                this._internalMarkAllSubMeshesAsTexturesDirty();
            }
            /**
             * Gets a boolean indicating that the plugin is compatible with a give shader language.
             * @returns true if the plugin is compatible with the shader language
             */
            isCompatible() {
                return true;
            }
            constructor(material) {
                super(material, _a.Name, 310, new MaterialIBLShadowsRenderDefines());
                /**
                 * The opacity of the shadows.
                 */
                this.shadowOpacity = __runInitializers(this, _shadowOpacity_initializers, 1.0);
                this._isEnabled = (__runInitializers(this, _shadowOpacity_extraInitializers), false);
                this._isColored = false;
                _IBLShadowsPluginMaterial_isEnabled_accessor_storage.set(this, __runInitializers(this, _isEnabled_initializers, false));
                this._internalMarkAllSubMeshesAsTexturesDirty = __runInitializers(this, _isEnabled_extraInitializers);
                this._internalMarkAllSubMeshesAsTexturesDirty = material._dirtyCallbacks[Constants.MATERIAL_TextureDirtyFlag];
            }
            _isOpenPBRMaterial() {
                return this._material.getClassName() === "OpenPBRMaterial";
            }
            prepareDefines(defines) {
                defines.RENDER_WITH_IBL_SHADOWS = this._isEnabled;
                defines.COLORED_IBL_SHADOWS = this.isColored;
            }
            getClassName() {
                return "IBLShadowsPluginMaterial";
            }
            getUniforms(_shaderLanguage) {
                const result = {};
                result.ubo = [];
                if (this._isOpenPBRMaterial()) {
                    if (_shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
                        result.fragment = `#ifdef RENDER_WITH_IBL_SHADOWS
                    var shadowOpacity: f32;
                #endif`;
                    }
                    else {
                        result.fragment = `#ifdef RENDER_WITH_IBL_SHADOWS
                    uniform float shadowOpacity;
                #endif`;
                    }
                }
                else {
                    result.ubo.push({ name: "renderTargetSize", size: 2, type: "vec2" });
                    if (_shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
                        result.fragment = `#ifdef RENDER_WITH_IBL_SHADOWS
                    var renderTargetSize: vec2f;
                    var shadowOpacity: f32;
                #endif`;
                    }
                    else {
                        result.fragment = `#ifdef RENDER_WITH_IBL_SHADOWS
                    uniform vec2 renderTargetSize;
                    uniform float shadowOpacity;
                #endif`;
                    }
                }
                result.ubo.push({ name: "shadowOpacity", size: 1, type: "float" });
                return result;
            }
            getSamplers(samplers) {
                samplers.push("iblShadowsTexture");
            }
            bindForSubMesh(uniformBuffer) {
                if (this._isEnabled && this.iblShadowsTexture) {
                    uniformBuffer.bindTexture("iblShadowsTexture", this.iblShadowsTexture);
                    uniformBuffer.updateFloat2("renderTargetSize", this._material.getScene().getEngine().getRenderWidth(), this._material.getScene().getEngine().getRenderHeight());
                    uniformBuffer.updateFloat("shadowOpacity", this.shadowOpacity);
                }
            }
            getCustomCode(shaderType, shaderLanguage) {
                let frag;
                if (shaderLanguage === 1 /* ShaderLanguage.WGSL */) {
                    frag = {
                        // eslint-disable-next-line @typescript-eslint/naming-convention
                        CUSTOM_FRAGMENT_DEFINITIONS: `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    var iblShadowsTextureSampler: sampler;
                    var iblShadowsTexture: texture_2d<f32>;

                    #ifdef COLORED_IBL_SHADOWS
                        fn computeIndirectShadow() -> vec3f {
                            var uv = fragmentInputs.position.xy / uniforms.renderTargetSize;
                            var shadowValue: vec3f = textureSample(iblShadowsTexture, iblShadowsTextureSampler, uv).rgb;
                            return mix(shadowValue, vec3f(1.0), 1.0 - uniforms.shadowOpacity);
                        }
                    #else
                        fn computeIndirectShadow() -> vec2f {
                            var uv = fragmentInputs.position.xy / uniforms.renderTargetSize;
                            var shadowValue: vec2f = textureSample(iblShadowsTexture, iblShadowsTextureSampler, uv).rg;
                            return mix(shadowValue, vec2f(1.0), 1.0 - uniforms.shadowOpacity);
                        }
                    #endif
                #endif
            `,
                    };
                    if (this._material instanceof PBRBaseMaterial) {
                        // eslint-disable-next-line @typescript-eslint/naming-convention
                        frag["CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION"] = `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    #ifndef UNLIT
                        #ifdef REFLECTION
                            #ifdef COLORED_IBL_SHADOWS
                                var shadowValue: vec3f = computeIndirectShadow();
                                finalIrradiance *= shadowValue;
                                finalRadianceScaled *= mix(vec3f(1.0), shadowValue, roughness);
                            #else
                                var shadowValue: vec2f = computeIndirectShadow();
                                finalIrradiance *= vec3f(shadowValue.x);
                                finalRadianceScaled *= vec3f(mix(pow(shadowValue.y, 4.0), shadowValue.x, roughness));
                            #endif
                        #endif
                    #else
                        finalDiffuse *= computeIndirectShadow().x;
                    #endif
                #endif
            `;
                    }
                    else if (this._isOpenPBRMaterial()) {
                        // eslint-disable-next-line @typescript-eslint/naming-convention
                        frag["CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION"] = `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    #ifndef UNLIT
                        #ifdef REFLECTION
                            #ifdef COLORED_IBL_SHADOWS
                                var shadowValue: vec3f = computeIndirectShadow();
                                ambient_occlusion = min(ambient_occlusion, shadowValue);
                            #else
                                var shadowValue: vec2f = computeIndirectShadow();
                                ambient_occlusion = min(ambient_occlusion, vec3f(shadowValue.x));
                                specular_ambient_occlusion = min(specular_ambient_occlusion, pow(shadowValue.y, 4.0));
                            #endif
                        #endif
                    #else
                        ambient_occlusion = min(ambient_occlusion, vec3f(computeIndirectShadow().x));
                    #endif
                #endif
            `;
                    }
                    else {
                        frag["CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR"] = `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    #ifdef COLORED_IBL_SHADOWS
                        var shadowValue: vec3f = computeIndirectShadow();
                        color *= toGammaSpace(vec4f(shadowValue, 1.0f));
                    #else
                        var shadowValue: vec2f = computeIndirectShadow();
                        color *= toGammaSpace(vec4f(shadowValue.x, shadowValue.x, shadowValue.x, 1.0f));
                    #endif
                #endif
            `;
                    }
                }
                else {
                    frag = {
                        // eslint-disable-next-line @typescript-eslint/naming-convention
                        CUSTOM_FRAGMENT_DEFINITIONS: `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    uniform sampler2D iblShadowsTexture;
                #ifdef COLORED_IBL_SHADOWS
                    vec3 computeIndirectShadow() {
                        vec2 uv = gl_FragCoord.xy / renderTargetSize;
                        vec3 shadowValue = texture2D(iblShadowsTexture, uv).rgb;
                        return mix(shadowValue.rgb, vec3(1.0), 1.0 - shadowOpacity);
                    }
                #else
                    vec2 computeIndirectShadow() {
                        vec2 uv = gl_FragCoord.xy / renderTargetSize;
                        vec2 shadowValue = texture2D(iblShadowsTexture, uv).rg;
                        return mix(shadowValue.rg, vec2(1.0), 1.0 - shadowOpacity);
                    }
                #endif
                #endif
            `,
                    };
                    if (this._material instanceof PBRBaseMaterial) {
                        // eslint-disable-next-line @typescript-eslint/naming-convention
                        frag["CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION"] = `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    #ifndef UNLIT
                        #ifdef REFLECTION
                            #ifdef COLORED_IBL_SHADOWS
                                vec3 shadowValue = computeIndirectShadow();
                                finalIrradiance.rgb *= shadowValue.rgb;
                                finalRadianceScaled *= mix(vec3(1.0), shadowValue.rgb, roughness);
                            #else
                                vec2 shadowValue = computeIndirectShadow();
                                finalIrradiance *= shadowValue.x;
                                finalRadianceScaled *= mix(pow(shadowValue.y, 4.0), shadowValue.x, roughness);
                            #endif
                        #endif
                    #else
                        finalDiffuse *= computeIndirectShadow().x;
                    #endif
                #endif
            `;
                    }
                    else if (this._isOpenPBRMaterial()) {
                        // eslint-disable-next-line @typescript-eslint/naming-convention
                        frag["CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION"] = `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    #ifndef UNLIT
                        #ifdef REFLECTION
                            #ifdef COLORED_IBL_SHADOWS
                                vec3 shadowValue = computeIndirectShadow();
                                ambient_occlusion = min(ambient_occlusion, shadowValue);
                            #else
                                vec2 shadowValue = computeIndirectShadow();
                                ambient_occlusion = min(ambient_occlusion, vec3(shadowValue.x));
                                specular_ambient_occlusion = min(specular_ambient_occlusion, pow(shadowValue.y, 4.0));
                            #endif
                        #endif
                    #else
                        ambient_occlusion = min(ambient_occlusion, vec3(computeIndirectShadow().x));
                    #endif
                #endif
            `;
                    }
                    else {
                        frag["CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR"] = `
                #ifdef RENDER_WITH_IBL_SHADOWS
                    #ifdef COLORED_IBL_SHADOWS
                        vec3 shadowValue = computeIndirectShadow();
                        color.rgb *= toGammaSpace(shadowValue.rgb);
                    #else
                        vec2 shadowValue = computeIndirectShadow();
                        color.rgb *= toGammaSpace(shadowValue.x);
                    #endif
                #endif
            `;
                    }
                }
                return shaderType === "vertex" ? null : frag;
            }
        },
        _IBLShadowsPluginMaterial_isEnabled_accessor_storage = new WeakMap(),
        (() => {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _shadowOpacity_decorators = [serialize()];
            _isEnabled_decorators = [serialize(), expandToProperty("_markAllSubMeshesAsTexturesDirty")];
            __esDecorate(_a, null, _isEnabled_decorators, { kind: "accessor", name: "isEnabled", static: false, private: false, access: { has: obj => "isEnabled" in obj, get: obj => obj.isEnabled, set: (obj, value) => { obj.isEnabled = value; } }, metadata: _metadata }, _isEnabled_initializers, _isEnabled_extraInitializers);
            __esDecorate(null, null, _shadowOpacity_decorators, { kind: "field", name: "shadowOpacity", static: false, private: false, access: { has: obj => "shadowOpacity" in obj, get: obj => obj.shadowOpacity, set: (obj, value) => { obj.shadowOpacity = value; } }, metadata: _metadata }, _shadowOpacity_initializers, _shadowOpacity_extraInitializers);
            if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        })(),
        /**
         * Defines the name of the plugin.
         */
        _a.Name = "IBLShadowsPluginMaterial",
        _a;
})();

/** This file must only contain pure code and pure imports */
/**
 * Voxel-based shadow rendering for IBL's.
 * This should not be instanciated directly, as it is part of a scene component
 */
class IblShadowsRenderPipeline extends PostProcessRenderPipeline {
    /**
     * Reset the shadow accumulation. This has a similar affect to lowering the remanence for a single frame.
     * This is useful when making a sudden change to the IBL.
     */
    resetAccumulation() {
        this._accumulationPass.reset = true;
    }
    /**
     * How dark the shadows appear. 1.0 is full opacity, 0.0 is no shadows.
     */
    get shadowOpacity() {
        return this._shadowOpacity;
    }
    set shadowOpacity(value) {
        this._shadowOpacity = value;
        this._setPluginParameters();
    }
    /**
     * Render the shadows in color rather than black and white.
     * This is slightly more expensive than black and white shadows but can be much
     * more accurate when the strongest lights in the IBL are non-white.
     */
    get coloredShadows() {
        return this._coloredShadows;
    }
    set coloredShadows(value) {
        this._coloredShadows = value;
        this._voxelTracingPass.coloredShadows = value;
        this._setPluginParameters();
    }
    /**
     * A multiplier for the render size of the shadows. Used for rendering lower-resolution shadows.
     */
    get shadowRenderSizeFactor() {
        return this._renderSizeFactor;
    }
    set shadowRenderSizeFactor(value) {
        this._renderSizeFactor = Math.max(Math.min(value, 1.0), 0.0);
        this._voxelTracingPass.resize(value);
        this._spatialBlurPass.resize(value);
        this._accumulationPass.resize(value);
        this._setPluginParameters();
    }
    /**
     * How dark the voxel shadows appear. 1.0 is full opacity, 0.0 is no shadows.
     */
    get voxelShadowOpacity() {
        return this._voxelTracingPass?.voxelShadowOpacity;
    }
    set voxelShadowOpacity(value) {
        if (!this._voxelTracingPass) {
            return;
        }
        this._voxelTracingPass.voxelShadowOpacity = value;
    }
    /**
     * How dark the screen-space shadows appear. 1.0 is full opacity, 0.0 is no shadows.
     */
    get ssShadowOpacity() {
        return this._voxelTracingPass?.ssShadowOpacity;
    }
    set ssShadowOpacity(value) {
        if (!this._voxelTracingPass) {
            return;
        }
        this._voxelTracingPass.ssShadowOpacity = value;
    }
    /**
     * The number of samples used in the screen space shadow pass.
     */
    get ssShadowSampleCount() {
        return this._voxelTracingPass?.sssSamples;
    }
    set ssShadowSampleCount(value) {
        if (!this._voxelTracingPass) {
            return;
        }
        this._voxelTracingPass.sssSamples = value;
    }
    /**
     * The stride of the screen-space shadow pass. This controls the distance between samples
     * in pixels.
     */
    get ssShadowStride() {
        return this._voxelTracingPass?.sssStride;
    }
    set ssShadowStride(value) {
        if (!this._voxelTracingPass) {
            return;
        }
        this._voxelTracingPass.sssStride = value;
    }
    /**
     * A scale for the maximum distance a screen-space shadow can be cast in world-space.
     * The maximum distance that screen-space shadows cast is derived from the voxel size
     * and this value so shouldn't need to change if you scale your scene
     */
    get ssShadowDistanceScale() {
        return this._sssMaxDistScale;
    }
    set ssShadowDistanceScale(value) {
        this._sssMaxDistScale = value;
        this._updateSsShadowParams();
    }
    /**
     * Screen-space shadow thickness scale. This value controls the assumed thickness of
     * on-screen surfaces in world-space. It scales with the size of the shadow-casting
     * region so shouldn't need to change if you scale your scene.
     */
    get ssShadowThicknessScale() {
        return this._sssThicknessScale;
    }
    set ssShadowThicknessScale(value) {
        this._sssThicknessScale = value;
        this._updateSsShadowParams();
    }
    /**
     * Returns the texture containing the voxel grid data
     * @returns The texture containing the voxel grid data
     * @internal
     */
    _getVoxelGridTexture() {
        const tex = this._voxelRenderer?.getVoxelGrid();
        if (tex && tex.isReady()) {
            return tex;
        }
        return this._dummyTexture3d;
    }
    /**
     * Returns the noise texture.
     * @returns The noise texture.
     * @internal
     */
    _getNoiseTexture() {
        const tex = this._noiseTexture;
        if (tex && tex.isReady()) {
            return tex;
        }
        return this._dummyTexture2d;
    }
    /**
     * Returns the voxel-tracing texture.
     * @returns The voxel-tracing texture.
     * @internal
     */
    _getVoxelTracingTexture() {
        const tex = this._voxelTracingPass?.getOutputTexture();
        if (tex && tex.isReady()) {
            return tex;
        }
        return this._dummyTexture2d;
    }
    /**
     * Returns the spatial blur texture.
     * @returns The spatial blur texture.
     * @internal
     */
    _getSpatialBlurTexture() {
        const tex = this._spatialBlurPass.getOutputTexture();
        if (tex && tex.isReady()) {
            return tex;
        }
        return this._dummyTexture2d;
    }
    /**
     * Returns the accumulated shadow texture.
     * @returns The accumulated shadow texture.
     * @internal
     */
    _getAccumulatedTexture() {
        const tex = this._accumulationPass?.getOutputTexture();
        if (tex && tex.isReady()) {
            return tex;
        }
        return this._dummyTexture2d;
    }
    /**
     * Turn on or off the debug view of the G-Buffer. This will display only the targets
     * of the g-buffer that are used by the shadow pipeline.
     */
    get gbufferDebugEnabled() {
        return this._gbufferDebugEnabled;
    }
    set gbufferDebugEnabled(enabled) {
        if (enabled && !this.allowDebugPasses) {
            Logger.Warn("Can't enable G-Buffer debug view without setting allowDebugPasses to true.");
            return;
        }
        this._gbufferDebugEnabled = enabled;
        if (enabled) {
            this._enableEffect(this._getGBufferDebugPass().name, this.cameras);
        }
        else {
            this._disableEffect(this._getGBufferDebugPass().name, this.cameras);
        }
    }
    /**
     * Turn on or off the debug view of the CDF importance sampling data
     */
    get cdfDebugEnabled() {
        return this.scene.iblCdfGenerator ? this.scene.iblCdfGenerator.debugEnabled : false;
    }
    /**
     * Turn on or off the debug view of the CDF importance sampling data
     */
    set cdfDebugEnabled(enabled) {
        if (!this.scene.iblCdfGenerator) {
            return;
        }
        if (enabled && !this.allowDebugPasses) {
            Logger.Warn("Can't enable importance sampling debug view without setting allowDebugPasses to true.");
            return;
        }
        if (enabled === this.scene.iblCdfGenerator.debugEnabled) {
            return;
        }
        this.scene.iblCdfGenerator.debugEnabled = enabled;
        if (enabled) {
            this._enableEffect(this.scene.iblCdfGenerator.debugPassName, this.cameras);
        }
        else {
            this._disableEffect(this.scene.iblCdfGenerator.debugPassName, this.cameras);
        }
    }
    /**
     * Display the debug view for just the shadow samples taken this frame.
     */
    get voxelTracingDebugEnabled() {
        return this._voxelTracingPass?.debugEnabled;
    }
    set voxelTracingDebugEnabled(enabled) {
        if (!this._voxelTracingPass) {
            return;
        }
        if (enabled && !this.allowDebugPasses) {
            Logger.Warn("Can't enable voxel tracing debug view without setting allowDebugPasses to true.");
            return;
        }
        if (enabled === this._voxelTracingPass.debugEnabled) {
            return;
        }
        this._voxelTracingPass.debugEnabled = enabled;
        if (enabled) {
            this._enableEffect(this._voxelTracingPass.debugPassName, this.cameras);
        }
        else {
            this._disableEffect(this._voxelTracingPass.debugPassName, this.cameras);
        }
    }
    /**
     * Display the debug view for the spatial blur pass
     */
    get spatialBlurPassDebugEnabled() {
        return this._spatialBlurPass.debugEnabled;
    }
    set spatialBlurPassDebugEnabled(enabled) {
        if (!this._spatialBlurPass) {
            return;
        }
        if (enabled && !this.allowDebugPasses) {
            Logger.Warn("Can't enable spatial blur debug view without setting allowDebugPasses to true.");
            return;
        }
        if (enabled === this._spatialBlurPass.debugEnabled) {
            return;
        }
        this._spatialBlurPass.debugEnabled = enabled;
        if (enabled) {
            this._enableEffect(this._spatialBlurPass.debugPassName, this.cameras);
        }
        else {
            this._disableEffect(this._spatialBlurPass.debugPassName, this.cameras);
        }
    }
    /**
     * Display the debug view for the shadows accumulated over time.
     */
    get accumulationPassDebugEnabled() {
        return this._accumulationPass?.debugEnabled;
    }
    set accumulationPassDebugEnabled(enabled) {
        if (!this._accumulationPass) {
            return;
        }
        if (enabled && !this.allowDebugPasses) {
            Logger.Warn("Can't enable accumulation pass debug view without setting allowDebugPasses to true.");
            return;
        }
        if (enabled === this._accumulationPass.debugEnabled) {
            return;
        }
        this._accumulationPass.debugEnabled = enabled;
        if (enabled) {
            this._enableEffect(this._accumulationPass.debugPassName, this.cameras);
        }
        else {
            this._disableEffect(this._accumulationPass.debugPassName, this.cameras);
        }
    }
    /**
     * Add a mesh to be used for shadow-casting in the IBL shadow pipeline.
     * These meshes will be written to the voxel grid.
     * @param mesh A mesh or list of meshes that you want to cast shadows
     */
    addShadowCastingMesh(mesh) {
        if (Array.isArray(mesh)) {
            for (const m of mesh) {
                if (m && this._shadowCastingMeshes.indexOf(m) === -1) {
                    this._shadowCastingMeshes.push(m);
                    if (IsGaussianSplattingClassName(m.getClassName())) {
                        m.needsRotationScaleTextures = true;
                    }
                }
            }
        }
        else {
            if (mesh && this._shadowCastingMeshes.indexOf(mesh) === -1) {
                this._shadowCastingMeshes.push(mesh);
                if (IsGaussianSplattingClassName(mesh.getClassName())) {
                    mesh.needsRotationScaleTextures = true;
                }
            }
        }
    }
    /**
     * Remove a mesh from the shadow-casting list. The mesh will no longer be written
     * to the voxel grid and will not cast shadows.
     * @param mesh The mesh or list of meshes that you don't want to cast shadows.
     */
    removeShadowCastingMesh(mesh) {
        if (Array.isArray(mesh)) {
            for (const m of mesh) {
                const index = this._shadowCastingMeshes.indexOf(m);
                if (index !== -1) {
                    this._shadowCastingMeshes.splice(index, 1);
                    if (IsGaussianSplattingClassName(m.getClassName())) {
                        m.needsRotationScaleTextures = false;
                    }
                }
            }
        }
        else {
            const index = this._shadowCastingMeshes.indexOf(mesh);
            if (index !== -1) {
                this._shadowCastingMeshes.splice(index, 1);
                if (IsGaussianSplattingClassName(mesh.getClassName())) {
                    mesh.needsRotationScaleTextures = false;
                }
            }
        }
    }
    /**
     * Clear the list of shadow-casting meshes. This will remove all meshes from the list
     */
    clearShadowCastingMeshes() {
        for (const m of this._shadowCastingMeshes) {
            if (IsGaussianSplattingClassName(m.getClassName())) {
                m.needsRotationScaleTextures = false;
            }
        }
        this._shadowCastingMeshes.length = 0;
    }
    /**
     * The exponent of the resolution of the voxel shadow grid. Higher resolutions will result in sharper
     * shadows but are more expensive to compute and require more memory.
     * The resolution is calculated as 2 to the power of this number.
     */
    get resolutionExp() {
        return this._voxelRenderer.voxelResolutionExp;
    }
    set resolutionExp(newResolution) {
        if (newResolution === this._voxelRenderer.voxelResolutionExp) {
            return;
        }
        if (this._voxelRenderer.isVoxelizationInProgress()) {
            Logger.Warn("Can't change the resolution of the voxel grid while voxelization is in progress.");
            return;
        }
        this._voxelRenderer.voxelResolutionExp = Math.max(1, Math.min(newResolution, 8));
        this._accumulationPass.reset = true;
    }
    /**
     * The number of different directions to sample during the voxel tracing pass
     */
    get sampleDirections() {
        return this._voxelTracingPass?.sampleDirections;
    }
    /**
     * The number of different directions to sample during the voxel tracing pass
     */
    set sampleDirections(value) {
        if (!this._voxelTracingPass) {
            return;
        }
        this._voxelTracingPass.sampleDirections = value;
    }
    /**
     * The decree to which the shadows persist between frames. 0.0 is no persistence, 1.0 is full persistence.
     **/
    get shadowRemanence() {
        return this._accumulationPass?.remanence;
    }
    /**
     * The decree to which the shadows persist between frames. 0.0 is no persistence, 1.0 is full persistence.
     **/
    set shadowRemanence(value) {
        if (!this._accumulationPass) {
            return;
        }
        this._accumulationPass.remanence = value;
    }
    /**
     * The global Y-axis rotation of the IBL for shadows. This should match the Y-rotation of the environment map applied to materials, skybox, etc.
     */
    get envRotation() {
        return this._voxelTracingPass?.envRotation;
    }
    /**
     * The global Y-axis rotation of the IBL for shadows. This should match the Y-rotation of the environment map applied to materials, skybox, etc.
     */
    set envRotation(value) {
        if (!this._voxelTracingPass) {
            return;
        }
        this._voxelTracingPass.envRotation = value;
        this._accumulationPass.reset = true;
    }
    /**
     * Allow debug passes to be enabled. Default is false.
     */
    get allowDebugPasses() {
        return this._allowDebugPasses;
    }
    /**
     * Allow debug passes to be enabled. Default is false.
     */
    set allowDebugPasses(value) {
        if (this._allowDebugPasses === value) {
            return;
        }
        this._allowDebugPasses = value;
        if (value && this.scene.iblCdfGenerator) {
            if (this.scene.iblCdfGenerator.isReady()) {
                this._createDebugPasses();
            }
            else {
                this.scene.iblCdfGenerator.onGeneratedObservable.addOnce(() => {
                    this._createDebugPasses();
                });
            }
        }
        else {
            this._disposeDebugPasses();
        }
    }
    /**
     *  Support test.
     */
    static get IsSupported() {
        const engine = EngineStore.LastCreatedEngine;
        if (!engine) {
            return false;
        }
        return engine._features.supportIBLShadows;
    }
    /**
     * Toggle the shadow tracing on or off
     * @param enabled Toggle the shadow tracing on or off
     */
    toggleShadow(enabled) {
        this._enabled = enabled;
        this._voxelTracingPass.enabled = enabled;
        this._spatialBlurPass.enabled = enabled;
        this._accumulationPass.enabled = enabled;
        for (const mat of this._materialsWithRenderPlugin) {
            if (mat.pluginManager) {
                const plugin = mat.pluginManager.getPlugin(IBLShadowsPluginMaterial.Name);
                plugin.isEnabled = enabled;
            }
        }
        this._setPluginParameters();
    }
    /**
     * Trigger the scene to be re-voxelized. This should be run when any shadow-casters have been added, removed or moved.
     */
    updateVoxelization() {
        if (this._shadowCastingMeshes.length === 0) {
            Logger.Warn("IBL Shadows: updateVoxelization called with no shadow-casting meshes to voxelize.");
            return;
        }
        this._voxelRenderer.updateVoxelGrid(this._shadowCastingMeshes);
        this._voxelRenderer.onVoxelizationCompleteObservable.addOnce(() => {
            this.onVoxelizationCompleteObservable.notifyObservers();
        });
        this._updateSsShadowParams();
    }
    /**
     * Trigger the scene bounds of shadow-casters to be calculated. This is the world size that the voxel grid will cover and will always be a cube.
     */
    updateSceneBounds() {
        const bounds = {
            min: new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE),
            max: new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE),
        };
        for (const mesh of this._shadowCastingMeshes) {
            const localBounds = mesh.getHierarchyBoundingVectors(true);
            bounds.min = Vector3.Minimize(bounds.min, localBounds.min);
            bounds.max = Vector3.Maximize(bounds.max, localBounds.max);
        }
        if (this._shadowCastingMeshes.length === 0) {
            Logger.Warn("IBL Shadows: updateSceneBounds called with no shadow-casting meshes.");
            this.voxelGridSize = 1.0;
            return;
        }
        // If no visible geometry contributed, bounds stay at sentinel values. Keep the
        // last valid invWorldScale and return silently — this is expected when all parts
        // are temporarily hidden (e.g. while switching between displayed models).
        if (bounds.min.x > bounds.max.x) {
            return;
        }
        const size = bounds.max.subtract(bounds.min);
        this.voxelGridSize = Math.max(size.x, size.y, size.z);
        if (!isFinite(this.voxelGridSize) || this.voxelGridSize === 0) {
            Logger.Warn("IBL Shadows: Scene size is invalid. Can't update bounds.");
            this.voxelGridSize = 1.0;
            return;
        }
        const halfSize = this.voxelGridSize / 2.0;
        const centre = bounds.max.add(bounds.min).multiplyByFloats(-0.5, -0.5, -0.5);
        const invWorldScaleMatrix = Matrix.Compose(new Vector3(1.0 / halfSize, 1.0 / halfSize, 1.0 / halfSize), new Quaternion(), new Vector3(0, 0, 0));
        const invTranslationMatrix = Matrix.Compose(new Vector3(1.0, 1.0, 1.0), new Quaternion(), centre);
        invTranslationMatrix.multiplyToRef(invWorldScaleMatrix, invWorldScaleMatrix);
        this._voxelTracingPass.setWorldScaleMatrix(invWorldScaleMatrix);
        this._voxelRenderer.setWorldScaleMatrix(invWorldScaleMatrix);
        // Set world scale for spatial blur.
        this._spatialBlurPass.setWorldScale(halfSize * 2.0);
        this._updateSsShadowParams();
    }
    /**
     * @param name The rendering pipeline name
     * @param scene The scene linked to this pipeline
     * @param options Options to configure the pipeline
     * @param cameras Cameras to apply the pipeline to.
     */
    constructor(name, scene, options = {}, cameras) {
        super(scene.getEngine(), name);
        this._allowDebugPasses = false;
        this._debugPasses = [];
        this._shadowCastingMeshes = [];
        this._shadowOpacity = 0.8;
        this._enabled = true;
        this._coloredShadows = false;
        this._materialsWithRenderPlugin = [];
        /**
         * Observable that triggers when the shadow renderer is ready
         */
        this.onShadowTextureReadyObservable = new Observable();
        /**
         * Observable that triggers when a new IBL is set and the importance sampling is ready
         */
        this.onNewIblReadyObservable = new Observable();
        /**
         * Observable that triggers when the voxelization is complete
         */
        this.onVoxelizationCompleteObservable = new Observable();
        /**
         * The current world-space size of that the voxel grid covers in the scene.
         */
        this.voxelGridSize = 1.0;
        this._renderSizeFactor = 1.0;
        this._gbufferDebugEnabled = false;
        this._gBufferDebugSizeParams = new Vector4(0.0, 0.0, 0.0, 0.0);
        this.scene = scene;
        RegisterGeometryBufferRendererSceneComponent(GeometryBufferRenderer);
        RegisterIblCdfGeneratorSceneComponent(IblCdfGenerator);
        this._cameras = cameras || [scene.activeCamera];
        // Create the dummy textures to be used when the pipeline is not ready
        const blackPixels = new Uint8Array([0, 0, 0, 255]);
        this._dummyTexture2d = new RawTexture(blackPixels, 1, 1, Constants.TEXTUREFORMAT_RGBA, scene, false);
        this._dummyTexture3d = new RawTexture3D(blackPixels, 1, 1, 1, Constants.TEXTUREFORMAT_RGBA, scene, false);
        // Setup the geometry buffer target formats
        const textureTypesAndFormats = {};
        textureTypesAndFormats[GeometryBufferRenderer.SCREENSPACE_DEPTH_TEXTURE_TYPE] = {
            textureFormat: Constants.TEXTUREFORMAT_R,
            textureType: Constants.TEXTURETYPE_FLOAT,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
        };
        textureTypesAndFormats[GeometryBufferRenderer.VELOCITY_LINEAR_TEXTURE_TYPE] = {
            textureFormat: Constants.TEXTUREFORMAT_RG,
            textureType: Constants.TEXTURETYPE_HALF_FLOAT,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
        };
        textureTypesAndFormats[GeometryBufferRenderer.POSITION_TEXTURE_TYPE] = {
            textureFormat: Constants.TEXTUREFORMAT_RGBA,
            textureType: Constants.TEXTURETYPE_HALF_FLOAT,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
        };
        textureTypesAndFormats[GeometryBufferRenderer.NORMAL_TEXTURE_TYPE] = {
            textureFormat: Constants.TEXTUREFORMAT_RGBA,
            textureType: Constants.TEXTURETYPE_HALF_FLOAT,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
        };
        const geometryBufferRenderer = scene.enableGeometryBufferRenderer(undefined, Constants.TEXTUREFORMAT_DEPTH32_FLOAT, textureTypesAndFormats);
        if (!geometryBufferRenderer) {
            Logger.Error("Geometry buffer renderer is required for IBL shadows to work.");
            return;
        }
        this._geometryBufferRenderer = geometryBufferRenderer;
        this._geometryBufferRenderer.enableScreenspaceDepth = true;
        this._geometryBufferRenderer.enableVelocityLinear = true;
        this._geometryBufferRenderer.enablePosition = true;
        this._geometryBufferRenderer.enableNormal = true;
        this._geometryBufferRenderer.generateNormalsInWorldSpace = true;
        this.scene.enableIblCdfGenerator();
        this.shadowOpacity = options.shadowOpacity ?? 0.8;
        this._voxelRenderer = new _IblShadowsVoxelRenderer(this.scene, this, options ? options.resolutionExp : 6, options.triPlanarVoxelization !== undefined ? options.triPlanarVoxelization : true);
        this._voxelTracingPass = new _IblShadowsVoxelTracingPass(this.scene, this);
        this._spatialBlurPass = new _IblShadowsSpatialBlurPass(this.scene, this);
        this._accumulationPass = new _IblShadowsAccumulationPass(this.scene, this);
        this._accumulationPass.onReadyObservable.addOnce(() => {
            this.onShadowTextureReadyObservable.notifyObservers();
        });
        this.sampleDirections = options.sampleDirections || 2;
        this.voxelShadowOpacity = options.voxelShadowOpacity ?? 1.0;
        this.envRotation = options.envRotation ?? 0.0;
        this.shadowRenderSizeFactor = options.shadowRenderSizeFactor || 1.0;
        this.ssShadowOpacity = options.ssShadowsEnabled === undefined || options.ssShadowsEnabled ? 1.0 : 0.0;
        this.ssShadowDistanceScale = options.ssShadowDistanceScale || 1.25;
        this.ssShadowSampleCount = options.ssShadowSampleCount || 16;
        this.ssShadowStride = options.ssShadowStride || 8;
        this.ssShadowThicknessScale = options.ssShadowThicknessScale || 1.0;
        this.shadowRemanence = options.shadowRemanence ?? 0.75;
        this._noiseTexture = new Texture(Tools.GetAssetUrl("https://assets.babylonjs.com/core/blue_noise/blue_noise_rgb.png"), this.scene, false, true, Constants.TEXTURE_NEAREST_SAMPLINGMODE);
        scene.postProcessRenderPipelineManager.addPipeline(this);
        this.scene.onActiveCameraChanged.add(this._listenForCameraChanges.bind(this));
        this.scene.onBeforeRenderObservable.add(this._updateBeforeRender.bind(this));
        this._listenForCameraChanges();
        this.scene.getEngine().onResizeObservable.add(this._handleResize.bind(this));
        // Assigning the shadow texture to the materials needs to be done after the RT's are created.
        if (this.scene.iblCdfGenerator) {
            this.scene.iblCdfGenerator.onGeneratedObservable.add(() => {
                this._setPluginParameters();
                this.onNewIblReadyObservable.notifyObservers();
            });
        }
    }
    _handleResize() {
        this._voxelRenderer.resize();
        this._voxelTracingPass.resize(this.shadowRenderSizeFactor);
        this._spatialBlurPass.resize(this.shadowRenderSizeFactor);
        this._accumulationPass.resize(this.shadowRenderSizeFactor);
        this._setPluginParameters();
    }
    _getGBufferDebugPass() {
        if (this._gbufferDebugPass) {
            return this._gbufferDebugPass;
        }
        const isWebGPU = this.engine.isWebGPU;
        const textureNames = ["depthSampler", "normalSampler", "positionSampler", "velocitySampler"];
        const options = {
            width: this.scene.getEngine().getRenderWidth(),
            height: this.scene.getEngine().getRenderHeight(),
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            engine: this.scene.getEngine(),
            textureType: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            textureFormat: Constants.TEXTUREFORMAT_RGBA,
            uniforms: ["sizeParams"],
            samplers: textureNames,
            reusable: false,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializations: (useWebGPU, list) => {
                if (useWebGPU) {
                    list.push(import('./iblShadowGBufferDebug.fragment-Bx1F4RDG.esm.js'));
                }
                else {
                    list.push(import('./iblShadowGBufferDebug.fragment-CiINH-c7.esm.js'));
                }
            },
        };
        this._gbufferDebugPass = new PostProcess("iblShadowGBufferDebug", "iblShadowGBufferDebug", options);
        if (this.engine.isWebGPU) {
            this._gbufferDebugPass.samples = this.engine.currentSampleCount ?? 1;
        }
        this._gbufferDebugPass.autoClear = false;
        this._gbufferDebugPass.onApplyObservable.add((effect) => {
            const depthIndex = this._geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.SCREENSPACE_DEPTH_TEXTURE_TYPE);
            effect.setTexture("depthSampler", this._geometryBufferRenderer.getGBuffer().textures[depthIndex]);
            const normalIndex = this._geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.NORMAL_TEXTURE_TYPE);
            effect.setTexture("normalSampler", this._geometryBufferRenderer.getGBuffer().textures[normalIndex]);
            const positionIndex = this._geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE);
            effect.setTexture("positionSampler", this._geometryBufferRenderer.getGBuffer().textures[positionIndex]);
            const velocityIndex = this._geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.VELOCITY_LINEAR_TEXTURE_TYPE);
            effect.setTexture("velocitySampler", this._geometryBufferRenderer.getGBuffer().textures[velocityIndex]);
            effect.setVector4("sizeParams", this._gBufferDebugSizeParams);
            if (this.scene.activeCamera) {
                effect.setFloat("maxDepth", this.scene.activeCamera.maxZ);
            }
        });
        return this._gbufferDebugPass;
    }
    _createDebugPasses() {
        if (this.scene.iblCdfGenerator) {
            this._debugPasses = [{ pass: this.scene.iblCdfGenerator.getDebugPassPP(), enabled: this.cdfDebugEnabled }];
        }
        else {
            this._debugPasses = [];
        }
        this._debugPasses.push({ pass: this._voxelTracingPass.getDebugPassPP(), enabled: this.voxelTracingDebugEnabled }, { pass: this._spatialBlurPass.getDebugPassPP(), enabled: this.spatialBlurPassDebugEnabled }, { pass: this._accumulationPass.getDebugPassPP(), enabled: this.accumulationPassDebugEnabled }, { pass: this._getGBufferDebugPass(), enabled: this.gbufferDebugEnabled });
        for (let i = 0; i < this._debugPasses.length; i++) {
            if (!this._debugPasses[i].pass) {
                continue;
            }
            this.addEffect(new PostProcessRenderEffect(this.scene.getEngine(), this._debugPasses[i].pass.name, () => {
                return this._debugPasses[i].pass;
            }, true));
        }
        const cameras = this.cameras.slice();
        this.scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this.name, this.cameras);
        this.scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(this.name, cameras);
        for (let i = 0; i < this._debugPasses.length; i++) {
            if (!this._debugPasses[i].pass) {
                continue;
            }
            if (this._debugPasses[i].enabled) {
                this._enableEffect(this._debugPasses[i].pass.name, this.cameras);
            }
            else {
                this._disableEffect(this._debugPasses[i].pass.name, this.cameras);
            }
        }
    }
    _disposeEffectPasses() {
        this.scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this.name, this.cameras);
        this._disposeDebugPasses();
        this._reset();
    }
    _disposeDebugPasses() {
        for (let i = 0; i < this._debugPasses.length; i++) {
            this._disableEffect(this._debugPasses[i].pass.name, this.cameras);
            this._debugPasses[i].pass.dispose();
        }
        this._debugPasses = [];
    }
    _updateDebugPasses() {
        let count = 0;
        if (this._gbufferDebugEnabled) {
            count++;
        }
        if (this.cdfDebugEnabled) {
            count++;
        }
        if (this.voxelTracingDebugEnabled) {
            count++;
        }
        if (this.spatialBlurPassDebugEnabled) {
            count++;
        }
        if (this.accumulationPassDebugEnabled) {
            count++;
        }
        const rows = Math.ceil(Math.sqrt(count));
        const cols = Math.ceil(count / rows);
        const width = 1.0 / cols;
        const height = 1.0 / rows;
        let x = 0;
        let y = 0;
        if (this.gbufferDebugEnabled) {
            this._gBufferDebugSizeParams.set(x, y, cols, rows);
            x -= width;
            if (x <= -1) {
                x = 0;
                y -= height;
            }
        }
        if (this.cdfDebugEnabled && this.scene.iblCdfGenerator) {
            this.scene.iblCdfGenerator.setDebugDisplayParams(x, y, cols, rows);
            x -= width;
            if (x <= -1) {
                x = 0;
                y -= height;
            }
        }
        if (this.voxelTracingDebugEnabled) {
            this._voxelTracingPass.setDebugDisplayParams(x, y, cols, rows);
            x -= width;
            if (x <= -1) {
                x = 0;
                y -= height;
            }
        }
        if (this.spatialBlurPassDebugEnabled) {
            this._spatialBlurPass.setDebugDisplayParams(x, y, cols, rows);
            x -= width;
            if (x <= -1) {
                x = 0;
                y -= height;
            }
        }
        if (this.accumulationPassDebugEnabled) {
            this._accumulationPass.setDebugDisplayParams(x, y, cols, rows);
        }
    }
    /**
     * Update the SS shadow max distance and thickness based on the voxel grid size and resolution.
     * The max distance should be just a little larger than the world size of a single voxel.
     */
    _updateSsShadowParams() {
        this._voxelTracingPass.sssMaxDist = (this._sssMaxDistScale * this.voxelGridSize) / (1 << this.resolutionExp);
        this._voxelTracingPass.sssThickness = this._sssThicknessScale * 0.005 * this.voxelGridSize;
    }
    /**
     * Apply the shadows to a material or array of materials. If no material is provided, all
     * materials in the scene will be added.
     * @param material Material that will be affected by the shadows. If not provided, all materials of the scene will be affected.
     */
    addShadowReceivingMaterial(material) {
        if (material) {
            if (Array.isArray(material)) {
                for (const m of material) {
                    this._addShadowSupportToMaterial(m);
                }
            }
            else {
                this._addShadowSupportToMaterial(material);
            }
        }
        else {
            for (const mat of this.scene.materials) {
                this._addShadowSupportToMaterial(mat);
            }
        }
    }
    /**
     * Remove a material from the list of materials that receive shadows. If no material
     * is provided, all materials in the scene will be removed.
     * @param material The material or array of materials that will no longer receive shadows
     */
    removeShadowReceivingMaterial(material) {
        if (Array.isArray(material)) {
            for (const m of material) {
                const matIndex = this._materialsWithRenderPlugin.indexOf(m);
                if (matIndex !== -1) {
                    this._materialsWithRenderPlugin.splice(matIndex, 1);
                    const plugin = m.pluginManager?.getPlugin(IBLShadowsPluginMaterial.Name);
                    plugin.isEnabled = false;
                }
            }
        }
        else {
            const matIndex = this._materialsWithRenderPlugin.indexOf(material);
            if (matIndex !== -1) {
                this._materialsWithRenderPlugin.splice(matIndex, 1);
                const plugin = material.pluginManager.getPlugin(IBLShadowsPluginMaterial.Name);
                plugin.isEnabled = false;
            }
        }
    }
    /**
     * Clear the list of materials that receive shadows. This will remove all materials from the list
     */
    clearShadowReceivingMaterials() {
        for (const mat of this._materialsWithRenderPlugin) {
            const plugin = mat.pluginManager?.getPlugin(IBLShadowsPluginMaterial.Name);
            if (plugin) {
                plugin.isEnabled = false;
            }
        }
        this._materialsWithRenderPlugin.length = 0;
    }
    _addShadowSupportToMaterial(material) {
        if (!(material instanceof PBRBaseMaterial) && !(material instanceof StandardMaterial) && !(material instanceof OpenPBRMaterial)) {
            return;
        }
        let plugin = material.pluginManager?.getPlugin(IBLShadowsPluginMaterial.Name);
        if (!plugin) {
            plugin = new IBLShadowsPluginMaterial(material);
        }
        if (this._materialsWithRenderPlugin.indexOf(material) !== -1) {
            return;
        }
        if (this._enabled) {
            plugin.iblShadowsTexture = this._getAccumulatedTexture().getInternalTexture();
            plugin.shadowOpacity = this.shadowOpacity;
        }
        plugin.isEnabled = this._enabled;
        plugin.isColored = this._coloredShadows;
        this._materialsWithRenderPlugin.push(material);
    }
    _setPluginParameters() {
        if (!this._enabled) {
            return;
        }
        for (const mat of this._materialsWithRenderPlugin) {
            if (mat.pluginManager) {
                const plugin = mat.pluginManager.getPlugin(IBLShadowsPluginMaterial.Name);
                plugin.iblShadowsTexture = this._getAccumulatedTexture().getInternalTexture();
                plugin.shadowOpacity = this.shadowOpacity;
                plugin.isColored = this._coloredShadows;
            }
        }
    }
    _updateBeforeRender() {
        this._updateDebugPasses();
    }
    _listenForCameraChanges() {
        // We want to listen for camera changes and change settings while the camera is moving.
        this.scene.activeCamera?.onViewMatrixChangedObservable.add(() => {
            this._accumulationPass.isMoving = true;
        });
    }
    /**
     * Checks if the IBL shadow pipeline is ready to render shadows
     * @returns true if the IBL shadow pipeline is ready to render the shadows
     */
    isReady() {
        return (this._noiseTexture.isReady() &&
            this._voxelRenderer.isReady() &&
            this.scene.iblCdfGenerator &&
            this.scene.iblCdfGenerator.isReady() &&
            (!this._voxelTracingPass || this._voxelTracingPass.isReady()) &&
            (!this._spatialBlurPass || this._spatialBlurPass.isReady()) &&
            (!this._accumulationPass || this._accumulationPass.isReady()));
    }
    /**
     * Get the class name
     * @returns "IBLShadowsRenderPipeline"
     */
    getClassName() {
        return "IBLShadowsRenderPipeline";
    }
    /**
     * Disposes the IBL shadow pipeline and associated resources
     */
    dispose() {
        const materials = this._materialsWithRenderPlugin.splice(0);
        for (const mat of materials) {
            this.removeShadowReceivingMaterial(mat);
        }
        this._disposeEffectPasses();
        this._noiseTexture.dispose();
        this._voxelRenderer.dispose();
        this._voxelTracingPass.dispose();
        this._spatialBlurPass.dispose();
        this._accumulationPass.dispose();
        this._dummyTexture2d.dispose();
        this._dummyTexture3d.dispose();
        this.onNewIblReadyObservable.clear();
        this.onShadowTextureReadyObservable.clear();
        this.onVoxelizationCompleteObservable.clear();
        super.dispose();
    }
}

export { IblShadowsRenderPipeline };
//# sourceMappingURL=iblShadowsRenderPipeline-CG-YhfMG.esm.js.map