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

    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/colorspace.glsl"
#include "/lib/util/encoders.glsl"

const int shadowMapResolution   = 2048;     //[512 1024 1536 2048 4560 3072 3584 4096 6144 8192 16384]

const bool shadowHardwareFiltering = false;

in mat2x2 coord;

in float warp;

in vec3 shadowmapPos;
in vec3 worldPos;

in vec4 tint;

flat in int matID;

flat in vec3 normal;

flat in mat3x3 lightColor;

#ifdef gTERRAIN
    in float viewDist;
    flat in mat3 tbn;
    in vec3 tangentViewDir;
#endif

uniform sampler2D gcolor;

uniform sampler2D noisetex;

uniform int frameCounter;

uniform float frameTimeCounter;
uniform float lightFlip;
uniform float sunAngle;

uniform vec3 lightvec, lightvecView;

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

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

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

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


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

/*
float get_specGGX(vec3 normal, vec3 viewvec, vec3 lvec, float roughness) {
    const float f0  = 0.02;
    roughness  *= roughness;

    vec3 h      = lvec - viewvec;
    float hn    = inversesqrt(dot(h, h));
    float hDotL = saturate(dot(h, lvec)*hn);
    float hDotN = saturate(dot(h, normal)*hn);
    float nDotL = saturate(dot(normal, lvec));
    float denom = (hDotN * roughness - hDotN) * hDotN + 1.0;
    float D     = roughness / (pi * denom * denom);
    float F     = f0 + (1.0-f0) * exp2((-5.55473*hDotL-6.98316)*hDotL);
    float k2    = 0.25 * roughness;

    return nDotL * D * F / (hDotL * hDotL * (1.0-k2) + k2);
}*/


#ifdef gTERRAIN

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

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

#define waterParallaxDepth 2.0

vec3 waterParallax(vec3 pos, vec3 dir) {    //based on spectrum by zombye
    const uint steps    = 10;

    vec3 interval   = inversesqrt(float(steps)) * dir / -dir.y;
    float height    = waterWaves(pos) * waterParallaxDepth;
    float stepSize  = height;
        pos.xz     += stepSize * interval.xz;

    float offset    = stepSize * interval.y;
        height      = waterWaves(pos) * waterParallaxDepth;

    for (uint i = 1; i < steps - 1 && height < offset; ++i) {
        stepSize    = offset - height;
        pos.xz     += stepSize * interval.xz;

        offset     += stepSize * interval.y;
        height      = waterWaves(pos) * waterParallaxDepth;
    }

    if (height < offset) {
        stepSize    = offset - height;
        pos.xz     += stepSize * interval.xz;
    }

    return pos;
}

vec3 waterNormal() {
    vec3 pos    = waterParallax(worldPos, tangentViewDir.xzy);

    float dstep   = 0.002 + (1.0 - exp(-viewDist * rcp(32.0))) * 0.038;

    vec2 delta;
        delta.x     = waterWaves(pos + vec3( dstep, 0.0, -dstep));
        delta.y     = waterWaves(pos + vec3(-dstep, 0.0,  dstep));
        delta      -= waterWaves(pos + vec3(-dstep, 0.0, -dstep));

    return normalize(vec3(-delta.x, 2.0 * dstep, -delta.y));
}

/*
vec3 waterNormal() {
    vec3 offset[4] = vec3[4] ( 
            vec3(-1.0, 0.0, 0.0),
            vec3(1.0, 0.0, 0.0),
            vec3(0.0, 0.0, 1.0),
            vec3(0.0, 0.0, -1.0)
        );

    float deltaStep = 0.02 + linStep(viewDist, 16.0, 64.0)*0.4;

    vec3 pos        = worldPos;

    float hL        = waterWaves(pos+offset[0]*deltaStep);
    float hR        = waterWaves(pos+offset[1]*deltaStep);
    float hU        = waterWaves(pos+offset[2]*deltaStep);
    float hD        = waterWaves(pos+offset[3]*deltaStep);

    vec3 delta      = vec3(0.0, 0.0, 1.0);

        #ifdef water_normal_hq
            float h0    = waterWaves(pos);
            delta.x     = ((hL-h0)+(h0-hR))/deltaStep;
            delta.y     = ((hU-h0)+(h0-hD))/deltaStep;
        #else
            delta.x     = (hL-hR)/deltaStep;
            delta.y     = (hU-hD)/deltaStep;
        #endif

            delta.xy   *= 0.7;

        delta.z     = sqrt(1.0 - dot(delta.xy, delta.xy));

        //delta       = clamp(delta, -1, 1);

    return normalize(delta*tbn);
}*/
#endif

void main() {
    vec4 sceneColor   = texture(gcolor, coord[0]);
    if (sceneColor.a<0.02) discard;
        sceneColor.rgb *= tint.rgb;

    vec3 sceneNormal = normal;

    sceneColor.rgb  = toLinear(sceneColor.rgb);
    sceneColor.rgb  = linearToAP1Albedo(sceneColor.rgb);

    vec3 hue    = normalize(sceneColor.rgb);

    #ifdef gTERRAIN
        if (matID == 102) {
            //sceneColor.a *= 0.7;
            sceneColor = vec4(0.01, 0.03, 0.1, 0.11);
            hue      = vec3(1.0);
            sceneNormal = waterNormal();
        }
    #endif

    vec2 lmap       = coord[1];
    float ao        = tint.a;

    lmap.y          = cube(lmap.y);

    float diff      = diffuseLambert(normal, lightvec);

    shadowData shadow   = getShadowRegular(diff>0.0);

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

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

    #ifdef lightleakWorkaroundToggle
        directLight    *= sstep(coord[1].y, 0.1, 0.2);
    #endif

    vec3 indirectLight  = sqr(lmap.y)*lightColor[1];
        indirectLight  += lmap.y * directCol * 0.06;
        indirectLight  += vec3(0.5, 0.7, 1.0) * (0.01 * minLightLuma);
        indirectLight  *= ao;

    vec3 result     = directLight + indirectLight;
        result     += getBlocklightMap(lightColor[2], lmap.x)*ao;

    sceneColor.rgb *= result;

    /*DRAWBUFFERS:312*/
    gl_FragData[0]  = makeDrawbuffer(sceneColor.rgb, saturate(sceneColor.a));
    gl_FragData[1]  = vec4(encodeNormal(sceneNormal), encode2x8(coord[1]), 1.0);
    gl_FragData[2]  = vec4(encodeMatID16(matID), encode3x8(hue), 0.0, 1.0);
}