/*
Nether Storm — volumetric ray-marched 3D noise haze for the Nether.
Adapted from Rethinking Voxels' netherStorm.glsl for BSL's render pipeline.

Technique:
- Marches view rays through the world from camera outward
- At each step, samples 4 octaves of 3D noise (faked from 2D noisetex)
- Falls off above/below a configurable altitude band
- Animated drift via frameTimeCounter, tinted by netherCol
*/

float NetherStormNoise3D(vec3 p) {
    p.z = fract(p.z) * 128.0;
    float iz = floor(p.z);
    float fz = fract(p.z);
    vec2 a_off = vec2(23.0, 29.0) * (iz) / 128.0;
    vec2 b_off = vec2(23.0, 29.0) * (iz + 1.0) / 128.0;
    float a = texture2D(noisetex, p.xy + a_off).r;
    float b = texture2D(noisetex, p.xy + b_off).r;
    return mix(a, b, fz);
}

vec4 GetNetherStorm(vec3 viewPos, float z0, float z1, float dither) {
    // Don't draw storm against the void/sky beyond solids — only inside the volume
    vec3 nViewPos = normalize(viewPos);
    float lViewPos = length(viewPos);
    
    // View distance limit (we want storm to be visible across reasonable distances)
    float maxDist = min(far, 192.0);
    
    #ifdef LOW_QUALITY_NETHER_STORM
        int sampleCount = int(maxDist / 32.0);
    #else
        int sampleCount = int(maxDist / 16.0);
    #endif
    
    // Convert view space direction to world space for stable world-anchored noise
    // (BSL provides cameraPosition uniform for world position)
    vec3 worldDir = mat3(gbufferModelViewInverse) * nViewPos;
    vec3 traceAdd = worldDir * maxDist / float(sampleCount);
    vec3 tracePos = cameraPosition + traceAdd * dither;
    
    // The depth in world units where geometry was hit (so we don't draw storm behind walls)
    float worldDepth = lViewPos;
    
    vec4 storm = vec4(0.0);
    
    for (int i = 0; i < sampleCount; i++) {
        tracePos += traceAdd;
        
        // Stop the march at solid geometry
        float traceDistFromCam = length(tracePos - cameraPosition);
        if (traceDistFromCam > worldDepth) break;
        
        vec3 wind = vec3(frameTimeCounter * 0.002);
        
        vec3 tracePosM = tracePos * 0.001;
        tracePosM.y += tracePosM.x;
        tracePosM += NetherStormNoise3D(tracePosM - wind) * 0.01;
        tracePosM = tracePosM * vec3(2.0, 0.5, 2.0);
        
        // Altitude falloff: storm concentrates around NETHER_STORM_ALT
        float traceAltitudeM = abs(tracePos.y - NETHER_STORM_ALT);
        if (tracePos.y < NETHER_STORM_ALT) traceAltitudeM *= 10.0;
        traceAltitudeM = 1.0 - clamp(traceAltitudeM / NETHER_STORM_HEIGHT, 0.0, 1.0);
        
        // 4 octaves of fractal noise — fast and looks like turbulent cloud
        float density = 0.0;
        for (int h = 0; h < 4; h++) {
            float sampleVal = NetherStormNoise3D(tracePosM + wind);
            sampleVal = sampleVal * sampleVal;
            sampleVal *= traceAltitudeM;
            sampleVal = sampleVal * sampleVal;
            sampleVal = sampleVal * sampleVal;
            sampleVal *= sqrt(max(1.0 - traceDistFromCam / maxDist, 0.0));
            
            density += sampleVal;
            tracePosM *= 2.0;
            wind *= -2.0;
        }
        
        storm.a += density;
    }
    
    storm.a = clamp(storm.a * NETHER_STORM_I, 0.0, 1.0);
    storm.rgb = netherCol.rgb * NETHER_STORM_BRIGHTNESS;
    
    return storm;
}
