#version 400 compatibility

/*
====================================================================================================

    Copyright (C) 2020 RRe36

    All Rights Reserved unless otherwise explicitly stated.


    By downloading this you have agreed to the license and terms of use.
    These can be found inside the included license-file
    or here: https://rre36.com/copyright-license

    Violating these terms may be penalized with actions according to the Digital Millennium
    Copyright Act (DMCA), the Information Society Directive and/or similar laws
    depending on your country.

====================================================================================================
*/

#include "/lib/head.glsl"
#include "/lib/util/encoders.glsl"

in vec2 coord;

flat in mat2x3 lightColor;

uniform sampler2D colortex0;
uniform sampler2D colortex2;
uniform sampler2D colortex3;

uniform sampler2D depthtex0;
uniform sampler2D depthtex1;

uniform sampler2D noisetex;

uniform int frameCounter;
uniform int isEyeInWater;

uniform float eyeAltitude;
uniform float far;
uniform float frameTimeCounter;

uniform vec2 taaOffset;

uniform vec3 cameraPosition;

uniform mat4 gbufferModelView, gbufferModelViewInverse;
uniform mat4 gbufferProjection, gbufferProjectionInverse;


/* ------ includes ------ */
#define FUTIL_TBLEND
#include "/lib/fUtil.glsl"

#include "/lib/frag/bluenoise.glsl"

#include "/lib/util/transforms.glsl"

#include "/lib/frag/noise.glsl"


/* ------ functions ------ */
vec3 smokeDensity(vec3 rPos, float altitude) {
    float smokeFade = sqr(1.0 - linStep(altitude, 128.0, 256.0));
        smokeFade  *= expf(-max0((altitude - 32.0) * 0.03)) * 0.6 + 0.4;

    float smoke     = 0.0;
    float smokeGlowing = 0.0;

    if (smokeFade > 1e-10) {
        vec3 wind   = frameTimeCounter * 0.6 * vec3(1.0, -0.9, 0.1);

        vec3 pos    = (rPos + cameraPosition) * 0.03;
            pos    += value3D(pos * 4.0 + wind * 0.5) * 1.5 - 0.75;
            pos    += value3D(pos * 8.0 - wind) - 0.5;

        pos.x *= 0.7;

        float noise     = value3D(pos * 4.0 + wind);
            noise      += value3D(pos * 8.0 + wind * 2 + noise * 0.5) * 0.5;
            //noise      += value3D(pos * 16.0 + wind * 4 + noise * 0.5) * 0.25;
            noise      /= 1.5;

        smoke       = smokeFade - noise;
        float glowProximity = sqrt(sstep(length(rPos), 4.0, 24.0));
        smokeGlowing = saturate((glowProximity * (0.3 + cube(smokeFade) * 0.15) - noise) * pi);
        //smoke      -= 0.1;

        smoke       = cubeSmooth(max0(smoke)) * (smokeFade * 0.5 + 0.5);
        smokeGlowing *= smokeFade;
    }

    return vec3(1.0, smoke, smokeGlowing);
}

flat in vec3 fogCol;
flat in vec3 fogCol2;

const vec3 hazeCoeff    = vec3(1e-3);
const vec3 smokeCoeff   = vec3(3e-2);

mat2x3 volumetricSmoke(vec3 scenePos, vec3 sceneDir, float dither) {
    vec3 startPos   = gbufferModelViewInverse[3].xyz;
    vec3 endPos     = scenePos;
        if (length(endPos) > 192.0) endPos = sceneDir * 192.0;

    float baseStep  = length(endPos - startPos);
    float stepCoeff = saturate(baseStep * rcp(clamp(far, 128.0, 192.0)));

    uint steps      = 8 + uint(stepCoeff * 16.0);

    vec3 rStep      = (endPos - startPos) / float(steps);
    vec3 rPos       = startPos + rStep * dither;
    float rLength   = length(rStep);

    mat3x3 scattering = mat3x3(0.0);
    vec3 transmittance = vec3(1.0);

    const mat2x3 scatterMat   = mat2x3(hazeCoeff, smokeCoeff);
    const mat2x3 extinctMat   = mat2x3(hazeCoeff * 1.1, smokeCoeff);

    for (uint i = 0; i < steps; ++i, rPos += rStep) {
        if (max3(transmittance) < 0.01) break;

        float altitude  = rPos.y + eyeAltitude;

        if (altitude > 256.0) continue;

        vec3 density    = smokeDensity(rPos, altitude);

        vec2 stepRho    = density.xy * rLength;
        vec3 od         = extinctMat * stepRho;

        vec3 stepT      = expf(-od);
        vec3 scatterInt = saturate((stepT - 1.0) * rcp(-max(od, 1e-16)));
        vec3 visScatter = transmittance * scatterInt;

        vec3 a          = visScatter * transmittance;

        mat2x3 stepScatter = mat2x3(scatterMat[0] * stepRho.x * a, scatterMat[1] * stepRho.y * a);

        #ifdef netherSmokeEmission
            vec3 smokeEmission = density.z * mix(vec3(1.0, 0.1, 0.0), lightColor[1] * 0.5, density.z) * stepRho.y * a;

            scattering     += mat3x3(stepScatter[0], stepScatter[1], smokeEmission);
        #else
            scattering     += mat3x3(stepScatter[0], stepScatter[1], vec3(0.0));
        #endif

        transmittance  *= stepT;
    }

    //vec3 smokeCol       = mix(fogCol, lightColor[1], saturate(scattering[1]));

    vec3 color          = scattering[0] * fogCol2 + scattering[1] * fogCol + scattering[2] * 0.6 * netherSmokeEmissionMult;

    return mat2x3(color, transmittance);     
}

void applyFogData(inout vec3 color, in mat2x3 data) {
    color = color * data[1] + data[0];
}


/* ------ simple fog ------ */

vec3 simpleFog(vec3 scenecolor, float d, vec3 color) {
    //float dist      = max(0.0, d-16.0);
    float density   = (linStep(d, far * 0.4, far * 0.99));

    return mix(scenecolor, color, density);
}

vec3 lavaFog(vec3 scene, float d) {
    float density   = max(0.0, d);

    float transmittance = expf(-1.0 * density);

    return mix(vec3(1.0, 0.3, 0.02), scene, transmittance);
}

void main() {
    vec4 sceneColor = stex(colortex0);  //that macro certainly makes it neater
    //vec4 tex1       = stex(colortex1);
    vec4 tex2       = stex(colortex2);
    vec4 tex3       = stex(colortex3);

    float sceneDepth0   = stex(depthtex0).x;
    vec3 viewPos0       = screenToViewSpace(vec3(coord, sceneDepth0));
    vec3 scenePos0      = viewToSceneSpace(viewPos0);

    float sceneDepth1   = stex(depthtex1).x;
    vec3 viewPos1       = screenToViewSpace(vec3(coord, sceneDepth1));
    vec3 scenePos1      = viewToSceneSpace(viewPos1);

    bool translucent    = (sceneDepth0<sceneDepth1);

    vec3 translucentAlbedo = sqr(decode3x8(tex2.g));

    sceneColor.rgb      = blendTranslucencies(sceneColor.rgb, tex3, translucentAlbedo);

    sceneColor.rgb      = simpleFog(sceneColor.rgb, length(scenePos0), fogCol2);

    #ifdef netherSmokeEnabled
        mat2x3 fogData      = volumetricSmoke(scenePos0, normalize(scenePos0), ditherBluenoise());

        applyFogData(sceneColor.rgb, fogData);
    #endif

    if (isEyeInWater == 2) {
        sceneColor.rgb = lavaFog(sceneColor.rgb, length(viewPos0));
    }

    /*DRAWBUFFERS:0*/
    gl_FragData[0]  = makeDrawbuffer(sceneColor);
}