import { ShadowImpl } from './shadow_impl.js';
import '@zephyr3d/base';
import '../render/primitive.js';
import { decodeNormalizedFloatFromRGBA, encodeNormalizedFloatToRGBA } from '../shaders/misc.js';
import { getDevice } from '../app/api.js';
import { GaussianBlurBlitter } from '../blitter/gaussianblur.js';
import { computeShadowMapDepth, filterShadowESM } from '../shaders/shadow.js';
import { LIGHT_TYPE_POINT } from '../values.js';
import { ShaderHelper } from '../material/shader/helper.js';

class BlurBlitter extends GaussianBlurBlitter {
    _packFloat = false;
    get packFloat() {
        return this._packFloat;
    }
    set packFloat(b) {
        if (this._packFloat !== !!b) {
            this._packFloat = !!b;
            this.invalidateHash();
        }
    }
    readTexel(scope, type, srcTex, srcUV, srcLayer, sampleType) {
        const pb = scope.$builder;
        const texel = super.readTexel(scope, type, srcTex, srcUV, srcLayer, sampleType);
        if (this.packFloat) {
            return pb.vec4(decodeNormalizedFloatFromRGBA(scope, texel), 0, 0, 1);
        } else {
            return texel;
        }
    }
    writeTexel(scope, type, srcUV, texel) {
        const outTexel = super.writeTexel(scope, type, srcUV, texel);
        if (this.packFloat) {
            return encodeNormalizedFloatToRGBA(scope, outTexel.r);
        } else {
            return outTexel;
        }
    }
    calcHash() {
        return `${super.calcHash()}-${Number(this.packFloat)}`;
    }
}
/** @internal */ class ESM extends ShadowImpl {
    /** @internal */ _depthScale;
    /** @internal */ _blur;
    /** @internal */ _kernelSize;
    /** @internal */ _blurSize;
    /** @internal */ _logSpace;
    /** @internal */ _blitterH;
    /** @internal */ _blitterV;
    /** @internal */ _mipmap;
    constructor(kernelSize, blurSize, depthScale){
        super();
        this._blur = true;
        this._depthScale = depthScale ?? 500;
        this._kernelSize = kernelSize ?? 5;
        this._blurSize = blurSize ?? 1;
        this._logSpace = true;
        this._mipmap = true;
        this._blitterH = new BlurBlitter('horizonal', this._kernelSize, 4, 1 / 1024);
        this._blitterV = new BlurBlitter('vertical', this._kernelSize, 4, 1 / 1024);
    }
    resourceDirty() {
        return this._resourceDirty;
    }
    get blur() {
        return this._blur;
    }
    set blur(val) {
        if (this._blur !== !!val) {
            this._blur = !!val;
            this._resourceDirty = true;
        }
    }
    get mipmap() {
        return this._mipmap;
    }
    set mipmap(b) {
        if (this._mipmap !== !!b) {
            this._mipmap = !!b;
            if (this._blur) {
                this._resourceDirty = true;
            }
        }
    }
    get kernelSize() {
        return this._kernelSize;
    }
    set kernelSize(val) {
        this._kernelSize = val;
    }
    get blurSize() {
        return this._blurSize;
    }
    set blurSize(val) {
        this._blurSize = val;
    }
    get logSpace() {
        return this._logSpace;
    }
    set logSpace(val) {
        this._logSpace = !!val;
    }
    getType() {
        return 'esm';
    }
    getShadowMapBorder(_shadowMapParams) {
        return this._blur ? Math.ceil((this._kernelSize + 1) / 2 * this._blurSize) : 0;
    }
    getShadowMap(shadowMapParams) {
        const implData = shadowMapParams.implData;
        return implData ? implData.blurFramebuffer2.getColorAttachments()[0] : shadowMapParams.shadowMapFramebuffer.getColorAttachments()[0];
    }
    /** @internal */ fetchTemporalFramebuffer(autoRelease, lightType, numCascades, width, height, colorFormat, depthFormat, mipmapping) {
        const device = getDevice();
        const useTextureArray = numCascades > 1 && device.type !== 'webgl';
        const colorAttachments = colorFormat ? useTextureArray ? [
            device.pool.fetchTemporalTexture2DArray(false, colorFormat, width, height, numCascades, mipmapping)
        ] : lightType === LIGHT_TYPE_POINT ? [
            device.pool.fetchTemporalTextureCube(false, colorFormat, width, mipmapping)
        ] : [
            device.pool.fetchTemporalTexture2D(false, colorFormat, width, height, mipmapping)
        ] : null;
        const depthAttachment = depthFormat ? useTextureArray ? device.pool.fetchTemporalTexture2DArray(false, depthFormat, width, height, numCascades, false) : device.type !== 'webgl' && lightType === LIGHT_TYPE_POINT ? device.pool.fetchTemporalTextureCube(false, depthFormat, width, false) : device.pool.fetchTemporalTexture2D(false, depthFormat, width, height, false) : null;
        const fb = device.pool.createTemporalFramebuffer(autoRelease, colorAttachments, depthAttachment);
        if (colorAttachments) {
            device.pool.releaseTexture(colorAttachments[0]);
        }
        if (depthAttachment) {
            device.pool.releaseTexture(depthAttachment);
        }
        return fb;
    }
    doUpdateResources(shadowMapParams) {
        const implData = {
            blurFramebuffer: null,
            blurFramebuffer2: null
        };
        shadowMapParams.implData = implData;
        const colorFormat = this.getShadowMapColorFormat(shadowMapParams);
        const shadowMapWidth = shadowMapParams.shadowMapFramebuffer.getColorAttachments()[0].width;
        const shadowMapHeight = shadowMapParams.shadowMapFramebuffer.getColorAttachments()[0].height;
        if (this._blur) {
            shadowMapParams.implData = {
                blurFramebuffer: this.fetchTemporalFramebuffer(true, shadowMapParams.lightType, shadowMapParams.numShadowCascades, shadowMapWidth, shadowMapHeight, colorFormat, null, false),
                blurFramebuffer2: this.fetchTemporalFramebuffer(true, shadowMapParams.lightType, shadowMapParams.numShadowCascades, shadowMapWidth, shadowMapHeight, colorFormat, null, true)
            };
        } else {
            shadowMapParams.implData = null;
        }
        shadowMapParams.shadowMap = this.getShadowMap(shadowMapParams);
        shadowMapParams.shadowMapSampler = shadowMapParams.shadowMap?.getDefaultSampler(false) ?? null;
    }
    postRenderShadowMap(shadowMapParams) {
        if (shadowMapParams.implData) {
            const implData = shadowMapParams.implData;
            const colorAttachment = shadowMapParams.shadowMapFramebuffer.getColorAttachments()[0];
            this._blitterH.blurSize = this._blurSize / colorAttachment.width;
            this._blitterH.kernelSize = this._kernelSize;
            this._blitterH.logSpace = this._logSpace;
            this._blitterH.packFloat = colorAttachment.format === 'rgba8unorm';
            this._blitterV.blurSize = this._blurSize / colorAttachment.height;
            this._blitterV.kernelSize = this._kernelSize;
            this._blitterV.logSpace = this._logSpace;
            this._blitterV.packFloat = colorAttachment.format === 'rgba8unorm';
            this._blitterH.blit(colorAttachment, implData.blurFramebuffer);
            this._blitterV.blit(implData.blurFramebuffer.getColorAttachments()[0], implData.blurFramebuffer2);
        }
    }
    getDepthScale() {
        return this._depthScale;
    }
    setDepthScale(val) {
        this._depthScale = val;
    }
    getShaderHash() {
        return '';
    }
    getShadowMapColorFormat(_shadowMapParams) {
        const device = getDevice();
        return device.getDeviceCaps().textureCaps.supportHalfFloatColorBuffer ? device.type === 'webgl' ? 'rgba16f' : 'r16f' : device.getDeviceCaps().textureCaps.supportFloatColorBuffer ? device.type === 'webgl' ? 'rgba32f' : 'r32f' : 'rgba8unorm';
    }
    getShadowMapDepthFormat(_shadowMapParams) {
        return 'd24s8';
    }
    computeShadowMapDepth(shadowMapParams, scope, worldPos) {
        return computeShadowMapDepth(scope, worldPos, shadowMapParams.shadowMap.format);
    }
    computeShadowCSM(shadowMapParams, scope, shadowVertex, NdotL, split) {
        const funcNameComputeShadowCSM = 'lib_computeShadowCSM';
        const pb = scope.$builder;
        pb.func(funcNameComputeShadowCSM, [
            pb.vec4('shadowVertex'),
            pb.float('NdotL'),
            pb.int('split')
        ], function() {
            this.$l.shadowCoord = pb.div(this.shadowVertex, this.shadowVertex.w);
            this.$l.shadowCoord = pb.add(pb.mul(this.shadowCoord, 0.5), 0.5);
            this.$l.inShadow = pb.all(pb.bvec2(pb.all(pb.bvec4(pb.greaterThanEqual(this.shadowCoord.x, 0), pb.lessThanEqual(this.shadowCoord.x, 1), pb.greaterThanEqual(this.shadowCoord.y, 0), pb.lessThanEqual(this.shadowCoord.y, 1))), pb.lessThanEqual(this.shadowCoord.z, 1)));
            this.$l.shadow = pb.float(1);
            this.$if(this.inShadow, function() {
                this.shadow = filterShadowESM(this, shadowMapParams.lightType, shadowMapParams.shadowMap.format, this.shadowCoord, this.split);
            });
            this.$return(this.shadow);
        });
        return pb.getGlobalScope()[funcNameComputeShadowCSM](shadowVertex, NdotL, split);
    }
    computeShadow(shadowMapParams, scope, shadowVertex, NdotL) {
        const funcNameComputeShadow = 'lib_computeShadow';
        const pb = scope.$builder;
        pb.func(funcNameComputeShadow, [
            pb.vec4('shadowVertex'),
            pb.float('NdotL')
        ], function() {
            if (shadowMapParams.lightType === LIGHT_TYPE_POINT) {
                this.$l.dir = pb.sub(this.shadowVertex.xyz, ShaderHelper.getLightPositionAndRangeForShadow(this).xyz);
                this.$return(filterShadowESM(this, LIGHT_TYPE_POINT, shadowMapParams.shadowMap.format, this.dir));
            } else {
                this.$l.shadowCoord = pb.div(this.shadowVertex, this.shadowVertex.w);
                this.$l.shadowCoord = pb.add(pb.mul(this.shadowCoord, 0.5), 0.5);
                this.$l.inShadow = pb.all(pb.bvec2(pb.all(pb.bvec4(pb.greaterThanEqual(this.shadowCoord.x, 0), pb.lessThanEqual(this.shadowCoord.x, 1), pb.greaterThanEqual(this.shadowCoord.y, 0), pb.lessThanEqual(this.shadowCoord.y, 1))), pb.lessThanEqual(this.shadowCoord.z, 1)));
                this.$l.shadow = pb.float(1);
                this.$if(this.inShadow, function() {
                    this.shadow = filterShadowESM(this, shadowMapParams.lightType, shadowMapParams.shadowMap.format, this.shadowCoord);
                });
                this.$return(this.shadow);
            }
        });
        return pb.getGlobalScope()[funcNameComputeShadow](shadowVertex, NdotL);
    }
    useNativeShadowMap(_shadowMapParams) {
        return false;
    }
}

export { ESM };
//# sourceMappingURL=esm.js.map
