#define DEFERRED0
#define PRECOMPUTED_ATMOSPHERIC_SCATTERING

layout(location = 0) out vec3 transmittanceOut;
layout(location = 1) out vec3 colortex5Out;

/* DRAWBUFFERS:45 */

flat in vec3 directIlluminance;
flat in vec3 skyIlluminance;

flat in vec3 sunIlluminance;
flat in vec3 moonIlluminance;

#include "/lib/Head/Common.inc"

uniform sampler2D noisetex;
uniform sampler3D colortex4;

uniform int moonPhase;
uniform sampler2D colortex15;
uniform sampler2D colortex13;
uniform sampler2D colortex14;
uniform sampler2D colortex12; 

uniform float nightVision;
uniform float viewWidth;
uniform float viewHeight;
uniform float wetness;
uniform float eyeAltitude;
uniform float isLightningFlashing;

uniform vec3 cameraPosition;
uniform vec3 worldSunVector;
uniform vec3 worldLightVector;	

uniform int frameCounter;
uniform float frameTimeCounter;

#ifdef IS_IRIS
    uniform int dimension;
#else
    uniform int worldTime;
    #define dimension (worldTime < 0 ? 1 : (worldTime > 24000 ? -1 : 0))
#endif

uniform vec2 screenPixelSize;
uniform vec2 screenSize;

#include "/lib/Head/Noise.inc"

#include "/lib/Atmosphere/Atmosphere.glsl"

#include "/lib/Atmosphere/VolumetricClouds.glsl"

#include "/lib/Atmosphere/PlanarClouds.glsl"

void GetPlanetCurvePosition(inout vec3 p) {
    p.y = length(p + vec3(0.0, planetRadius, 0.0)) - planetRadius;
}

vec3 RenderSkybox(in vec3 worldDir, in float dither, in CloudProperties cloudProperties) {
	vec4 cloudsData = vec4(0.0, 0.0, 0.0, 1.0);
	vec3 transmittance;
	vec3 skyRadiance = GetSkyRadiance(atmosphereModel, worldDir, worldSunVector, transmittance) * 20.0;

	float LdotV = dot(worldDir, worldLightVector);

	vec4 phases;
	phases.x = 	HenyeyGreensteinPhase(LdotV, cloudForwardG) 	  * 0.7  + HenyeyGreensteinPhase(LdotV, cloudBackwardG)		  * cloudBackwardWeight  	  + CornetteShanksPhase(LdotV, 0.9) * cloudProperties.cloudPeakWeight;
	phases.y = 	HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.7) * 0.35 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.7) * cloudBackwardWeight * 0.6 + CornetteShanksPhase(LdotV, 0.6) * cloudProperties.cloudPeakWeight * 0.5;
	phases.z = 	HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.5) * 0.17 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.5) * cloudBackwardWeight * 0.3 + CornetteShanksPhase(LdotV, 0.4) * cloudProperties.cloudPeakWeight * 0.2;
	phases.w = 	HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.3) * 0.08 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.3) * cloudBackwardWeight * 0.2 + CornetteShanksPhase(LdotV, 0.2) * cloudProperties.cloudPeakWeight * 0.1;

	vec3 planeOriginVol = vec3(0.0, planetRadius + eyeAltitude, 0.0);
	float noiseDetail = GetNoiseDetail(worldDir);

	#ifdef VOLUMETRIC_CLOUDS
		cloudsData = RenderCloudLayer(
			cloudProperties,
			worldDir,
			dither,
			phases,
			wind,
			skyRadiance,
			planeOriginVol,
			noiseDetail
		);

		#if CLOUD_LAYER2_ENABLED == 1
		{
			CloudProperties layer2Props = GetLayer2CloudProperties();
			vec4 phases2;
			phases2.x = HenyeyGreensteinPhase(LdotV, cloudForwardG) 	   * 0.65 + HenyeyGreensteinPhase(LdotV, cloudBackwardG) 		* cloudBackwardWeight 		+ CornetteShanksPhase(LdotV, 0.8) * layer2Props.cloudPeakWeight;
			phases2.y = HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.7) * 0.3  + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.7) * cloudBackwardWeight * 0.5 + CornetteShanksPhase(LdotV, 0.5) * layer2Props.cloudPeakWeight * 0.5;
			phases2.z = HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.5) * 0.15 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.5) * cloudBackwardWeight * 0.25 + CornetteShanksPhase(LdotV, 0.3) * layer2Props.cloudPeakWeight * 0.2;
			phases2.w = HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.3) * 0.07 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.3) * cloudBackwardWeight * 0.15 + CornetteShanksPhase(LdotV, 0.15) * layer2Props.cloudPeakWeight * 0.1;

			vec4 layer2Clouds = RenderCloudLayer(
				layer2Props,
				worldDir,
				0.5,
				phases2,
				wind2,
				skyRadiance,
				planeOriginVol,
				noiseDetail * 0.8
			);

			if (eyeAltitude < layer2Props.altitude) {
				cloudsData.rgb += layer2Clouds.rgb * cloudsData.a;
				cloudsData.a *= layer2Clouds.a;
			} else {
				cloudsData.rgb = cloudsData.rgb * layer2Clouds.a + layer2Clouds.rgb;
				cloudsData.a *= layer2Clouds.a;
			}
		}
		#endif

		#if CLOUD_LAYER3_ENABLED == 1
		{
			CloudProperties layer3Props = GetLayer3CloudProperties();
			vec4 phases3;
			phases3.x = HenyeyGreensteinPhase(LdotV, cloudForwardG) 	   * 0.6  + HenyeyGreensteinPhase(LdotV, cloudBackwardG) 		* cloudBackwardWeight 		+ CornetteShanksPhase(LdotV, 0.7) * layer3Props.cloudPeakWeight;
			phases3.y = HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.7) * 0.25 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.7) * cloudBackwardWeight * 0.4 + CornetteShanksPhase(LdotV, 0.4) * layer3Props.cloudPeakWeight * 0.4;
			phases3.z = HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.5) * 0.12 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.5) * cloudBackwardWeight * 0.2 + CornetteShanksPhase(LdotV, 0.25) * layer3Props.cloudPeakWeight * 0.15;
			phases3.w = HenyeyGreensteinPhase(LdotV, cloudForwardG * 0.3) * 0.05 + HenyeyGreensteinPhase(LdotV, cloudBackwardG * 0.3) * cloudBackwardWeight * 0.1 + CornetteShanksPhase(LdotV, 0.1) * layer3Props.cloudPeakWeight * 0.08;

			vec4 layer3Clouds = RenderCloudLayer(
				layer3Props,
				worldDir,
				0.5,
				phases3,
				wind3,
				skyRadiance,
				planeOriginVol,
				noiseDetail * 0.6
			);

			if (eyeAltitude < layer3Props.altitude) {
				cloudsData.rgb += layer3Clouds.rgb * cloudsData.a;
				cloudsData.a *= layer3Clouds.a;
			} else {
				cloudsData.rgb = cloudsData.rgb * layer3Clouds.a + layer3Clouds.rgb;
				cloudsData.a *= layer3Clouds.a;
			}
		}
		#endif
	#endif

	#ifdef PLANAR_CLOUDS
		vec3 planeOriginPlan = vec3(0.0, planetRadius + eyeAltitude, 0.0);
		bool intersectsGround = RaySphereIntersection(planeOriginPlan, worldDir, planetRadius).y >= 0.0;
		
		if ((worldDir.y > 0.0 && eyeAltitude < CLOUD_PLANE_ALTITUDE)
		|| (intersectsGround && eyeAltitude > CLOUD_PLANE_ALTITUDE)) {
			vec2 intersection = RaySphereIntersection(planeOriginPlan, worldDir, planetRadius + CLOUD_PLANE_ALTITUDE);
			float cloudDistance = eyeAltitude > CLOUD_PLANE_ALTITUDE ? intersection.x : intersection.y;

			if (cloudDistance > 0.0 && cloudDistance < planetRadius + CLOUD_PLANE_ALTITUDE) {
				vec3 cloudPos = worldDir * cloudDistance;

				#ifdef FULL_AERIAL_PERSPECTIVE
					vec3 airTransmittance;
					vec3 aerialPerspective = 20.0 * GetSkyRadianceToPoint(atmosphereModel, cloudPos, worldSunVector, airTransmittance);
				#endif
				cloudPos += cameraPosition;

				vec4 cloudsTemp = vec4(0.0, 0.0, 0.0, 1.0);

				#ifdef CIRROCUMULUS_CLOUDS
					#ifdef CLOUDS_WEATHER
						if (cloudDynamicWeather.x < 0.4)
					#endif
					{
						vec4 sampleTemp = PlanarSample1(cloudDistance, cloudPos.xz, LdotV, 0.5, phases, worldDir);

						if (sampleTemp.a > minTransmittance) {
							#ifdef FULL_AERIAL_PERSPECTIVE
								float atmosFade = expf(-cloudDistance * fma(0.025, wetness, 0.05) * 0.00015);
								sampleTemp.rgb *= airTransmittance;
								sampleTemp.rgb += aerialPerspective * sampleTemp.a;
							#else
								float atmosFade = expf(-cloudDistance * fma(0.05, wetness, 0.1) * 0.00015);
							#endif
							sampleTemp.rgb = sampleTemp.rgb * atmosFade + skyRadiance * sampleTemp.a * oneMinus(atmosFade);
						}

						cloudsTemp.rgb = sampleTemp.rgb;
						cloudsTemp.a -= sampleTemp.a;
					}
				#endif
				#if CIRRUS_CLOUDS > 0
					#ifdef CLOUDS_WEATHER
						if (cloudDynamicWeather.y < 0.5)
					#endif
					{
						vec4 sampleTemp = PlanarSample0(cloudDistance, cloudPos.xz, LdotV);

						if (sampleTemp.a > minTransmittance) {
							#ifdef FULL_AERIAL_PERSPECTIVE
								float atmosFade = expf(-cloudDistance * fma(0.025, wetness, 0.05) * 0.00015);
								sampleTemp.rgb *= airTransmittance;
								sampleTemp.rgb += aerialPerspective * sampleTemp.a;
							#else
								float atmosFade = expf(-cloudDistance * fma(0.05, wetness, 0.1) * 0.00015);
							#endif
							sampleTemp.rgb = sampleTemp.rgb * atmosFade + skyRadiance * sampleTemp.a * oneMinus(atmosFade);
						}

						cloudsTemp.rgb += sampleTemp.rgb * cloudsTemp.a;
						cloudsTemp.a *= 1.0 - sampleTemp.a;
					}
				#endif

				if (eyeAltitude < CLOUD_PLANE_ALTITUDE) {
					cloudsData.rgb += cloudsTemp.rgb * cloudsData.a;
				} else {
					cloudsData.rgb = cloudsData.rgb * cloudsTemp.a + cloudsTemp.rgb;
				}

				cloudsData.a *= cloudsTemp.a;
			}
		}
	#endif

	vec3 skyboxData = skyRadiance * cloudsData.a + cloudsData.rgb;

	vec3 sunmoon = RenderSun(worldDir, worldSunVector);
	sunmoon += RenderMoonReflection(worldDir, worldSunVector);

	skyboxData += sunmoon * remap(minTransmittance, 1.0, cloudsData.a) * transmittance;


	skyboxData += RenderPlanet(worldDir, worldSunVector);
	skyboxData += RenderPlanet2(worldDir, worldSunVector);
	return clamp16F(skyboxData);
}

void main() {
	if (int(gl_FragCoord.x) == skyCaptureRes.x) {
		switch (int(gl_FragCoord.y)) {
		case 0:
			colortex5Out = directIlluminance;
			break;

		case 1:
			colortex5Out = skyIlluminance;
			break;

		case 2:
			colortex5Out = sunIlluminance;
			break;

		case 3:
			colortex5Out = moonIlluminance;
			break;

		#ifdef CLOUDS_WEATHER
			case 5:
			colortex5Out = cloudDynamicWeather;
			break;
		#endif
		}
	} else if (gl_FragCoord.y < skyCaptureRes.y + 2.0) {
		vec3 worldDir = UnprojectSky(gl_FragCoord.xy * rcp(skyCaptureRes));
		colortex5Out = GetSkyRadiance(atmosphereModel, worldDir, worldSunVector, transmittanceOut) * 20.0;
	} else {
		vec3 worldDir = UnprojectSky(gl_FragCoord.xy * rcp(skyCaptureRes) - vec2(0.0, 1.0));
		
		ivec2 texel = ivec2(gl_FragCoord.xy);
		float dither = R1(frameCounter, texelFetch(noisetex, texel & 255, 0).a);
		
		colortex5Out = RenderSkybox(worldDir, dither, GetGlobalCloudProperties());
	}
}