import { Disposable, DRef, Vector3, Vector4 } from '@zephyr3d/base';
import { fetchSampler } from '../utility/misc.js';
import { getDevice } from '../app/api.js';

/**
 * Base class for any kind of environment light
 * @public
 */ class EnvironmentLighting extends Disposable {
}
/**
 * IBL with SH based environment lighting
 * @public
 */ class EnvShIBL extends EnvironmentLighting {
    /** @internal */ static UNIFORM_NAME_IBL_RADIANCE_MAP = 'zIBLRadianceMap';
    /** @internal */ static UNIFORM_NAME_IBL_RADIANCE_MAP_MAX_LOD = 'zIBLRadianceMapMaxLOD';
    /** @internal */ static UNIFORM_NAME_IBL_IRRADIANCE_SH = 'zIBLIrradianceSH';
    /** @internal */ static UNIFORM_NAME_IBL_IRRADIANCE_WINDOW = 'zIBLIrradianceWindow';
    /** @internal */ _radianceMap;
    /** @internal */ _irradianceSH;
    /** @internal */ _irradianceSHFB;
    /** @internal */ _irraidanceWindow;
    /**
   * Creates an instance of EnvIBL
   * @param radianceMap - The radiance map
   * @param irradianceSH - The irradiance SH
   */ constructor(radianceMap, irradianceSH, irradianceSHFB){
        super();
        this._radianceMap = new DRef(radianceMap || null);
        this._irradianceSH = new DRef(irradianceSH || null);
        this._irradianceSHFB = new DRef(irradianceSHFB || null);
        this._irraidanceWindow = new Vector3();
    }
    /**
   * {@inheritDoc EnvironmentLighting.getType}
   * @override
   */ getType() {
        return 'ibl';
    }
    /** The radiance map */ get radianceMap() {
        return this._radianceMap.get();
    }
    set radianceMap(tex) {
        this._radianceMap.set(tex);
    }
    /** The irradiance sh coeffecients */ get irradianceSH() {
        return this._irradianceSH.get();
    }
    set irradianceSH(value) {
        this._irradianceSH.set(value);
    }
    /** The irradiance sh coeffecients */ get irradianceSHFB() {
        return this._irradianceSHFB.get();
    }
    set irradianceSHFB(value) {
        this._irradianceSHFB.set(value);
    }
    /** The irradiance sh window */ get irradianceWindow() {
        return this._irraidanceWindow;
    }
    set irradianceWindow(val) {
        this._irraidanceWindow = val;
    }
    /**
   * {@inheritDoc EnvironmentLighting.initShaderBindings}
   * @override
   */ initShaderBindings(pb) {
        if (pb.shaderKind === 'fragment') {
            if (this.radianceMap) {
                pb.getGlobalScope()[EnvShIBL.UNIFORM_NAME_IBL_RADIANCE_MAP] = pb.texCube().uniform(0);
                pb.getGlobalScope()[EnvShIBL.UNIFORM_NAME_IBL_RADIANCE_MAP_MAX_LOD] = pb.float().uniform(0);
            }
            if (this.irradianceSHFB) {
                const tex = pb.tex2D();
                const formatInfo = getDevice().getDeviceCaps().textureCaps.getTextureFormatInfo(this.irradianceSHFB.getColorAttachments()[0].format);
                if (formatInfo && !formatInfo.filterable) {
                    tex.sampleType('unfilterable-float');
                }
                pb.getGlobalScope()[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH] = tex.uniform(0);
                pb.getGlobalScope()[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_WINDOW] = pb.vec3().uniform(0);
            } else if (this.irradianceSH) {
                pb.getGlobalScope()[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH] = pb.vec4[9]().uniformBuffer(0);
                pb.getGlobalScope()[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_WINDOW] = pb.vec3().uniform(0);
            }
        }
    }
    /**
   * {@inheritDoc EnvironmentLighting.updateBindGroup}
   * @override
   */ updateBindGroup(bg) {
        if (this.radianceMap) {
            bg.setValue(EnvShIBL.UNIFORM_NAME_IBL_RADIANCE_MAP_MAX_LOD, this.radianceMap.mipLevelCount - 1);
            bg.setTexture(EnvShIBL.UNIFORM_NAME_IBL_RADIANCE_MAP, this.radianceMap);
        }
        if (this.irradianceSHFB) {
            bg.setTexture(EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH, this.irradianceSHFB.getColorAttachments()[0], fetchSampler('clamp_nearest_nomip'));
            bg.setValue(EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_WINDOW, this.irradianceWindow);
        } else if (this.irradianceSH) {
            bg.setBuffer(EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH, this.irradianceSH);
            bg.setValue(EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_WINDOW, this.irradianceWindow);
        }
    }
    /**
   * {@inheritDoc EnvironmentLighting.getRadiance}
   * @override
   */ getRadiance(scope, refl, roughness) {
        const pb = scope.$builder;
        return getDevice().getDeviceCaps().shaderCaps.supportShaderTextureLod ? pb.textureSampleLevel(scope[EnvShIBL.UNIFORM_NAME_IBL_RADIANCE_MAP], refl, pb.mul(roughness, scope[EnvShIBL.UNIFORM_NAME_IBL_RADIANCE_MAP_MAX_LOD])).rgb : pb.textureSample(scope[EnvShIBL.UNIFORM_NAME_IBL_RADIANCE_MAP], refl).rgb;
    }
    /**
   * {@inheritDoc EnvironmentLighting.getIrradiance}
   * @override
   */ getIrradiance(scope, normal) {
        const pb = scope.$builder;
        const that = this;
        pb.func('Z_sh_Y0', [
            pb.vec3('v')
        ], function() {
            this.$return(0.2820947917);
        });
        pb.func('Z_sh_Y1', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(this.v.y, -0.4886025119));
        });
        pb.func('Z_sh_Y2', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(this.v.z, 0.4886025119));
        });
        pb.func('Z_sh_Y3', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(this.v.x, -0.4886025119));
        });
        pb.func('Z_sh_Y4', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(this.v.x, this.v.y, 1.0925484306));
        });
        pb.func('Z_sh_Y5', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(this.v.y, this.v.z, -1.0925484306));
        });
        pb.func('Z_sh_Y6', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(pb.sub(pb.mul(this.v.z, this.v.z, 3), 1), 0.3153915652));
        });
        pb.func('Z_sh_Y7', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(this.v.x, this.v.z, -1.0925484306));
        });
        pb.func('Z_sh_Y8', [
            pb.vec3('v')
        ], function() {
            this.$return(pb.mul(pb.sub(pb.mul(this.v.x, this.v.x), pb.mul(this.v.y, this.v.y)), 0.5462742153));
        });
        pb.func('Z_sh_eval', [
            pb.vec3('v')
        ], function() {
            this.$l.window = this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_WINDOW];
            if (that.irradianceSHFB) {
                this.$l.c = pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(0.5 / 3, 0.5 / 3), 0).rgb, this.Z_sh_Y0(this.v), this.window.x);
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(1.5 / 3, 0.5 / 3), 0).rgb, this.Z_sh_Y1(this.v), this.window.y));
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(2.5 / 3, 0.5 / 3), 0).rgb, this.Z_sh_Y2(this.v), this.window.y));
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(0.5 / 3, 1.5 / 3), 0).rgb, this.Z_sh_Y3(this.v), this.window.y));
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(1.5 / 3, 1.5 / 3), 0).rgb, this.Z_sh_Y4(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(2.5 / 3, 1.5 / 3), 0).rgb, this.Z_sh_Y5(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(0.5 / 3, 2.5 / 3), 0).rgb, this.Z_sh_Y6(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(1.5 / 3, 2.5 / 3), 0).rgb, this.Z_sh_Y7(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(pb.textureSampleLevel(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH], pb.vec2(2.5 / 3, 2.5 / 3), 0).rgb, this.Z_sh_Y8(this.v), this.window.z));
            } else {
                this.$l.c = pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][0].xyz, this.Z_sh_Y0(this.v), this.window.x);
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][1].xyz, this.Z_sh_Y1(this.v), this.window.y));
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][2].xyz, this.Z_sh_Y2(this.v), this.window.y));
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][3].xyz, this.Z_sh_Y3(this.v), this.window.y));
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][4].xyz, this.Z_sh_Y4(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][5].xyz, this.Z_sh_Y5(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][6].xyz, this.Z_sh_Y6(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][7].xyz, this.Z_sh_Y7(this.v), this.window.z));
                this.c = pb.add(this.c, pb.mul(this[EnvShIBL.UNIFORM_NAME_IBL_IRRADIANCE_SH][8].xyz, this.Z_sh_Y8(this.v), this.window.z));
            }
            this.$return(this.c);
        });
        return pb.getGlobalScope().Z_sh_eval(normal);
    }
    /**
   * {@inheritDoc EnvironmentLighting.hasRadiance}
   * @override
   */ hasRadiance() {
        return !!this._radianceMap.get();
    }
    /**
   * {@inheritDoc EnvironmentLighting.hasIrradiance}
   * @override
   */ hasIrradiance() {
        return !!this._irradianceSH.get() || !!this._irradianceSHFB.get();
    }
    /**
   * Disposes the object and releases all GPU resources
   * @override
   */ onDispose() {
        super.onDispose();
        this._radianceMap.dispose();
        this._irradianceSH.dispose();
        this._irradianceSHFB.dispose();
    }
}
/**
 * Constant ambient light
 * @public
 */ class EnvConstantAmbient extends EnvironmentLighting {
    /** @internal */ static UNIFORM_NAME_CONSTANT_AMBIENT = 'zConstantAmbient';
    /** @internal */ _ambientColor;
    /**
   * Creates an instance of EnvConstantAmbient
   * @param ambientColor - The ambient color
   */ constructor(ambientColor){
        super();
        this._ambientColor = ambientColor ? new Vector4(ambientColor) : new Vector4(0, 0, 0, 1);
    }
    /** The ambient color */ get ambientColor() {
        return this._ambientColor;
    }
    set ambientColor(ambientColor) {
        if (ambientColor) {
            this._ambientColor.set(ambientColor);
        }
    }
    /**
   * {@inheritDoc EnvironmentLighting.getType}
   * @override
   */ getType() {
        return 'constant';
    }
    /**
   * {@inheritDoc EnvironmentLighting.initShaderBindings}
   * @override
   */ initShaderBindings(pb) {
        if (pb.shaderKind === 'fragment') {
            pb.getGlobalScope()[EnvConstantAmbient.UNIFORM_NAME_CONSTANT_AMBIENT] = pb.vec4().uniform(0);
        }
    }
    /**
   * {@inheritDoc EnvironmentLighting.updateBindGroup}
   * @override
   */ updateBindGroup(bg) {
        bg.setValue(EnvConstantAmbient.UNIFORM_NAME_CONSTANT_AMBIENT, this._ambientColor);
    }
    /**
   * {@inheritDoc EnvironmentLighting.getRadiance}
   * @override
   */ getRadiance(_scope, _refl, _roughness) {
        return null;
    }
    /**
   * {@inheritDoc EnvironmentLighting.getIrradiance}
   * @override
   */ getIrradiance(scope, _normal) {
        return scope[EnvConstantAmbient.UNIFORM_NAME_CONSTANT_AMBIENT].rgb;
    }
    /**
   * {@inheritDoc EnvironmentLighting.hasRadiance}
   * @override
   */ hasRadiance() {
        return false;
    }
    /**
   * {@inheritDoc EnvironmentLighting.hasIrradiance}
   * @override
   */ hasIrradiance() {
        return true;
    }
}
/**
 * Hemispheric ambient light
 * @public
 */ class EnvHemisphericAmbient extends EnvironmentLighting {
    /** @internal */ static UNIFORM_NAME_AMBIENT_UP = 'zHemisphericAmbientUp';
    /** @internal */ static UNIFORM_NAME_AMBIENT_DOWN = 'zHemisphericAmbientDown';
    /** @internal */ _ambientUp;
    /** @internal */ _ambientDown;
    /**
   * Creates an instance of EnvConstantAmbient
   * @param ambientUp - The upside ambient color
   * @param ambientDown - The downside ambient color
   */ constructor(ambientUp, ambientDown){
        super();
        this._ambientUp = new Vector4(ambientUp);
        this._ambientDown = new Vector4(ambientDown);
    }
    /** The upside ambient color */ get ambientUp() {
        return this._ambientUp;
    }
    set ambientUp(color) {
        if (color) {
            this._ambientUp.set(color);
        }
    }
    /** The downside ambient color */ get ambientDown() {
        return this._ambientDown;
    }
    set ambientDown(color) {
        if (color) {
            this._ambientDown.set(color);
        }
    }
    /**
   * {@inheritDoc EnvironmentLighting.getType}
   * @override
   */ getType() {
        return 'hemisphere';
    }
    /**
   * {@inheritDoc EnvironmentLighting.initShaderBindings}
   * @override
   */ initShaderBindings(pb) {
        if (pb.shaderKind === 'fragment') {
            pb.getGlobalScope()[EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_UP] = pb.vec4().uniform(0);
            pb.getGlobalScope()[EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_DOWN] = pb.vec4().uniform(0);
        }
    }
    /**
   * {@inheritDoc EnvironmentLighting.updateBindGroup}
   * @override
   */ updateBindGroup(bg) {
        bg.setValue(EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_UP, this._ambientUp);
        bg.setValue(EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_DOWN, this._ambientDown);
    }
    /**
   * {@inheritDoc EnvironmentLighting.getRadiance}
   * @override
   */ getRadiance(scope, refl, _roughness) {
        const pb = scope.$builder;
        const factor = pb.add(pb.mul(refl.y, 0.5), 0.5);
        return pb.mix(scope[EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_DOWN], scope[EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_UP], factor).rgb;
    }
    /**
   * {@inheritDoc EnvironmentLighting.getIrradiance}
   * @override
   */ getIrradiance(scope, normal) {
        const pb = scope.$builder;
        const factor = pb.add(pb.mul(normal.y, 0.5), 0.5);
        return pb.mix(scope[EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_DOWN], scope[EnvHemisphericAmbient.UNIFORM_NAME_AMBIENT_UP], factor).rgb;
    }
    /**
   * {@inheritDoc EnvironmentLighting.hasRadiance}
   * @override
   */ hasRadiance() {
        return true;
    }
    /**
   * {@inheritDoc EnvironmentLighting.hasIrradiance}
   * @override
   */ hasIrradiance() {
        return true;
    }
}

export { EnvConstantAmbient, EnvHemisphericAmbient, EnvShIBL, EnvironmentLighting };
//# sourceMappingURL=envlight.js.map
