#define useNoise
#include "/lib/global.glsl"
#include "/lib/util/math.glsl"


/* ------ uniforms ------ */

uniform sampler2D colortex0;
uniform sampler2D colortex1;
uniform sampler2D colortex2;
uniform sampler2D colortex3;
uniform sampler2D colortex4;

uniform sampler2D depthtex1;

uniform sampler2D noisetex;

#ifdef fastShadows
    const bool shadowHardwareFiltering = true;

    uniform sampler2DShadow shadowtex0;
    uniform sampler2DShadow shadowtex1;

    uniform sampler2DShadow shadowcolor0;
#else
    uniform sampler2D shadowtex0;
    uniform sampler2D shadowtex1;
    uniform sampler2D shadowcolor0;
#endif

uniform mat4 shadowModelView;
//uniform mat4 shadowModelViewInverse;
uniform mat4 shadowProjection;
uniform mat4 shadowProjectionInverse;

uniform int frameCounter;
uniform int isEyeInWater;

uniform float far;
uniform float near;
uniform float aspectRatio;
uniform float frameTimeCounter;
uniform float viewWidth;
uniform float viewHeight;
uniform float rainStrength;
uniform float wetness;

uniform vec3 cameraPosition;

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


/* ------ inputs from vertex stage ------ */

in vec2 coord;

flat in vec3 sunVector;
flat in vec3 moonVector;
flat in vec3 lightVector;
flat in vec3 upVector;

flat in float timeNoon;


/* ------ structs ------ */

struct sceneData {
    vec3 albedo;
    vec3 normal;
    vec2 lightmap;
    vec4 sample2;
    vec4 sample3;
} scene;

struct depthData {
    float depth;
    float linear;
} depth;

struct positionData {
    vec3 camera;
    vec3 view;
    vec3 world;
} pos;

struct vectorData {
    vec3 sun;
    vec3 moon;
    vec3 light;
    vec3 up;
    vec3 view;
} vec;

struct shadingData {
    float diffuse;
    float specular;
    float shadow;
    float ao;
    float lit;
    float vanillaAo;

    vec3 shadowcolor;
    vec3 result;
    vec3 gi;
} sdata;

struct returnData {
    float directLight;
    float ao;
    float specular;

    vec3 indirect;
    vec3 shadowColor;
} rdata;


/* ------ includes ------ */

#include "/lib/util/decode.glsl"
#include "/lib/util/decodeIn.glsl"
#include "/lib/util/colorConversion.glsl"
#include "/lib/util/depth.glsl"
#include "/lib/util/positions.glsl"
#include "/lib/util/dither.glsl"
#include "/lib/util/taaJitter.glsl"
#include "/lib/util/encode.glsl"


/* ------ functions ------ */

vec3 unpackNormal(vec3 x) {
    return x*2.0-1.0;
}

#include "diffGGX.glsl"

#include "core.glsl"

#ifdef setAmbientOcclusion
    #include "/lib/shadow/dbao.glsl"
#endif

#ifdef s_vcShadow
    #include "/lib/noise/value.glsl"
    #include "/lib/nature/vc_common.glsl"
    #include "/lib/nature/vc_shadow.glsl"
#endif

void main() {
    scene.albedo    = texture(colortex0, coord).rgb;
    scene.normal    = unpackNormal(texture(colortex1, coord).rgb);
    scene.sample2   = texture(colortex2, coord);
    scene.lightmap  = scene.sample2.rg;
    scene.sample3   = texture(colortex3, coord);
    vec2 sample4    = texture(colortex4, coord).ra;

    decodeData();

    depth.depth     = texture(depthtex1, coord).x;
    depth.linear    = depthLin(depth.depth);

    pos.camera      = cameraPosition;
    pos.view        = getViewpos(depth.depth);
    pos.world       = toWorldpos(pos.view);

    vec.sun         = sunVector;
    vec.moon        = moonVector;
    vec.light       = lightVector;
    vec.up          = upVector;
    vec.view        = normalize(pos.view);

    sdata.shadow        = 1.0;
    sdata.diffuse       = 1.0;
    sdata.gi            = vec3(0.0);
    sdata.ao            = 1.0;
    sdata.specular      = 0.0;
    sdata.shadowcolor   = vec3(1.0);
    sdata.vanillaAo     = flatten(pow2(sample4.y), 0.85);

    if(mask.terrain) {
        float worldDistance = length(pos.world.xyz-pos.camera.xyz);
        float falloff       = 1.0-pow2(linStep(worldDistance, 100.0, 160.0));

        if (scene.sample3.r>0.26 && scene.sample3.r<0.28) sdata.diffuse = 1.0;
        else diffuseOrenNayar(scene.normal);

        getDirectLight(sdata.diffuse>0.01);

        sdata.lit       = min(sdata.shadow, sdata.diffuse)*sample4.x;

        #ifdef s_vcShadow
            #ifdef s_fakeGI
                float cloudshadow = vc_getShadow(pos.world, mat3(gbufferModelViewInverse)*vec.light, 2, 0.5)*0.8+0.2;
                sdata.lit *= cloudshadow*0.8+0.2;
                sdata.indirect *= cloudshadow;
            #else
                if (sdata.lit>0.0) {
                    float cloudshadow = vc_getShadow(pos.world, mat3(gbufferModelViewInverse)*vec.light, 2, 0.5);
                    sdata.lit *= cloudshadow*0.8+0.2;
                }
            #endif
        #endif

        if (sdata.lit>0.01) specGGX(scene.normal);
        sdata.specular *= sdata.lit;
        
        #ifdef setAmbientOcclusion
            dbao(falloff);
        #endif
    }

    rdata.directLight   = sdata.lit;
    rdata.ao            = sdata.ao*sdata.vanillaAo;
    rdata.indirect      = sdata.gi*0.3;
    rdata.specular      = sdata.specular;
    rdata.shadowColor   = saturate(sdata.shadowcolor);

    /*DRAWBUFFERS:345*/
    gl_FragData[0]  = max(vec4(scene.sample3.rgb, encodeV3(rdata.shadowColor)), 0.0);
    gl_FragData[1]  = vec4(rdata.indirect, rdata.ao);
    gl_FragData[2]  = max(vec4(rdata.directLight, rdata.specular/8.0, 0.0, 1.0), 0.0);
}