import { ShadowImpl } from './shadow_impl.js';
import { decodeNormalizedFloatFromRGBA } from '../shaders/misc.js';
import { computeShadowMapDepth, computeReceiverPlaneDepthBias, filterShadowPCF } from '../shaders/shadow.js';
import { LIGHT_TYPE_POINT } from '../values.js';
import { ShaderHelper } from '../material/shader/helper.js';
import { computeShadowBiasCSM, computeShadowBias } from './shader.js';
import { getDevice } from '../app/api.js';

/** @internal */ class PCFOPT extends ShadowImpl {
    _kernelSize;
    _shadowSampler;
    constructor(kernelSize){
        super();
        this._kernelSize = kernelSize ?? 5;
        this._shadowSampler = null;
    }
    get kernelSize() {
        return this._kernelSize;
    }
    set kernelSize(val) {
        val = val !== 3 && val !== 5 && val !== 7 ? 5 : val;
        this._kernelSize = val;
    }
    getType() {
        return 'pcf-opt';
    }
    dispose() {
        this._shadowSampler = null;
    }
    resourceDirty() {
        return false;
    }
    getShadowMapBorder(_shadowMapParams) {
        return this._kernelSize;
    }
    getShadowMap(shadowMapParams) {
        return this.useNativeShadowMap(shadowMapParams) ? shadowMapParams.shadowMapFramebuffer.getDepthAttachment() : shadowMapParams.shadowMapFramebuffer.getColorAttachments()[0];
    }
    doUpdateResources(shadowMapParams) {
        shadowMapParams.shadowMap = this.getShadowMap(shadowMapParams);
        shadowMapParams.shadowMapSampler = shadowMapParams.shadowMap?.getDefaultSampler(this.useNativeShadowMap(shadowMapParams)) || null;
    }
    postRenderShadowMap() {}
    getDepthScale() {
        return 1;
    }
    setDepthScale(_val) {}
    getShaderHash() {
        return `${this._kernelSize}`;
    }
    getShadowMapColorFormat(shadowMapParams) {
        return this.useNativeShadowMap(shadowMapParams) ? null : 'rgba8unorm';
    }
    getShadowMapDepthFormat(_shadowMapParams) {
        return getDevice().type === 'webgl' ? 'd24s8' : 'd32f';
    }
    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;
        const that = this;
        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.$l.receiverPlaneDepthBias = computeReceiverPlaneDepthBias(this, this.shadowCoord);
            this.$if(this.inShadow, function() {
                this.$l.shadowBias = computeShadowBiasCSM(this, this.NdotL, this.split);
                this.shadowCoord.z = pb.sub(this.shadowCoord.z, this.shadowBias);
                this.shadow = filterShadowPCF(this, shadowMapParams.lightType, shadowMapParams.shadowMap.format, that._kernelSize, this.shadowCoord, this.receiverPlaneDepthBias, 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;
        const that = this;
        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);
                if (that.useNativeShadowMap(shadowMapParams)) {
                    this.$l.nearFar = ShaderHelper.getShadowCameraParams(this).xy;
                    this.$l.maxZ = pb.max(pb.max(pb.abs(this.dir.x), pb.abs(this.dir.y)), pb.abs(this.dir.z));
                    this.$l.distance = ShaderHelper.linearDepthToNonLinear(this, this.maxZ, this.nearFar);
                    this.$l.shadowBias = computeShadowBias(shadowMapParams.lightType, this, pb.div(this.maxZ, ShaderHelper.getLightPositionAndRangeForShadow(this).w), this.NdotL, true);
                    this.$return(that.sampleShadowMap(shadowMapParams, this, this.dir, this.distance, this.shadowBias));
                } else {
                    this.$l.distance = pb.div(pb.length(this.dir), ShaderHelper.getLightPositionAndRangeForShadow(this).w);
                    this.$l.shadowBias = computeShadowBias(shadowMapParams.lightType, this, this.distance, this.NdotL, true);
                    this.$return(that.sampleShadowMap(shadowMapParams, this, this.dir, this.distance, this.shadowBias));
                }
            } 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.$l.receiverPlaneDepthBias = computeReceiverPlaneDepthBias(this, this.shadowCoord);
                this.$if(this.inShadow, function() {
                    this.$l.shadowBias = computeShadowBias(shadowMapParams.lightType, this, this.shadowCoord.z, this.NdotL, false);
                    this.shadowCoord.z = pb.sub(this.shadowCoord.z, this.shadowBias);
                    this.shadow = filterShadowPCF(this, shadowMapParams.lightType, shadowMapParams.shadowMap.format, that._kernelSize, this.shadowCoord, this.receiverPlaneDepthBias);
                });
                this.$return(this.shadow);
            }
        });
        return pb.getGlobalScope()[funcNameComputeShadow](shadowVertex, NdotL);
    }
    useNativeShadowMap(_shadowMapParams) {
        return getDevice().type !== 'webgl';
    }
    /** @internal */ sampleShadowMap(shadowMapParams, scope, coords, z, bias) {
        const funcNameSampleShadowMap = `lib_sampleShadowMap`;
        const pb = scope.$builder;
        const that = this;
        pb.func(funcNameSampleShadowMap, [
            pb.vec3('coords'),
            pb.float('z'),
            pb.float('bias')
        ], function() {
            const floatDepthTexture = shadowMapParams.shadowMap.format !== 'rgba8unorm';
            if (that.useNativeShadowMap(shadowMapParams)) {
                this.$return(pb.clamp(pb.textureSampleCompareLevel(ShaderHelper.getShadowMap(this), this.coords, pb.sub(this.z, this.bias)), 0, 1));
            } else {
                this.$l.shadowTex = pb.textureSampleLevel(ShaderHelper.getShadowMap(this), this.coords, 0);
                if (!floatDepthTexture) {
                    this.shadowTex.x = decodeNormalizedFloatFromRGBA(this, this.shadowTex);
                }
                this.$l.distance = pb.sub(this.z, this.bias);
                this.$return(pb.step(this.distance, this.shadowTex.x));
            }
        });
        return pb.getGlobalScope()[funcNameSampleShadowMap](coords, z, bias);
    }
    /** @internal */ sampleShadowMapCSM(shadowMapParams, scope, coords, split, z, bias) {
        const funcNameSampleShadowMapCSM = 'lib_sampleShadowMapCSM';
        const pb = scope.$builder;
        const that = this;
        pb.func(funcNameSampleShadowMapCSM, [
            pb.vec4('coords'),
            pb.int('split'),
            pb.float('z'),
            pb.float('bias')
        ], function() {
            const floatDepthTexture = shadowMapParams.shadowMap.format !== 'rgba8unorm';
            this.$l.distance = pb.sub(this.z, this.bias);
            if (that.useNativeShadowMap(shadowMapParams)) {
                if (shadowMapParams.shadowMap.isTexture2DArray()) {
                    this.$return(pb.clamp(pb.textureArraySampleCompareLevel(ShaderHelper.getShadowMap(this), this.coords.xy, this.split, this.distance), 0, 1));
                } else {
                    this.$return(pb.clamp(pb.textureSampleCompareLevel(ShaderHelper.getShadowMap(this), this.coords.xy, this.distance), 0, 1));
                }
            } else {
                if (shadowMapParams.shadowMap.isTexture2DArray()) {
                    this.$l.shadowTex = pb.textureArraySampleLevel(ShaderHelper.getShadowMap(this), this.coords.xy, this.split, 0);
                } else {
                    this.$l.shadowTex = pb.textureSampleLevel(ShaderHelper.getShadowMap(this), this.coords.xy, 0);
                }
                if (!floatDepthTexture) {
                    this.shadowTex.x = decodeNormalizedFloatFromRGBA(this, this.shadowTex);
                }
                this.$return(pb.step(this.distance, this.shadowTex.x));
            }
        });
        return pb.getGlobalScope()[funcNameSampleShadowMapCSM](coords, split, z, bias);
    }
}

export { PCFOPT };
//# sourceMappingURL=pcf_opt.js.map
