#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.

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

/*DRAWBUFFERS:0123*/
layout(location = 0) out vec4 scene;
layout(location = 1) out uvec4 sceneData;
layout(location = 2) out vec4 data2;
layout(location = 3) out vec4 data3;

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

const int shadowMapResolution   = 3072;     //[512 1024 1536 2048 2560 3072 3584 4096 6144 8192 16384]
const float shadowDistance      = 128.0;

const bool shadowHardwareFiltering = false;

in vec2 coord;

flat in mat3x3 lightColor;

uniform sampler2D colortex0;
uniform usampler2D colortex1;
uniform sampler2D colortex2;
uniform sampler2D colortex3;
uniform sampler2D colortex5;
uniform sampler2D colortex6;

uniform sampler2D noisetex;

uniform sampler2D depthtex0;

uniform sampler2D shadowtex0;
uniform sampler2D shadowtex1;
uniform sampler2D shadowcolor0;

uniform int frameCounter;

uniform float aspectRatio;
uniform float far, near;
uniform float viewHeight, viewWidth;

uniform vec2 taaOffset;
uniform vec2 viewSize, pixelSize;

uniform vec3 lightvec, lightvecView;

uniform mat4 gbufferModelView, gbufferModelViewInverse;
uniform mat4 gbufferProjection, gbufferProjectionInverse;
uniform mat4 shadowModelView, shadowModelViewInverse;
uniform mat4 shadowProjection, shadowProjectionInverse;


/* ------ includes ------ */
#include "/lib/util/transforms.glsl"

#define FUTIL_LINDEPTH
#define FUTIL_MAT16
#define FUTIL_LIGHTMAP
#define FUTIL_ROT2
#include "/lib/fUtil.glsl"

#define ATROUS_POSTRT
#include "/lib/util/kernel.glsl"
#include "/lib/light/indirect/atrousUpscale.glsl"

#include "/lib/light/diffuse.glsl"

#include "/lib/light/warp.glsl"

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

#define SOLIDPASS

#include "/lib/light/shadow.glsl"

#include "/lib/atmos/phase.glsl"

#include "/lib/light/subsurface.glsl"

#include "/lib/light/contactShadow.glsl"

#include "/lib/light/brdf.glsl"

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

/* ------ metal albedo encoding ------ */

float bayer2e(vec2 a){
    a = floor(a);
    return fract( dot(a, vec2(.5, a.y * .75)) );
}
#define bayer4e(a)   (bayer2e( .5*(a))*.25+bayer2e(a))

#define m vec3(31,63,31)
float encode3x8(vec3 a){
    float dither = bayer4e(gl_FragCoord.xy);
    a += (dither-.5) / m;
    a = saturate(a);
    ivec3 b = ivec3(a*m);
    return float( b.r|(b.g<<5)|(b.b<<11) ) / 65535.;
}
#undef m


/* ------ stars ------ */

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

vec3 skyStars(vec3 worldDir) {
    vec3 plane  = worldDir/(worldDir.y+length(worldDir.xz)*0.66);
    //float rot   = worldTime*rcp(2400.0);
    //plane.x    += rot*0.6;
    //plane.yz    = rotatePos(plane.yz, (25.0/180.0)*pi);
    vec2 uv1    = floor((plane.xz)*768)/768;
    vec2 uv2    = (plane.xz)*0.04;

    vec3 starcol = vec3(0.3, 0.78, 1.0);
        starcol  = mix(starcol, vec3(1.0, 0.7, 0.6), noise2D(uv2).x);
        starcol  = normalize(starcol)*(noise2D(uv2*1.5).x+1.0);

    float star  = 1.0;
        star   *= noise2D(uv1).x;
        star   *= noise2D(uv1+0.1).x;
        star   *= noise2D(uv1+0.26).x;

    star        = max(star-0.25, 0.0);
    star        = saturate(star*4.0);

    return star*starcol*0.25;
}


vec3 sunDisk(vec3 viewDir, vec3 sunDir) {
    vec3 sv     = normalize(sunDir + viewDir);
    float sun   = dot(sv, viewDir);

    const float size    = 0.015;
    float maxsize       = size + 0.001;

    float s   = 1.0-linStep(sun, size, maxsize);

    float limb = 1.0 - cube(linStep(sun, 0.0, maxsize))*0.9;
        s    *= limb;

    return s * lightColor[0] * 32.0;
}

void main() {
    vec4 sceneColor     = stex(colortex0);
    float sceneDepth    = stex(depthtex0).x;

    uvec4 tex1          = stex(colortex1);
        tex1.w          = pack2x16(vec2(encode3x8(sqrt(saturate(sceneColor.rgb))), 0.0));

    vec3 albedo         = sceneColor.rgb;

    vec3 viewPos    = screenToViewSpace(vec3(coord, sceneDepth));
    vec3 scenePos   = viewToSceneSpace(viewPos);

    #if DEBUG_VIEW!=4
    if (landMask(sceneDepth)) {

            vec2 d1     = unpack2x16(tex1.y);
        vec2 sceneLmap  = decode2x8(d1.x);

        vec3 sceneNormal = decodeNormal(unpack2x16(tex1.x));
        vec3 viewNormal = mat3(gbufferModelView) * sceneNormal;

        int matID       = decodeMatID16(d1.y);

        vec4 specularData = unpack4x8(tex1.z);

        materialLAB matLAB = decodeSpecularTexture(specularData);

        vec3 viewDir    = normalize(viewPos);

        //float rtao      = 1.0;

        /*
        #ifdef lightmapSmoothingEnabled
        vec4 tex3       = lightmap3x3(colortex3, depthtex0, colortex6, coord, 1);
            sceneLmap   = tex3.xy;
            sceneColor.a = tex3.z;
            rtao        = tex3.a;
        #else
            rtao        = stex(colortex3).a;
        #endif

        #ifndef rtaoEnabled
            rtao    = 1.0;
        #endif

        float ao        = sqr(sceneColor.a)*0.66+0.34;
        */
        
        /*
        #if (defined ambientOcclusionToggle && !defined rtaoEnabled) 
            vec4 upscale3 = textureBilateral(colortex3, depthtex0, 2, sceneDepth);
                ao   *= upscale3.w;
        #endif
        */
        
        /* ------ lighting calculation ------ */
        //~6 fps
        
        vec2 lmap       = sceneLmap;
        lmap.y          = cube(lmap.y);

        #if diffuseModel == 0
        float diff      = diffuseHammon(viewNormal, -viewDir, lightvecView, matLAB.roughness);
        #else
        float diff      = diffuseLambert(sceneNormal, lightvec);
        #endif

        #ifndef subsurfaceScatteringEnabled
            if (matID == 2) diff = diff*0.3+0.7;
            if (matID == 4) diff = sqrt(diff);
        #endif

        shadowData shadow = shadowData(1.0, vec3(1.0), vec3(0.0));

        #ifdef subsurfaceScatteringEnabled
            bool isSSS = matID == 2 || matID == 4;
            if (isSSS || matLAB.opacity > 0.002) shadow = getShadowSubsurface(diff>0.0, scenePos, normalize(viewPos), sceneColor.rgb, max(float(isSSS), matLAB.opacity));
            else shadow = getShadowRegular(diff>0.0, scenePos);    //~3fps, almost for free
        #else
            shadow = getShadowRegular(diff>0.0, scenePos);    //~3fps, almost for free
        #endif

        float diffLit       = min(diff, shadow.shadow);

        #ifdef contactShadowsEnabled
            if (diffLit > 0.0) diffLit *= getContactShadow(depthtex0, viewPos, ditherBluenoise(), sceneDepth, dot(viewNormal, viewDir), lightvecView);
            //if (diffLit > 0.0) diffLit *= getRTShadow(viewPos, ditherBluenoise());
        #endif

        vec3 directCol      = lightColor[0];
        vec3 directLight    = diffLit * shadow.color * directCol;

        materialProperties material     = materialProperties(1.0, 0.04, false, false, mat2x3(1.0));
                material    = labToGeneric(matLAB);

        #ifdef subsurfaceScatteringEnabled
            directLight    += shadow.subsurfaceScatter * directCol;
        #endif

        vec3 indirectLight = atrous3x3(colortex3, depthtex0, colortex6, coord, 1).rgb;

        vec3 result     = directLight + indirectLight;

        vec2 emissionParam = decode2x8(stex(colortex2).z);

        #ifndef labEmissionEnabled
            emissionParam.y = 0.0;
        #endif

        #ifdef emitterBoostEnabled
        float emissionAlpha = max2(emissionParam);

        if (emissionAlpha > 0.001) {
            float albedoLum     = getLuma(sceneColor.rgb);
            vec3 emitterCol     = mix(lightColor[2], vec3(v3avg(lightColor[2])), emitterRecoloring);
                //emitterCol     *= cubeSmooth(saturate(v3avg(sceneColor.rgb) * 1.5));
                emitterCol    *= saturate(mix(cube(albedoLum) * normalize(sceneColor.rgb), sceneColor.rgb, albedoLum));
            result += emitterCol * pi * emissionAlpha;
        }
        #endif

        sceneColor.rgb *= result;

        #if DEBUG_VIEW == 1
            sceneColor.rgb = 0.5 * result;
        #endif

        #ifdef specularBRDFEnabled
        if (diffLit > 0.0) {
            vec3 brdf   = BRDF(-viewDir, lightvecView, viewNormal, material) * directCol;

            #ifdef lightleakWorkaroundToggle
                brdf   *= sstep(sceneLmap.y, 0.1, 0.2);
            #endif

            //vec3 fresnel = BRDFfresnel(-viewDir, viewNormal, material);
            sceneColor.rgb = sceneColor.rgb + brdf;
            //sceneColor.rgb *= fresnel;
        }
        #endif

        #ifdef deffNAN
        if (isnan3(sceneColor.rgb) || isinf3(sceneColor.rgb)) sceneColor.g = 100.0;
        #endif

        #if DEBUG_VIEW==2
            //sceneColor.rgb    = vec3(indirectOcclusion.x) * lightColor[0];

            #ifdef DEBUG_WITH_ALBEDO
                sceneColor.rgb *= albedo;
            #endif
        #endif

        #if DEBUG_VIEW==3
            sceneColor.rgb    = indirectLight;

            #ifdef DEBUG_WITH_ALBEDO
                sceneColor.rgb *= albedo;
            #endif
        #endif
    }
    #endif

    if (!landMask(sceneDepth)) {
        sceneColor.rgb  = skyStars(normalize(scenePos));
        sceneColor.rgb += sunDisk(-normalize(viewPos), lightvecView);
    }

    scene       = makeDrawbuffer(sceneColor);
    sceneData   = tex1;
    data2       = vec4(0.0);
    data3       = vec4(0.0);
}