/********************************************************
    © 2020 Continuum Graphics LLC. All Rights Reserved
 ********************************************************/

#include "/../ContinuumLib/Syntax.glsl"

#define Composite0_glsl 30
#define DynamicShaderStage Composite0_glsl

varying vec2 texcoord;

flat varying vec3 wLightVector;
flat varying vec3 lightVector;
flat varying vec3 sunPositionNorm;
flat varying vec3 moonPositionNorm;
flat varying vec3 wSunVector;
flat varying vec3 wMoonVector;

varying vec3 sunIlluminanceClouds;
varying vec3 skyIlluminanceClouds;


/***********************************************************************/
#if defined vsh

uniform sampler3D colortex7;

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

uniform vec3 sunPosition;

uniform float eyeAltitude;
uniform int worldTime;

#include "/../ContinuumLib/Debug.glsl"
#include "/../ContinuumLib/Utilities.glsl"

#include "/../ContinuumLib/Uniform/ProjectionMatrices.glsl"
#include "/../ContinuumLib/Common/PrecomputedSky.glsl"

void main() {
    texcoord = gl_Vertex.xy * 4.0;

    gl_Position = vec4(gl_Vertex.xy * 2.0 - 1.0, 0.0, 1.0);

    #if defined WORLD0
    sunPositionNorm = sunPosition * 0.01;
    moonPositionNorm = -sunPosition * 0.01;
    wSunVector = mat3(gbufferModelViewInverse) * sunPositionNorm;
    wMoonVector = mat3(gbufferModelViewInverse) * moonPositionNorm;

    lightVector = (worldTime > 23075 || worldTime < 12925 ? sunPositionNorm : -sunPositionNorm);
    wLightVector = mat3(gbufferModelViewInverse) * lightVector;

    vec3 moon_irradiance = vec3(0.0);
    vec3 sky_irradiance_moon = vec3(0.0);

    sunIlluminanceClouds = SUN_SPECTRAL_RADIANCE_TO_LUMINANCE * GetSunAndSkyIrradiance(ATMOSPHERE, colortex7, colortex7, vec3(0.0, volumetric_cloudHeight / 1000.0 + ATMOSPHERE.bottom_radius, 0.0), vec3(0.0, 1.0, 0.0), wSunVector, wMoonVector, skyIlluminanceClouds, sky_irradiance_moon, moon_irradiance);
    moon_irradiance = MOON_SPECTRAL_RADIANCE_TO_LUMINANCE * moon_irradiance;
    sunIlluminanceClouds += moon_irradiance;

    skyIlluminanceClouds *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
    sky_irradiance_moon *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE_MOON;
    skyIlluminanceClouds += sky_irradiance_moon;
    #endif

    CalculateProjectionMatrices();
}

#endif
/***********************************************************************/



/***********************************************************************/
#if defined fsh

#include "/../ContinuumLib/Debug.glsl"

uniform sampler2D colortex2; //Material Data
uniform sampler2D colortex3; //Cloud Feedback Buffer (Doesn't work yet)

uniform sampler2D depthtex0;
uniform sampler2D depthtex1;

uniform sampler2D shadowtex1;
uniform sampler2D shadowcolor1;
uniform sampler2D shadowcolor;

uniform sampler2D noisetex;

uniform sampler3D colortex7;

uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferPreviousModelView;
uniform mat4 gbufferProjection;
uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferPreviousProjection;

uniform mat4 shadowProjection;
uniform mat4 shadowModelView;

uniform vec3 cameraPosition;
uniform vec3 previousCameraPosition;

uniform vec2 viewDimensions;
uniform vec2 pixelSize;
uniform vec2 taaJitter;

uniform float fov;
uniform float eyeAltitude;
uniform float frameTimeCounter;

#include "/ShaderConstants.glsl"

/* DRAWBUFFERS:03 */
layout (location = 0) out vec4 Buffer0;
layout (location = 1) out vec4 Buffer3;
#define LAYOUT_0 Buffer0
#include "/../ContinuumLib/Exit.glsl"

#include "/../ContinuumLib/Utilities.glsl"

#include "/../ContinuumLib/Uniform/ShadowMatrices.glsl"
#include "/../ContinuumLib/Uniform/ProjectionMatrices.glsl"
#include "/../ContinuumLib/Uniform/ShadowDistortion.glsl"

/*******************************************************************************
 - Sample Functions
******************************************************************************/

float GetDepth(vec2 coord) {
    return texture(depthtex1, coord).x;
}

/*******************************************************************************
 - Space Conversions
******************************************************************************/

vec3 CalculateViewSpacePosition(vec3 screenPos) {
    #ifdef TAA
        screenPos -= vec3(taaJitter, 0.0) * 0.5;
    #endif
    screenPos = screenPos * 2.0 - 1.0;

    return projMAD(projInverseMatrix, screenPos) / (screenPos.z * projInverseMatrix[2].w + projInverseMatrix[3].w);
}

vec3 CalculateViewSpacePositionUnjittered(vec3 screenPos) {
    screenPos = screenPos * 2.0 - 1.0;

    return projMAD(projInverseMatrix, screenPos) / (screenPos.z * projInverseMatrix[2].w + projInverseMatrix[3].w);
}

vec3 CalculateWorldSpacePosition(vec3 viewPos) {
    return mat3(gbufferModelViewInverse) * viewPos + gbufferModelViewInverse[3].xyz;
}

float ScreenToViewSpaceDepth(float p) {
    p = p * 2.0 - 1.0;
    vec2 x = projInverseMatrix[2].zw * p + projInverseMatrix[3].zw;

    return x.x / x.y;
}

vec2 CalculateCameraVelocity(vec3 screenPos, vec3 worldPos, float depth, out vec3 prevView) {
    vec3 projection = (depth < 1.0 ? (cameraPosition - previousCameraPosition) : vec3(0.0)) + worldPos;
         projection = mat3(gbufferPreviousModelView) * projection + gbufferPreviousModelView[3].xyz;
         prevView = projection;

         projection = (diagonal3(gbufferPreviousProjection) * projection + gbufferPreviousProjection[3].xyz) / -projection.z * 0.5 + 0.5;

    return (screenPos.xy - projection.xy);
}


/*******************************************************************************
 - Includes
******************************************************************************/

#include "/../InternalLib/Fragment/AmbientOcclusion.fsh"
#include "/../ContinuumLib/Common/PrecomputedSky.glsl"

#include "/../InternalLib/Fragment/VolumetricClouds.fsh"
#include "/../InternalLib/Fragment/GlobalIllumination.fsh"

#include "/../ContinuumLib/Common/MatID.glsl"

/*******************************************************************************
 - Main
******************************************************************************/

vec3 CalculateTemporalGiAccumulation(vec3 screenpos, float surfaceDepth, mat2x3 position, vec3 normal, float dither, float skyLightmap, bool isFoliage){
    float blendWeight = 0.95;
    
    vec3 GI = CalculateGI(position, normal, dither, skyLightmap, isFoliage);

    vec3 prevView = vec3(0.0);
    vec2 velocity = CalculateCameraVelocity(screenpos, position[1], screenpos.z, prevView);

    float linearDepth = -ScreenToViewSpaceDepth(screenpos.z);
    
    vec2 correctedCoord = (texcoord - velocity) * 0.25;
    vec2 coordCheck = correctedCoord * 4.0 - vec2(3.0, 2.0);
    vec2 scaledPixelSize = pixelSize * 4.0;

    blendWeight = clamp(coordCheck, scaledPixelSize, 1.0 - scaledPixelSize) != coordCheck || surfaceDepth < 0.7 ? 0.0 : blendWeight;

    vec2 coordNormal = ((texcoord - velocity) - vec2(1., 0.0)) * 0.25;

    vec4 prevFrame = texture(colortex3, correctedCoord);

    vec3 prevNormal = textureRaw(colortex3, coordNormal).rgb * 2.0 - 1.0;
    float prevLinearDepth = prevFrame.a;

    float sceneChange = exp(-abs(linearDepth - prevLinearDepth) / linearDepth * 12.0);
          sceneChange *= float(distance(normal, prevNormal) <= 1.0);

    return mix(GI, prevFrame.rgb, blendWeight * sceneChange);
}

void main() {
    /*
    This file calculates all the effects that has to be either 1/4th res or 1/2nd res.
    Todo:
    - Volumetric light (ground fog)
    - Volumetric clouds:
      * Since colortex3 is a non clearing buffer, i want to temporarily sample the clouds in this file!
        If someone can make it work, please do! Couldn't figure it out myself.
        This would mean a HUGE performance increase for the clouds!
    - Global Illumination
    */
    
    float dither = bayer64(gl_FragCoord.xy);

    vec2 scaledCoord0 = texcoord - vec2(2.0, 2.0);

    if (clamp01(scaledCoord0) == scaledCoord0) {
	
        float depth = textureRaw(depthtex1, scaledCoord0).x;

        vec3 viewPosition = CalculateViewSpacePositionUnjittered(vec3(scaledCoord0, depth));
        float normFactor = inversesqrt(dot(viewPosition, viewPosition));
        vec3 viewVector = normFactor * -viewPosition;

        if (depth >= 1.0) {
            Buffer3 = vec4(viewVector * 0.5 + 0.5, 1.0);

            exit();
            return;
        }

        vec4 materialData = textureRaw(colortex2, scaledCoord0);
        vec3 normal = mat3(gbufferModelView) * DecodeNormal(materialData.x);

        float ao = CalculateGTAO(scaledCoord0, viewPosition, normal, viewVector, dither);

        Buffer3 = vec4(normal * 0.5 + 0.5, ao);

        exit();
        return;
    }

    
    scaledCoord0 = texcoord - vec2(3.0, 2.0);

    if (clamp01(scaledCoord0) == scaledCoord0) {
        float depth = texture(depthtex1, scaledCoord0).x;
        if (depth >= 1.0) {
            Buffer3 = vec4(0.0, 0.0, 0.0, 1.0);

            exit();
            return;
        }

        #if !defined WORLD_1
        float depth0 = texture(depthtex0, scaledCoord0).x;

        mat2x3 position;
        position[0] = CalculateViewSpacePositionUnjittered(vec3(scaledCoord0, depth));
        position[1] = CalculateWorldSpacePosition(position[0]);

        vec4 materialData = textureRaw(colortex2, scaledCoord0);
        vec3 normal = mat3(gbufferModelView) * DecodeNormal(materialData.x);

        vec2 decode2z = Decode2x8(materialData.z);
        vec2 decode2y = Decode2x8(materialData.y);

        mat2x3 metalIOR = mat2x3(0.0);
        float materialID;
        float reflectivity;

        UnpackMaterialID(decode2y.y, materialID, reflectivity, metalIOR);

        float skyLightmap   = decode2z.y;

        bool isFoliage = materialID == 8.0;
        
        if (depth0 > 0.7)
        dither = fract(frameTimeCounter * (1.0 / 7.0) + dither);

        vec3 GI = CalculateTemporalGiAccumulation(vec3(scaledCoord0, depth), depth0, position, normal, dither, skyLightmap, isFoliage);
        #else
        vec3 GI = vec3(0.0);
        #endif

        Buffer3 = vec4(GI, -ScreenToViewSpaceDepth(depth));

        exit();
        return;
    }
    

    //Volumetric clouds
    /*
    vec2 scaledCoord1 = texcoord * 0.5 - vec2(0.0, 1.0);

    if (clamp01(scaledCoord1) == scaledCoord1) {
        float depth = texture(depthtex1, scaledCoord1).x;
        if (depth < 1.0) {
            Buffer3 = mix(vec4(0.0, 0.0, 0.0, 1.0), texture(colortex3, texcoord * 0.25), 0.95);

            exit();
            return;
        }

        mat2x3 position;
        position[0] = CalculateViewSpacePositionUnjittered(vec3(scaledCoord1, depth));
        position[1] = CalculateWorldSpacePosition(position[0]);

        float normFactor = inversesqrt(dot(position[0], position[0]));
        vec3 viewVector = normFactor * position[0];
        vec3 worldVector = mat3(gbufferModelViewInverse) * viewVector;

        vec3 kCamera = vec3(0.0, 0.05 + cameraPosition.y / 1000.0 + ATMOSPHERE.bottom_radius, 0.0);
        vec2 planetSphere = rsi(kCamera, worldVector, ATMOSPHERE.bottom_radius);

        float cloudTransmit = 1.0;
		vec3 cloud = vec3(0.0);

        float temporaloffset = fract(frameTimeCounter * 10.0);
		
		calculateVolumetricClouds(cloud, cloudTransmit, worldVector, wLightVector, position[1], planetSphere, temporaloffset, dot(worldVector, wSunVector), ATMOSPHERE.bottom_radius * 1000.0, VC_QUALITY, VC_SUNLIGHT_QUALITY, 1);

        vec3 prevView = vec3(0.0);
        vec2 velocity = CalculateCameraVelocity(vec3(scaledCoord1, depth), position[1], depth, prevView);

        float blendWeight = 0.95;

        vec4 clouds = vec4(cloud * 0.01, cloudTransmit);
        vec2 reprojectedCoord = (texcoord - velocity * 2.0) * 0.25;
        vec2 coordCheck = reprojectedCoord * 2.0 - vec2(0.0, 1.0);
        vec2 scaledPixelSize = pixelSize;

        blendWeight = clamp(coordCheck, scaledPixelSize, 1.0 - scaledPixelSize) != coordCheck ? 0.0 : blendWeight;

        vec4 previousFrame = texture(colortex3, reprojectedCoord);

        vec4 bufferOut = mix(clouds, previousFrame, blendWeight);

        Buffer3 = bufferOut;

        exit();
        return;
    }
    */

    exit();
    return;
}

#endif
/***********************************************************************/
