import { RenderPass } from './renderpass.js';
import { RENDER_PASS_TYPE_LIGHT, MaterialVaryingFlags, QUEUE_OPAQUE, QUEUE_TRANSPARENT } from '../values.js';
import { Vector4 } from '@zephyr3d/base';
import { ShaderHelper } from '../material/shader/helper.js';
import { PostEffectLayer } from '../posteffect/posteffect.js';

/**
 * Forward render pass
 * @internal
 */ class LightPass extends RenderPass {
    /** @internal */ _shadowMapHash;
    /** @internal */ _transmission;
    /**
   * Creates an instance of ForwardRenderPass
   */ constructor(){
        super(RENDER_PASS_TYPE_LIGHT);
        this._shadowMapHash = null;
        this._transmission = false;
        this._clearColor = Vector4.zero();
    }
    /** @internal */ get transmission() {
        return this._transmission;
    }
    set transmission(val) {
        this._transmission = val;
    }
    /** @internal */ _getGlobalBindGroupHash(ctx, camera) {
        return `${this._shadowMapHash}:${ctx.lightBlending ? 1 : 0}:${camera.oit?.calculateHash() ?? ''}:${ctx.env.getHash(ctx)}:${ctx.materialFlags}:${ctx.linearDepthTexture?.uid ?? 0}:${ctx.sceneColorTexture?.uid ?? 0}:${ctx.HiZTexture?.uid ?? 0}`;
    }
    /** @internal */ renderLightPass(ctx, camera, itemList, lights, flags) {
        const baseLightPass = !ctx.lightBlending;
        ctx.drawEnvLight = baseLightPass && ctx.env.light.type !== 'none' && (ctx.env.light.envLight.hasRadiance() || ctx.env.light.envLight.hasIrradiance());
        ctx.renderPassHash = this.getGlobalBindGroupHash(ctx, camera);
        const bindGroup = ctx.globalBindGroupAllocator.getGlobalBindGroup(ctx);
        if (!flags.cameraSet[ctx.renderPassHash]) {
            ShaderHelper.setCameraUniforms(bindGroup, ctx, camera, !!ctx.device.getFramebuffer());
            flags.cameraSet[ctx.renderPassHash] = 1;
        }
        if (ctx.currentShadowLight) {
            ShaderHelper.setLightUniformsShadow(bindGroup, ctx, lights[0]);
        } else {
            if (!flags.lightSet[ctx.renderPassHash]) {
                ShaderHelper.setLightUniforms(bindGroup, ctx, ctx.clusteredLight.clusterParam, ctx.clusteredLight.countParam, ctx.clusteredLight.lightBuffer, ctx.clusteredLight.lightIndexTexture);
                flags.lightSet[ctx.renderPassHash] = 1;
            }
        }
        if (ctx.materialFlags & MaterialVaryingFlags.APPLY_FOG && !flags.fogSet[ctx.renderPassHash]) {
            ShaderHelper.setFogUniforms(bindGroup, ctx.env.sky.skyType === 'scatter' ? 1 : 0, ctx.env.sky.mappedFogType, baseLightPass ? 0 : 1, ctx.env.sky.atmosphereParams, ctx.env.sky.heightFogParams, ctx.env.sky.getAerialPerspectiveLUT(ctx), ctx.env.sky.getSkyDistantLightLUT(ctx));
            flags.fogSet[ctx.renderPassHash] = 1;
        }
        ctx.device.setBindGroup(0, bindGroup);
        const reverseWinding = camera.worldMatrixDet < 0;
        for (const lit of itemList.lit){
            this.drawItemList(lit, ctx, reverseWinding);
        }
        if (!ctx.lightBlending) {
            for (const unlit of itemList.unlit){
                this.drawItemList(unlit, ctx, reverseWinding);
            }
        }
    }
    /** @internal */ renderItems(ctx, camera, renderQueue) {
        ctx.renderPassHash = null;
        ctx.env = ctx.scene.env;
        ctx.drawEnvLight = false;
        ctx.flip = this.isAutoFlip(ctx);
        const ssr = !!(ctx.materialFlags & MaterialVaryingFlags.SSR_STORE_ROUGHNESS);
        const tmpFramebuffer = ssr ? ctx.device.pool.fetchTemporalFramebuffer(false, ctx.device.getDrawingBufferWidth(), ctx.device.getDrawingBufferHeight(), ctx.device.getFramebuffer().getColorAttachments()[0], ctx.device.getFramebuffer().getDepthAttachment()) : null;
        const oit = renderQueue.drawTransparent && camera.oit && camera.oit.supportDevice(ctx.device.type) ? camera.oit : null;
        if (!oit && renderQueue.drawTransparent) {
            renderQueue.sortTransparentItems(camera.getWorldPosition());
        }
        const flags = {
            lightSet: {},
            cameraSet: {},
            fogSet: {}
        };
        const items = renderQueue.itemList;
        const lists = this._transmission ? [
            items?.transmission,
            items?.transmission_trans,
            items?.transparent
        ] : [
            items?.opaque,
            items?.transparent
        ];
        const transparentPass = lists.length - 1;
        for(let i = 0; i < lists.length; i++){
            if (lists[i]) {
                ctx.queue = i === 0 ? QUEUE_OPAQUE : QUEUE_TRANSPARENT;
                ctx.oit = i === 0 || !items ? null : oit;
                if ((ctx.queue === QUEUE_TRANSPARENT || this._transmission) && ctx.scene.env.sky.fogPresents) {
                    ctx.materialFlags |= MaterialVaryingFlags.APPLY_FOG;
                }
                const numOitPasses = ctx.oit ? ctx.oit.begin(ctx) : 1;
                for(let p = 0; p < numOitPasses; p++){
                    if (ctx.oit) {
                        if (!ctx.oit.beginPass(ctx, p)) {
                            continue;
                        }
                    }
                    let lightIndex = 0;
                    if (ctx.shadowMapInfo) {
                        for (const k of ctx.shadowMapInfo.keys()){
                            ctx.currentShadowLight = k;
                            ctx.lightBlending = lightIndex > 0;
                            this._shadowMapHash = ctx.shadowMapInfo.get(k).shaderHash;
                            this.renderLightPass(ctx, camera, lists[i], [
                                k
                            ], flags);
                            lightIndex++;
                        }
                    }
                    if (lightIndex === 0 || renderQueue.unshadowedLights.length > 0) {
                        ctx.currentShadowLight = null;
                        ctx.lightBlending = lightIndex > 0;
                        this._shadowMapHash = '';
                        if (ctx.lightBlending && tmpFramebuffer) {
                            ctx.materialFlags &= ~MaterialVaryingFlags.SSR_STORE_ROUGHNESS;
                            ctx.device.pushDeviceStates();
                            ctx.device.setFramebuffer(tmpFramebuffer);
                        }
                        this.renderLightPass(ctx, camera, lists[i], renderQueue.unshadowedLights, flags);
                        if (ctx.lightBlending && tmpFramebuffer) {
                            ctx.materialFlags |= MaterialVaryingFlags.SSR_STORE_ROUGHNESS;
                            ctx.device.popDeviceStates();
                        }
                    }
                    if (ctx.oit) {
                        ctx.oit.endPass(ctx, p);
                    }
                }
                if (ctx.oit) {
                    ctx.oit.end(ctx);
                }
                ctx.materialFlags &= ~MaterialVaryingFlags.APPLY_FOG;
            }
            if (i === 0 && !ctx.sceneColorTexture) {
                if (tmpFramebuffer) {
                    ctx.device.pushDeviceStates();
                    ctx.device.setFramebuffer(tmpFramebuffer);
                }
                ctx.env.sky.renderSky(ctx);
                if (ctx.env.sky.fogPresents) {
                    ctx.env.sky.renderFog(camera);
                }
                if (tmpFramebuffer) {
                    ctx.device.popDeviceStates();
                }
            }
            if (!renderQueue.needSceneColor() || ctx.sceneColorTexture) {
                if (i === 0 || i === transparentPass) {
                    ctx.compositor?.drawPostEffects(ctx, i === 0 ? PostEffectLayer.opaque : PostEffectLayer.transparent, ctx.linearDepthTexture);
                }
            }
        }
        if (tmpFramebuffer) {
            ctx.device.pool.releaseFrameBuffer(tmpFramebuffer);
        }
    }
}

export { LightPass };
//# sourceMappingURL=lightpass.js.map
