import { S as SceneComponentConstants, a as Scene, E as EngineStore, T as Texture, _ as _RetryWithInterval, aJ as Vector4, ay as Observable, L as Logger, C as Constants, aK as PostProcess, as as Vector3, aL as _WarnImport } from './index-BvJBnIUK.esm.js';
import { P as ProceduralTexture } from './proceduralTexture-Caulb72M.esm.js';
import { R as RawTexture } from './rawTexture-CcCH4bTg.esm.js';
import { CubeTexture } from './cubeTexture-ClCmKJYa.esm.js';

/** This file must only contain pure code and pure imports */
/**
 * Defines the IBL CDF Generator scene component responsible for generating CDF maps for a given IBL.
 */
class IblCdfGeneratorSceneComponent {
    /**
     * Creates a new instance of the component for the given scene
     * @param scene Defines the scene to register the component in
     */
    constructor(scene) {
        /**
         * The component name helpful to identify the component in the list of scene components.
         */
        this.name = SceneComponentConstants.NAME_IBLCDFGENERATOR;
        this._newIblObserver = null;
        this.scene = scene;
    }
    /**
     * Registers the component in a given scene
     */
    register() {
        this._updateIblSource();
        this._newIblObserver = this.scene.onEnvironmentTextureChangedObservable.add(this._updateIblSource.bind(this));
        this.scene.addIsReadyCheck(this);
    }
    /**
     * Rebuilds the elements related to this component in case of
     * context lost for instance.
     */
    rebuild() {
        // Nothing to do for this component
    }
    /**
     * Disposes the component and the associated resources
     */
    dispose() {
        this.scene.onEnvironmentTextureChangedObservable.remove(this._newIblObserver);
        this.scene.removeIsReadyCheck(this);
    }
    /**
     * @returns true once the CDF generator's procedural textures and effects are ready.
     * Used by `Scene.isReady` so that `executeWhenReady` waits for the CDF maps to be
     * generated before declaring the scene ready to render.
     */
    isReady() {
        const generator = this.scene._iblCdfGenerator;
        // If there's no generator, or no environment texture for it to consume,
        // there's nothing to wait for - report ready so Scene.isReady() doesn't stall.
        if (!generator || !this.scene.environmentTexture) {
            return true;
        }
        return !!generator.isReady();
    }
    _updateIblSource() {
        if (this.scene.iblCdfGenerator && this.scene.environmentTexture) {
            this.scene.iblCdfGenerator.iblSource = this.scene.environmentTexture;
        }
    }
}
let _Registered = false;
/**
 * Register side effects for iblCdfGeneratorSceneComponent.
 * Safe to call multiple times; only the first call has an effect.
 * @param iblCdfGeneratorClass The IblCdfGenerator class to register the component for
 */
function RegisterIblCdfGeneratorSceneComponent(iblCdfGeneratorClass) {
    if (_Registered) {
        return;
    }
    _Registered = true;
    Object.defineProperty(Scene.prototype, "iblCdfGenerator", {
        get: function () {
            return this._iblCdfGenerator;
        },
        set: function (value) {
            if (value) {
                this._iblCdfGenerator = value;
            }
        },
        enumerable: true,
        configurable: true,
    });
    Scene.prototype.enableIblCdfGenerator = function () {
        if (this._iblCdfGenerator) {
            return this._iblCdfGenerator;
        }
        this._iblCdfGenerator = new iblCdfGeneratorClass(this);
        if (!this._iblCdfGenerator.isSupported) {
            this._iblCdfGenerator = null;
            return null;
        }
        if (this.environmentTexture) {
            this._iblCdfGenerator.iblSource = this.environmentTexture;
        }
        return this._iblCdfGenerator;
    };
    Scene.prototype.disableIblCdfGenerator = function () {
        if (!this._iblCdfGenerator) {
            return;
        }
        this._iblCdfGenerator.dispose();
        this._iblCdfGenerator = null;
    };
    iblCdfGeneratorClass._SceneComponentInitialization = (scene) => {
        // Register the CDF generator component to the scene.
        let component = scene._getComponent(SceneComponentConstants.NAME_IBLCDFGENERATOR);
        if (!component) {
            component = new IblCdfGeneratorSceneComponent(scene);
            scene._addComponent(component);
        }
    };
}

/**
 * Build cdf maps to be used for IBL importance sampling.
 */
class IblCdfGenerator {
    /**
     * Returns whether the CDF renderer is supported by the current engine
     */
    get isSupported() {
        const engine = EngineStore.LastCreatedEngine;
        if (!engine) {
            return false;
        }
        return engine.getCaps().texelFetch;
    }
    /**
     * Gets the IBL source texture being used by the CDF renderer
     */
    get iblSource() {
        return this._iblSource;
    }
    /**
     * Sets the IBL source texture to be used by the CDF renderer.
     * This will trigger recreation of the CDF assets.
     */
    set iblSource(source) {
        if (this._iblSource === source) {
            return;
        }
        this._clearIblSourceReadinessObservers();
        this._disposeTextures();
        this._iblSource = source;
        if (!source) {
            return;
        }
        const recreateFromObservedSourceIfReady = () => {
            if (this._iblSource !== source || !this._isIblSourceReady(source)) {
                return;
            }
            this._clearIblSourceReadinessObservers();
            this._recreateAssetsFromNewIbl();
        };
        if (this._isIblSourceReady(source)) {
            this._recreateAssetsFromNewIbl();
            return;
        }
        if (source instanceof Texture) {
            const observer = source.onLoadObservable.addOnce(recreateFromObservedSourceIfReady);
            if (observer) {
                this._iblSourceLoadUnsubscribe = () => source.onLoadObservable.remove(observer);
            }
        }
        else if (source instanceof CubeTexture) {
            const observer = source.onLoadObservable.addOnce(recreateFromObservedSourceIfReady);
            if (observer) {
                this._iblSourceLoadUnsubscribe = () => source.onLoadObservable.remove(observer);
            }
        }
        this._iblSourceReadyRetryObserver = _RetryWithInterval(() => this._iblSource !== source || this._isIblSourceReady(source), recreateFromObservedSourceIfReady, undefined, 16, 30000, false);
    }
    _isIblSourceReady(source) {
        if (!source.isReadyOrNotBlocking()) {
            return false;
        }
        if (!source.isCube) {
            return true;
        }
        const internalTexture = source.getInternalTexture();
        return !!internalTexture && internalTexture.isReady;
    }
    _clearIblSourceReadinessObservers() {
        this._iblSourceReadyRetryObserver?.();
        this._iblSourceReadyRetryObserver = null;
        this._iblSourceLoadUnsubscribe?.();
        this._iblSourceLoadUnsubscribe = null;
    }
    _recreateAssetsFromNewIbl() {
        if (this._debugPass) {
            this._debugPass.dispose();
        }
        this._createTextures();
        if (this._debugPass) {
            // Recreate the debug pass because of the new textures
            this._createDebugPass();
        }
    }
    /**
     * Return the cumulative distribution function (CDF) texture
     * @returns Return the cumulative distribution function (CDF) texture
     */
    getIcdfTexture() {
        return this._icdfPT ? this._icdfPT : this._dummyTexture;
    }
    /**
     * 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);
    }
    /**
     * The name of the debug pass post process
     */
    get debugPassName() {
        return this._debugPassName;
    }
    /**
     * Gets the debug pass post process
     * @returns The post process
     */
    getDebugPassPP() {
        if (!this._debugPass) {
            this._createDebugPass();
        }
        return this._debugPass;
    }
    /**
     * Instanciates the CDF renderer
     * @param sceneOrEngine Scene to attach to
     * @returns The CDF renderer
     */
    constructor(sceneOrEngine) {
        this._iblSourceLoadUnsubscribe = null;
        this._iblSourceReadyRetryObserver = null;
        this._cachedDominantDirection = null;
        /** Enable the debug view for this pass */
        this.debugEnabled = false;
        this._debugSizeParams = new Vector4(0.0, 0.0, 1.0, 1.0);
        this._debugPassName = "CDF Debug";
        /**
         * Observable that triggers when the CDF renderer is ready
         */
        this.onGeneratedObservable = new Observable();
        /**
         * Observable that triggers when CDF texture references change.
         * It is raised after disposing textures (so fallback ICDF can be used)
         * and after creating new textures (so consumers can rebind immediately).
         */
        this.onTextureChangedObservable = new Observable();
        if (sceneOrEngine) {
            if (IblCdfGenerator._IsScene(sceneOrEngine)) {
                this._scene = sceneOrEngine;
            }
            else {
                this._engine = sceneOrEngine;
            }
        }
        else {
            this._scene = EngineStore.LastCreatedScene;
        }
        if (this._scene) {
            this._engine = this._scene.getEngine();
        }
        if (!this.isSupported) {
            Logger.Warn("CDF renderer is not supported by the current engine.");
            return;
        }
        const blackPixels = new Uint16Array([0, 0, 0, 255]);
        this._dummyTexture = new RawTexture(blackPixels, 1, 1, Constants.TEXTUREFORMAT_RGBA, sceneOrEngine, false, false, undefined, Constants.TEXTURETYPE_HALF_FLOAT);
        if (this._scene) {
            RegisterIblCdfGeneratorSceneComponent(IblCdfGenerator);
            IblCdfGenerator._SceneComponentInitialization(this._scene);
        }
    }
    _createTextures() {
        const size = this._iblSource ? { width: this._iblSource.getSize().width, height: this._iblSource.getSize().height } : { width: 1, height: 1 };
        if (!this._iblSource) {
            this._iblSource = RawTexture.CreateRTexture(new Uint8Array([255]), 1, 1, this._engine, false, false, Constants.TEXTURE_NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
            this._iblSource.name = "Placeholder IBL Source";
        }
        if (this._iblSource.isCube) {
            size.width *= 4;
            size.height *= 2;
            // Force the resolution to be a power of 2 because we rely on the
            // auto-mipmap generation for the scaled luminance texture to produce
            // a 1x1 mip that represents the true average pixel intensity of the IBL.
            size.width = 1 << Math.floor(Math.log2(size.width));
            size.height = 1 << Math.floor(Math.log2(size.height));
        }
        const isWebGPU = this._engine.isWebGPU;
        // Create CDF maps (Cumulative Distribution Function) to assist in importance sampling
        const cdfOptions = {
            generateDepthBuffer: false,
            generateMipMaps: false,
            format: Constants.TEXTUREFORMAT_R,
            type: Constants.TEXTURETYPE_FLOAT,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            gammaSpace: false,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./iblCdfx.fragment-DGA8H9G3.esm.js'), import('./iblCdfy.fragment-CmWd0MAL.esm.js'), import('./iblScaledLuminance.fragment-EABp5VBQ.esm.js')]);
                }
                else {
                    await Promise.all([import('./iblCdfx.fragment-BsgQwCBI.esm.js'), import('./iblCdfy.fragment-BzhtZFyN.esm.js'), import('./iblScaledLuminance.fragment-IzUY0tVq.esm.js')]);
                }
            },
        };
        const icdfOptions = {
            generateDepthBuffer: false,
            generateMipMaps: false,
            format: Constants.TEXTUREFORMAT_RGBA,
            type: Constants.TEXTURETYPE_HALF_FLOAT,
            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE,
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            gammaSpace: false,
            extraInitializationsAsync: async () => {
                if (isWebGPU) {
                    await Promise.all([import('./iblIcdf.fragment-DbwFaHFP.esm.js'), import('./iblDominantDirection.fragment-CfaNeW3L.esm.js')]);
                }
                else {
                    await Promise.all([import('./iblIcdf.fragment-CMz_EE6F.esm.js'), import('./iblDominantDirection.fragment-qlLzp-EI.esm.js')]);
                }
            },
        };
        this._cdfyPT = new ProceduralTexture("cdfyTexture", { width: size.width, height: size.height + 1 }, "iblCdfy", this._scene, cdfOptions, false, false);
        this._cdfyPT.autoClear = false;
        this._cdfyPT.setTexture("iblSource", this._iblSource);
        this._cdfyPT.setInt("iblHeight", size.height);
        this._cdfyPT.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        this._cdfyPT.refreshRate = 0;
        if (this._iblSource.isCube) {
            this._cdfyPT.defines = "#define IBL_USE_CUBE_MAP\n";
        }
        this._cdfxPT = new ProceduralTexture("cdfxTexture", { width: size.width + 1, height: 1 }, "iblCdfx", this._scene, cdfOptions, false, false);
        this._cdfxPT.autoClear = false;
        this._cdfxPT.setTexture("cdfy", this._cdfyPT);
        this._cdfxPT.refreshRate = 0;
        this._cdfxPT.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        this._scaledLuminancePT = new ProceduralTexture("iblScaledLuminance", { width: size.width, height: size.height }, "iblScaledLuminance", this._scene, { ...cdfOptions, samplingMode: Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, generateMipMaps: true }, true, false);
        this._scaledLuminancePT.autoClear = false;
        this._scaledLuminancePT.setTexture("iblSource", this._iblSource);
        this._scaledLuminancePT.setInt("iblHeight", size.height);
        this._scaledLuminancePT.setInt("iblWidth", size.width);
        this._scaledLuminancePT.refreshRate = 0;
        if (this._iblSource.isCube) {
            this._scaledLuminancePT.defines = "#define IBL_USE_CUBE_MAP\n";
        }
        this._icdfPT = new ProceduralTexture("icdfTexture", { width: size.width, height: size.height }, "iblIcdf", this._scene, icdfOptions, false, false);
        this._icdfPT.autoClear = false;
        this._icdfPT.setTexture("cdfy", this._cdfyPT);
        this._icdfPT.setTexture("cdfx", this._cdfxPT);
        this._icdfPT.setTexture("iblSource", this._iblSource);
        this._icdfPT.setTexture("scaledLuminanceSampler", this._scaledLuminancePT);
        this._icdfPT.refreshRate = 0;
        this._icdfPT.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        this._icdfPT.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
        if (this._iblSource.isCube) {
            this._icdfPT.defines = "#define IBL_USE_CUBE_MAP\n";
        }
        // Once the textures are generated, notify that they are ready to use.
        this._icdfPT.onGeneratedObservable.addOnce(() => {
            this.onGeneratedObservable.notifyObservers();
        });
        this._dominantDirectionPT = new ProceduralTexture("iblDominantDirection", { width: 1, height: 1 }, "iblDominantDirection", this._scene, icdfOptions, false, false);
        this._dominantDirectionPT.autoClear = false;
        this._dominantDirectionPT.setTexture("icdfSampler", this._icdfPT);
        this._dominantDirectionPT.refreshRate = 0;
        this._dominantDirectionPT.defines = "#define NUM_SAMPLES 32u\n";
        this.onTextureChangedObservable.notifyObservers();
    }
    _disposeTextures() {
        this._cdfyPT?.dispose();
        this._cdfxPT?.dispose();
        this._icdfPT?.dispose();
        this._scaledLuminancePT?.dispose();
        this._dominantDirectionPT?.dispose();
        this._icdfPT = null;
        this.onTextureChangedObservable.notifyObservers();
    }
    _createDebugPass() {
        if (this._debugPass) {
            this._debugPass.dispose();
        }
        const isWebGPU = this._engine.isWebGPU;
        const debugOptions = {
            width: this._engine.getRenderWidth(),
            height: this._engine.getRenderHeight(),
            samplingMode: Texture.BILINEAR_SAMPLINGMODE,
            engine: this._engine,
            textureType: Constants.TEXTURETYPE_UNSIGNED_BYTE,
            uniforms: ["sizeParams"],
            samplers: ["cdfy", "icdf", "cdfx", "iblSource"],
            defines: this._iblSource?.isCube ? "#define IBL_USE_CUBE_MAP\n" : "",
            shaderLanguage: isWebGPU ? 1 /* ShaderLanguage.WGSL */ : 0 /* ShaderLanguage.GLSL */,
            extraInitializations: (useWebGPU, list) => {
                if (useWebGPU) {
                    list.push(import('./iblCdfDebug.fragment-CM3E2e1x.esm.js'));
                }
                else {
                    list.push(import('./iblCdfDebug.fragment-BS5iDDxG.esm.js'));
                }
            },
        };
        this._debugPass = new PostProcess(this._debugPassName, "iblCdfDebug", debugOptions);
        const debugEffect = this._debugPass.getEffect();
        if (debugEffect) {
            debugEffect.defines = this._iblSource?.isCube ? "#define IBL_USE_CUBE_MAP\n" : "";
        }
        if (this._iblSource?.isCube) {
            this._debugPass.updateEffect("#define IBL_USE_CUBE_MAP\n");
        }
        this._debugPass.onApplyObservable.add((effect) => {
            effect.setTexture("cdfy", this._cdfyPT);
            effect.setTexture("icdf", this._icdfPT);
            effect.setTexture("cdfx", this._cdfxPT);
            effect.setTexture("iblSource", this._iblSource);
            effect.setFloat4("sizeParams", this._debugSizeParams.x, this._debugSizeParams.y, this._debugSizeParams.z, this._debugSizeParams.w);
        });
    }
    /**
     * Checks if the CDF renderer is ready
     * @returns true if the CDF renderer is ready
     */
    isReady() {
        return (this._iblSource &&
            this._iblSource.name !== "Placeholder IBL Source" &&
            this._iblSource.isReady() &&
            this._cdfyPT &&
            this._cdfyPT.isReady() &&
            this._icdfPT &&
            this._icdfPT.isReady() &&
            this._cdfxPT &&
            this._cdfxPT.isReady() &&
            this._scaledLuminancePT &&
            this._scaledLuminancePT.isReady());
    }
    /**
     * Explicitly trigger generation of CDF maps when they are ready to render.
     * @returns Promise that resolves when the CDF maps are rendered.
     */
    // eslint-disable-next-line @typescript-eslint/promise-function-async
    renderWhenReady() {
        this._cachedDominantDirection = null;
        // Even if a IBL source must be set before calling this function, _icdfPT may not yet be created because the creation may be asynchronous (see @set iblSource).
        const icdfPTPromise = new Promise((resolve, reject) => {
            _RetryWithInterval(() => !!this._icdfPT, () => resolve(void 0), () => reject(new Error("Waiting for _icdfPT creation failed")));
        });
        // eslint-disable-next-line github/no-then, @typescript-eslint/promise-function-async
        return icdfPTPromise.then(() => {
            const icdfTexture = this._icdfPT;
            const promises = [];
            const renderTargets = [this._cdfyPT, this._cdfxPT, this._scaledLuminancePT, icdfTexture];
            for (const target of renderTargets) {
                promises.push(new Promise((resolve) => {
                    if (target.isReady()) {
                        resolve();
                    }
                    else {
                        target.getEffect().executeWhenCompiled(() => {
                            resolve();
                        });
                    }
                }));
            }
            // eslint-disable-next-line github/no-then
            return Promise.all(promises).then(() => {
                for (const target of renderTargets) {
                    target.render();
                }
            });
        });
    }
    /**
     * Finds the average direction of the highest intensity areas of the IBL source
     * @returns Async promise that resolves to the dominant direction of the IBL source
     */
    // eslint-disable-next-line @typescript-eslint/promise-function-async
    findDominantDirection() {
        if (this._cachedDominantDirection) {
            return Promise.resolve(this._cachedDominantDirection);
        }
        return new Promise((resolve) => {
            this._dominantDirectionPT.onGeneratedObservable.addOnce(() => {
                const data = new Float32Array(4);
                // eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
                this._dominantDirectionPT.readPixels(0, 0, data, true).then(() => {
                    const dominantDirection = new Vector3(data[0], data[1], data[2]);
                    this._cachedDominantDirection = dominantDirection;
                    resolve(dominantDirection);
                });
            });
            if (this.isReady()) {
                if (this._dominantDirectionPT.isReady()) {
                    this._dominantDirectionPT.render();
                }
                else {
                    this._dominantDirectionPT.getEffect().executeWhenCompiled(() => {
                        this._dominantDirectionPT.render();
                    });
                }
            }
            else {
                this.onGeneratedObservable.addOnce(() => {
                    if (this._dominantDirectionPT.isReady()) {
                        this._dominantDirectionPT.render();
                    }
                    else {
                        this._dominantDirectionPT.getEffect().executeWhenCompiled(() => {
                            this._dominantDirectionPT.render();
                        });
                    }
                });
            }
        });
    }
    /**
     * Disposes the CDF renderer and associated resources
     */
    dispose() {
        this._clearIblSourceReadinessObservers();
        this._disposeTextures();
        this._dummyTexture.dispose();
        if (this._debugPass) {
            this._debugPass.dispose();
        }
        this.onGeneratedObservable.clear();
    }
    static _IsScene(sceneOrEngine) {
        return sceneOrEngine.getClassName() === "Scene";
    }
}
/**
 * @internal
 */
IblCdfGenerator._SceneComponentInitialization = (_) => {
    throw _WarnImport("IblCdfGeneratorSceneComponentSceneComponent");
};

var iblCdfGenerator = /*#__PURE__*/Object.freeze({
    __proto__: null,
    IblCdfGenerator: IblCdfGenerator
});

export { IblCdfGenerator as I, RegisterIblCdfGeneratorSceneComponent as R, IblCdfGeneratorSceneComponent as a, iblCdfGenerator as i };
//# sourceMappingURL=iblCdfGenerator-iH3FQI72.esm.js.map
