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

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

#define Composite1_glsl 31
#define DynamicShaderStage Composite1_glsl


flat varying vec3 lightVector;
flat varying vec3 wLightVector;

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

varying vec3 skyIlluminanceClouds;
varying vec3 sunIlluminanceClouds;

varying vec3 skyIlluminanceVert;
varying vec3 sunIlluminanceVert;

varying vec4 shR;
varying vec4 shG;
varying vec4 shB;

varying vec2 texcoord;

flat varying int isNight;


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

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

uniform mat4 shadowModelView;
uniform mat4 shadowModelViewInverse;

uniform vec3 sunPosition;
uniform vec3 shadowLightAngles;
uniform vec3 cameraPosition;

uniform sampler3D colortex7;

uniform float fov;
uniform float eyeAltitude;
uniform float wetness;

uniform int worldTime;

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

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

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

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

void CalculateSH(out vec4 shR, out vec4 shG, out vec4 shB) {
    shR = shZero();
    shG = shZero();
    shB = shZero();

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

    const float axisSampleCount = 8.0;
    
    // Accumulate coefficients according to surounding direction/color tuples.
    for (float az = 0.5; az < axisSampleCount; az += 1.0) {
        for (float ze = 0.5; ze < axisSampleCount; ze += 1.0) {
            vec3 rayDir = shGetUniformSphereSample(az / axisSampleCount, ze / axisSampleCount);

            vec3 transmit = vec3(0.0);
            vec3 SkyRadianceMoon = vec3(0.0);
            vec3 color = GetSkyRadiance(ATMOSPHERE, colortex7, colortex7, colortex7, kCamera, rayDir, 0.0, wSunVector, transmit, SkyRadianceMoon) * SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
            SkyRadianceMoon = SKY_SPECTRAL_RADIANCE_TO_LUMINANCE_MOON * SkyRadianceMoon;
            color += SkyRadianceMoon;

            vec4 sh = shEvaluateCosineLobe(rayDir);
            shR = shAdd(shR, shScale(sh, color.r));
            shG = shAdd(shG, shScale(sh, color.g));
            shB = shAdd(shB, shScale(sh, color.b));
        }
    }
    
    // integrating over a sphere so each sample has a weight of 4*PI/samplecount (uniform solid angle, for each sample)
    float shFactor = 4.0 * PI / (axisSampleCount * axisSampleCount);
    shR = shScale(shR, shFactor );
    shG = shScale(shG, shFactor );
    shB = shScale(shB, shFactor );

}

void main() {
    texcoord = gl_Vertex.xy;

    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 : moonPositionNorm);
        wLightVector = mat3(gbufferModelViewInverse) * lightVector;

        isNight = int(lightVector == moonPositionNorm);

        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;

        sunIlluminanceVert = SUN_SPECTRAL_RADIANCE_TO_LUMINANCE * GetSunAndSkyIrradiance(ATMOSPHERE, colortex7, colortex7, vec3(0.0, ATMOSPHERE.bottom_radius, 0.0), vec3(0.0, 1.0, 0.0), wSunVector, wMoonVector, skyIlluminanceVert, sky_irradiance_moon, moon_irradiance);
        moon_irradiance = MOON_SPECTRAL_RADIANCE_TO_LUMINANCE * moon_irradiance;
        sunIlluminanceVert += moon_irradiance;

        skyIlluminanceVert *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
        sky_irradiance_moon *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE_MOON;
        skyIlluminanceVert += sky_irradiance_moon;

        CalculateSH(shR, shG, shB);
    #endif

    CalculateProjectionMatrices();
    CalculateShadowMatrices();
}

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



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

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

uniform sampler2D colortex1; //Albedo
uniform sampler2D colortex2; //Material Data
uniform sampler2D colortex3; //Half Res Data

uniform sampler2D depthtex0;
uniform sampler2D depthtex1;

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

uniform sampler3D colortex7;

uniform sampler2D noisetex;

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

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

uniform vec3 cameraPosition;

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

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

uniform float near;
uniform float far;

const bool colortex3MipmapEnabled = true;

#include "/ShaderConstants.glsl"

/* DRAWBUFFERS:03 */
layout (location = 0) out vec4 Buffer0; //rgba8 Large Value Encode.
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"

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

float GetDepth(vec2 coord) {
    return texture(depthtex0, 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 CalculateViewSpacePosition(vec2 coord) {
    #ifdef TAA
        coord -= taaJitter * 0.5;
    #endif
    vec3 screenPos = vec3(coord, GetDepth(coord)) * 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;
}

vec3 ViewSpaceToScreenSpace(vec3 viewPos) {
    return ((projMAD(projMatrix, viewPos) / -viewPos.z)) * 0.5 + 0.5;
}

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

    return x.x / x.y;
}

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

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

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

#include "/../InternalLib/Fragment/DiffuseLighting.fsh"

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

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

vec3 cartToSphere(vec2 coord) {
    coord *= vec2(TAU, PI);
    vec2 lon = sincos(coord.x) * sin(coord.y);
    return vec3(lon.x, cos(coord.y), lon.y);
}

void CalculateSkySphere(vec2 coord, inout vec4 outBuffer){
    #if defined WORLD0
    if (any(greaterThan(coord, vec2(0.5 + pixelSize)))) return;
    coord *= 2.0;

    vec3 worldVector = cartToSphere(coord) * vec3(1.0, 1.0, -1.0);
    vec3 viewVector = mat3(gbufferModelView) * worldVector;

    vec3 SkyRadiance = vec3(0.0);
    vec3 skyCamera = vec3(0.0, 0.05 + cameraPosition.y / 1000.0 + ATMOSPHERE.bottom_radius, 0.0);

    vec3 transmit = vec3(0.0);
    vec3 SkyRadianceMoon = vec3(0.0);
    SkyRadiance = SKY_SPECTRAL_RADIANCE_TO_LUMINANCE * GetSkyRadiance(ATMOSPHERE, colortex7, colortex7, colortex7, skyCamera, worldVector, 0.0, wSunVector, transmit, SkyRadianceMoon);
    SkyRadianceMoon = SKY_SPECTRAL_RADIANCE_TO_LUMINANCE_MOON * SkyRadianceMoon;
    SkyRadiance += SkyRadianceMoon;

    SkyRadiance = calculateStars(SkyRadiance, worldVector, wMoonVector, transmit);

    #if defined VOLUMETRIC_CLOUDS || defined PLANET_SURFACE
        vec2 planetSphere = rsi(skyCamera, worldVector, ATMOSPHERE.bottom_radius);

        #ifdef PLANET_SURFACE
            if (planetSphere.y > 0.0) {
                vec3 planetSurfacePosition = skyCamera + worldVector * planetSphere.x;
                vec3 normal = normalize(planetSurfacePosition);

                // Planet Lighting
                vec3 skyIrradiance = vec3(0.0);
                vec3 moon_irradiance = vec3(0.0);
                vec3 sky_irradiance_moon = vec3(0.0);
                vec3 sunIrradiance = GetSunAndSkyIrradiance(ATMOSPHERE, colortex7, colortex7, planetSurfacePosition, normal, wSunVector, wMoonVector, skyIrradiance, sky_irradiance_moon, moon_irradiance) * SUN_SPECTRAL_RADIANCE_TO_LUMINANCE;
                moon_irradiance = MOON_SPECTRAL_RADIANCE_TO_LUMINANCE * moon_irradiance;
                sunIrradiance += moon_irradiance;

                skyIrradiance *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
                sky_irradiance_moon *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE_MOON;
                skyIrradiance += sky_irradiance_moon;

                const vec3 planetAlbedo = vec3(0.05, 0.1, 0.15);
                const float waterRoughness = 0.2;

                const float alpha = waterRoughness * waterRoughness;
                const float alpha2 = alpha * alpha;

                const float planetf0 = 0.0201;

                float NoL = clamp01(dot(normal, wLightVector));
                vec3 planet = sunIrradiance * sqrt(NoL) + skyIrradiance;
                     planet *= planetAlbedo * rPI;

                vec3 transmit;
                vec3 in_scatter_moon;
                vec3 in_scatter = GetSkyRadianceToPoint(ATMOSPHERE, colortex7, colortex7, colortex7, skyCamera, planetSurfacePosition, 0.0, wSunVector, transmit, in_scatter_moon) * SKY_SPECTRAL_RADIANCE_TO_LUMINANCE;
                in_scatter_moon *= SKY_SPECTRAL_RADIANCE_TO_LUMINANCE_MOON;
                in_scatter += in_scatter_moon;

                planet = planet * transmit + in_scatter;
                
                SkyRadiance = planet;
            }
		#endif

        float VoL = dot(worldVector, wLightVector);

        #ifdef PLANAR_CLOUDS
            SkyRadiance = calculatePlanarClouds(SkyRadiance, worldVector, wLightVector, planetSphere, 0.0, VoL, ATMOSPHERE.bottom_radius * 1000.0);
        #endif
        
        #ifdef VOLUMETRIC_CLOUDS
            float cloudTransmit = 1.0;
            vec3 cloud = vec3(0.0);
            calculateVolumetricClouds(cloud, cloudTransmit, worldVector, wLightVector, worldVector, planetSphere, 0.0, VoL, ATMOSPHERE.bottom_radius * 1000.0, 10, 10, 1);
            
            SkyRadiance = SkyRadiance * cloudTransmit + cloud;
	    #endif
    #endif

    outBuffer = EncodeRGBE8(max0(SkyRadiance));
    #else
    outBuffer = EncodeRGBE8(vec3(0.0));
    #endif
}

void main() {
    //This file takes in the opaque geometry and calculates diffuse lighting, composite1 will handle all front (Solid) to camera effects.
    //This file needs setup for rendering modes, forward, deferred, dual deferred, and blend mode.
	
	float depth = texture(depthtex1, texcoord).x;

    Buffer3 = texture(colortex3, texcoord);
    CalculateSkySphere(texcoord, Buffer3);
	
    vec4 albedo     = texture(colortex1, texcoord);
         albedo.rgb = srgbToLinear(albedo.rgb);

    vec4 materialData = textureRaw(colortex2, texcoord);

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

    mat2x3 metalIOR = mat2x3(0.0);
	
    float roughness     = decode2y.x;
    float torchLightmap = decode2z.x;
    float skyLightmap   = decode2z.y;
    float pomShadow     = decode2w.x;
    float materialID;
    float reflectivity;
    UnpackMaterialID(decode2y.y, materialID, reflectivity, metalIOR);

    bool isMetal = metalIOR[0].x > 0.0 || reflectivity >= 1.0;

    if (isMetal) {
        Buffer0 = EncodeRGBE8(albedo.rgb);
        exit();
        return;
    }

    bool isFoliage = materialID == 8.0;
    bool isLava    = materialID == 10.0;
    bool isHand    = materialID == 12.0;

    vec3 worldNormal = DecodeNormal(materialData.x);
    vec3 normal = mat3(gbufferModelView) * worldNormal;

    float dither = bayer64(gl_FragCoord.xy);
    #ifdef TAA
	    dither = fract(frameTimeCounter * (1.0 / 7.0) + dither);
    #endif

    mat2x3 position;
    position[0] = CalculateViewSpacePosition(vec3(texcoord, depth));
    position[1] = CalculateWorldSpacePosition(position[0]);

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

    vec3 shadows;
    vec3 diffuseLight;
    diffuseLight  = CalculateDiffuseLighting(position, vec3(texcoord, depth), albedo.rgb, normal, worldNormal, viewVector, depth, roughness, reflectivity, metalIOR, vec2(torchLightmap, skyLightmap), dither, pomShadow, isFoliage, isLava, isHand, shadows);
    #if defined WORLD0
    diffuseLight += CalculateSpecularHighlight(albedo.rgb, lightVector, normal, viewVector, shadows, roughness, reflectivity, metalIOR);
    #endif
    Buffer0 = EncodeRGBE8(max0(diffuseLight));
    
	exit();
}

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