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

    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"

in mat2x2 coord;

in vec4 tint;

flat in vec3 normal;

#ifdef vertexAttributeFix
    /* - */
#endif

#ifdef gTEXTURED
    uniform sampler2D gcolor;
    uniform sampler2D specular;

    #if (MC_VERSION >= 11500 && (defined gBLOCK || defined gENTITY || defined gHAND) && defined vertexAttributeFix)
        #ifdef normalmapEnabled
            #undef normalmapEnabled
        #endif
    #endif

    #ifdef normalmapEnabled
        uniform sampler2D normals;

        flat in mat3 tbn;

        vec3 decodeNormalTexture(vec3 ntex) {
            if(all(lessThan(ntex, vec3(0.003)))) return normal;
            
            vec3 nrm    = ntex * 2.0 - (254.0 * rcp(255.0));

            #if normalmapFormat==0
                nrm.z  = sqrt(saturate(1.0 - dot(nrm.xy, nrm.xy)));
            #elif normalmapFormat==1
                nrm    = normalize(nrm);
            #endif

            return normalize(nrm * tbn);
        }
    #endif
#endif

#ifdef gTERRAIN
flat in int foliage;
flat in int mat_id;
in vec3 worldPos;
#else
flat in int emitter;
#endif

#ifdef gENTITY
uniform vec4 entityColor;
#endif

uniform vec2 viewSize;
uniform vec2 taaOffset;

uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferModelView;

vec3 screenToViewSpace(vec3 screenpos, mat4 projInv, const bool taaAware) {
    screenpos   = screenpos*2.0-1.0;

    #ifdef taaEnabled
        if (taaAware) screenpos.xy -= taaOffset;
    #endif

    vec3 viewpos    = vec3(vec2(projInv[0].x, projInv[1].y)*screenpos.xy + projInv[3].xy, projInv[3].z);
        viewpos    /= projInv[2].w*screenpos.z + projInv[3].w;
    
    return viewpos;
}
vec3 screenToViewSpace(vec3 screenpos, mat4 projInv) {    
    return screenToViewSpace(screenpos, projInv, true);
}

vec3 screenToViewSpace(vec3 screenpos) {
    return screenToViewSpace(screenpos, gbufferProjectionInverse);
}

#ifdef directionalLMEnabled
    /* - */
#endif


#ifdef gTERRAIN

uniform sampler2D noisetex;

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

#define normalStep 0.002

uniform float frameTimeCounter;
uniform float wetness;
uniform float rainStrength;

float puddleWaves(vec2 pos) {
    float noise     = texture(noisetex, pos + vec2(frameTimeCounter) * vec2(0.3, 0.1)).z;
        noise      += texture(noisetex, pos * 1.8 + vec2(frameTimeCounter) * vec2(-0.1, -0.4)).z * 0.5;

    noise *= 0.5;

    return noise;
}

void getWetness(inout float wetnessOut, inout vec3 normalOut, inout vec2 material) {
    vec2 pos    = worldPos.xz * 0.02;

    float intensity = sstep(normal.y, 0.5, 0.9) * sstep(coord[1].y, 0.8, 0.95);

    if (min(wetness, intensity) <= 1e-2) return;

    float noise = texture(noisetex, pos).z;
        noise  += textureBicubic(noisetex, pos * 0.2).x * 0.5;
        noise  /= 1.5;
        //noise  += (1.0 - height) * 0.15;
        noise  -= saturate(1.0 - normal.y);
        noise  -= saturate(1.0 - sqrt(wetness) * 1.3) * 0.55;
        noise  *= intensity;

    vec3 flatNormal = normal;

    vec2 delta;
        delta.x     = puddleWaves(pos * 11.0 + vec2( normalStep, -normalStep));
        delta.y     = puddleWaves(pos * 11.0 + vec2(-normalStep,  normalStep));
        delta      -= puddleWaves(pos * 11.0 + vec2(-normalStep, -normalStep));

    flatNormal  = mix(flatNormal, normalize(vec3(-delta.x, 2.0 * normalStep, -delta.y)), 0.02 * sstep(noise, 0.55, 0.62) * rainStrength);

    normalOut   = mix(normalOut, flatNormal, sstep(noise, 0.5, 0.57));

    wetnessOut     = sqr(linStep(noise, 0.35, 0.57));

    material.x  = mix(material.x, 1.0, wetnessOut);
    material.y  = max(material.y, 0.04 * wetnessOut);
}

#endif

void main() {
    vec3 normalOut  = normal;
    vec2 lmap   = coord[1];
    vec2 materialData = vec2(0.0);
    vec2 wetnessData  = vec2(0.0);

    #ifdef gTEXTURED
        vec4 sceneColor   = texture(gcolor, coord[0]);
        if (sceneColor.a<0.1) discard;
            sceneColor.rgb *= tint.rgb;

        #ifdef normalmapEnabled
            normalOut   = decodeNormalTexture(texture(normals, coord[0]).rgb);
        #endif

        vec4 specularTex = texture(specular, coord[0]);

            materialData = specularTex.xy;
            wetnessData.y = specularTex.z;
        
        #ifdef gTERRAIN
            sceneColor.a  = tint.a;

            if (wetness > 1e-2) getWetness(wetnessData.x, normalOut, materialData);

            #if (defined normalmapEnabled && defined directionalLMEnabled)
            if(lmap.x > 0.001) {
                vec3 viewPos    = screenToViewSpace(vec3(gl_FragCoord.xy/viewSize, gl_FragCoord.z));

                vec3 dirLM_T    = normalize(dFdx(viewPos));
                vec3 dirLM_B    = normalize(dFdy(viewPos));
                vec3 dirLM_N    = cross(dirLM_T, dirLM_B);

                float dirLM     = 1.0;
                vec3 viewNormal = mat3(gbufferModelView) * normalOut;
                vec2 lmDeriv    = vec2(dFdx(lmap.x), dFdy(lmap.x)) * 256.0;
                vec3 lmDir      = normalize(vec3(lmDeriv.x * dirLM_T + 0.0005 * dirLM_N + lmDeriv.y * dirLM_B));

                float lmDiff    = clamp(dot(viewNormal, lmDir), -1.0, 1.0) * lmap.x;
                if (abs(lmDiff) > 0) lmDiff = pow(abs(lmDiff), mix(0.25, 1.0, lmap.x)) * sign(lmDiff);
                if (length(lmDeriv) > 0.001) dirLM = pow(lmap.x, 1.0 - lmDiff);
                lmap.x = mix(lmap.x, saturate(min(dirLM, lmap.x)), sqr(sstep(lmap.x, 0.125, 0.5)));
                //lmap.x = saturate(lmDiff);
            }
            #endif

        #else
            sceneColor.a  = 1.0;
        #endif

        #ifdef gENTITY
            sceneColor.rgb = mix(sceneColor.rgb, entityColor.rgb, entityColor.a);
        #endif
    #else
        vec4 sceneColor   = tint;
        if (sceneColor.a<0.01) discard;
            sceneColor.a  = 1.0;

        if (min3(sceneColor.rgb) < 0.01) lmap.xy = vec2(0.0);
    #endif

        sceneColor.rgb    = toLinear(sceneColor.rgb);

    #ifdef gTERRAIN
        int matID   = mat_id;
    #else
        #ifdef gNODIFF
        int matID = 2;
        #else
        int matID = 1;
        #endif

        if (emitter == 1) matID = 5;
    #endif

    //if (isnan3(sceneColor.rgb) || isinf3(sceneColor.rgb)) sceneColor.r = 100.0;

    /*DRAWBUFFERS:012*/
    gl_FragData[0]  = makeDrawbuffer(sceneColor.rgb, saturate(sceneColor.a));
    gl_FragData[1]  = vec4(encodeNormal(normalOut), encode2x8(lmap), 1.0);
    gl_FragData[2]  = vec4(encodeMatID16(matID), encode2x8(materialData), encode2x8(wetnessData), 0.0);
}