// ============================================================
//  deferred1.fsh
//  Volumetric cloud pass (runs before composite outline pass)
//
//  Cloud algorithm adapted from Escheridia V4.1 by jrwilliamson
//  (MIT License, 2025)
//
//  Writes to two targets:
//    colortex0 -- scene colour composited with clouds
//    colortex7 -- cloud opacity [0,1] in .r channel
//                 read by final.fsh computeGodrays() to occlude rays behind clouds
// ============================================================
#include "commonSettings.glsl"
#include "lib.glsl"

uniform sampler2D colortex0;
uniform sampler2D depthtex0;

// Cloud noise textures (from Escheridia V4.1, MIT-licensed)
uniform sampler2D clouds;
uniform sampler2D cloudBase;
uniform sampler2D fogTex;
uniform sampler2D detail;

uniform mat4  gbufferModelViewInverse;
uniform mat4  gbufferProjectionInverse;
uniform vec3  cameraPosition;
uniform float far;
uniform float viewWidth;
uniform float viewHeight;
uniform int frameCounter;
uniform float frameTimeCounter;
uniform float sunAngle;
uniform float rainStrength;
uniform vec3  shadowLightPosition;
uniform int   isEyeInWater;
uniform vec3  fogColor;
uniform float nightVision;

/* RENDERTARGETS: 0,7 */

varying vec2 texcoord;

#include "clouds.glsl"

vec3 reconstructWorldPos(vec2 uv, float depth) {
    vec3 ndcPos   = vec3(uv, depth) * 2.0 - 1.0;
    vec4 viewPos4 = gbufferProjectionInverse * vec4(ndcPos, 1.0);
    vec3 viewPos  = viewPos4.xyz / viewPos4.w;
    vec3 feetPos  = (gbufferModelViewInverse * vec4(viewPos, 1.0)).xyz;
    return feetPos + cameraPosition;
}

void main() {
    vec3  sceneColor = texture2D(colortex0, texcoord).rgb;
    float depth      = texture2D(depthtex0, texcoord).r;
     #if defined(IS_IRIS) && defined(OVERWORLD)

        // Pass a far-away sentinel instead so viewLength never limits the raymarch.
        vec3 worldPos;
        if (depth >= 0.9999) {
            // Reconstruct ray direction directly from NDC, bypassing the view matrix
            vec2 ndc = texcoord * 2.0 - 1.0;
            vec4 unproj = gbufferProjectionInverse * vec4(ndc, 0.0, 1.0);
            vec3 viewDir = normalize(unproj.xyz / unproj.w);
            // Rotate from view space to world space using ONLY the rotation part
            // of the model-view matrix (no translation, no bob offset)
            vec3 worldDir = normalize(mat3(gbufferModelViewInverse) * viewDir);
            worldPos = cameraPosition + worldDir * 1e7;
        } else {
            worldPos = reconstructWorldPos(texcoord, depth);
        }

        vec2 fragCoord = texcoord * vec2(viewWidth, viewHeight);
        #if WHITEOUT_SKY == 1
            vec4 cloudResult = vec4(sceneColor, 0.0); // clouds disabled
        #else
            vec4 cloudResult = cloudRaymarch(fragCoord, worldPos, sceneColor);
        #endif

        if (isEyeInWater == 1) {
            cloudResult.rgb = mix(sceneColor, fogColor, cloudResult.a * 0.3);
            cloudResult.a   = 0.0;
        }

        vec3 cloudFinal = cloudResult.rgb;
        if (nightVision > 0.0 && cloudResult.a > 0.01) {
            #define NV_BRIGHTNESS_LIFT    0.85  // How much dark sky is pushed toward bright grey
            #define NV_BRIGHTNESS_FLOOR   0.7   // Minimum brightness target for dark areas
            #define NV_CONTRAST_BOOST     0.15  // How much contrast is added
            #define NV_TINT_STRENGTH      0.5   // How strong the blue-green tint is
            #define NV_TINT_R             0.55  // Tint red channel
            #define NV_TINT_G             0.75  // Tint green channel
            #define NV_TINT_B             0.60  // Tint blue channel
            #define NV_TINT_BRIGHTNESS    1.4   // Overall brightness of the tint color
            #define NV_DARK_CUTOFF        0.5   // Sky luma above this = no effect (raise to affect brighter skies too)
            // Only process pixels that actually have cloud in them
            // Isolate just the cloud contribution, apply NV, then recomposite
            vec3 cloudOnly = mix(sceneColor, cloudResult.rgb, 1.0 / max(cloudResult.a, 0.01));
            cloudOnly = clamp(cloudOnly, 0.0, 1.0);

            float luma = dot(cloudOnly, vec3(0.299, 0.587, 0.114));
            float darknessFactor = 1.0 - smoothstep(0.0, NV_DARK_CUTOFF, luma);
            float strength = nightVision * darknessFactor;

            cloudOnly = mix(cloudOnly, vec3(max(luma, NV_BRIGHTNESS_FLOOR)), strength * NV_BRIGHTNESS_LIFT);
            float nvLuma = dot(cloudOnly, vec3(0.299, 0.587, 0.114));
            cloudOnly = mix(vec3(nvLuma), cloudOnly, 1.0 + NV_CONTRAST_BOOST * strength);
            cloudOnly = mix(cloudOnly, cloudOnly * vec3(NV_TINT_R, NV_TINT_G, NV_TINT_B) * NV_TINT_BRIGHTNESS, strength * NV_TINT_STRENGTH);
            cloudOnly = clamp(cloudOnly, 0.0, 1.0);

            cloudFinal = mix(sceneColor, cloudOnly, cloudResult.a);
        }
        gl_FragData[0] = vec4(cloudFinal, 1.0);
        gl_FragData[1] = vec4(cloudResult.a, 0.0, 0.0, 1.0);
    #else
        // No custom clouds
        gl_FragData[0] = vec4(sceneColor, 1.0);
        gl_FragData[1] = vec4(0.0, 0.0, 0.0, 1.0);
    #endif
}
