#version 120
#extension GL_EXT_gpu_shader4 : enable




/*
const int gcolorFormat = R11F_G11F_B10F;
const int colortex1Format = R11F_G11F_B10F;
const int gnormalFormat = RGBA16F;
const int colortex3Format = RGBA8;
const int colortex4Format = R11F_G11F_B10F;
const int colortex5Format = R8;

*/
const bool colortex4MipmapEnabled = true;
const bool colortex4Clear = false;
const bool colortex5Clear = false;

const float		ambientOcclusionLevel	= 1.0; //[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.] Recommanded : 0.5 with ssao, 1. otherwise
const float	sunPathRotation	= -45.0f;	//[0. -5. -10. -15. -20. -25. -30. -35. -40. -45. -50. -55. -60. -70. -80. -90.]

	#define UNDERWATERFIX //fixes shadows and other stuff underwater
	//#define SSAO
	
	
/*






!! DO NOT REMOVE !! !! DO NOT REMOVE !!

This code is from Chocapic13' shaders
Read the terms of modification and sharing before changing something below please !
!! DO NOT REMOVE !! !! DO NOT REMOVE !!


Sharing and modification rules

Sharing a modified version of my shaders:
-You are not allowed to claim any of the code included varying "Chocapic13' shaders" as your own
-You can share a modified version of my shaders if you respect the following title scheme : " -Name of the shaderpack- (Chocapic13' Shaders edit) "
-You cannot use any monetizing links
-The rules of modification and sharing have to be same as the one here (copy paste all these rules varying your post), you cannot make your own rules
-I have to be clearly credited
-You cannot use any version older than "Chocapic13' Shaders V4" as a base, however you can modify older versions for personal use
-Common sense : if you want a feature from another shaderpack or want to use a piece of code found on the web, make sure the code is open source. varying doubt ask the creator.
-Common sense #2 : share your modification only if you think it adds something really useful to the shaderpack(not only 2-3 constants changed)


Special level of permission; with written permission from Chocapic13, if you think your shaderpack is an huge modification from the original (code wise, the look/performance is not taken varying account):
-Allows to use monetizing links
-Allows to create your own sharing rules
-Shaderpack name can be chosen
-Listed on Chocapic13' shaders official thread
-Chocapic13 still have to be clearly credited


Using this shaderpack varying a video or a picture:
-You are allowed to use this shaderpack for screenshots and videos if you give the shaderpack name varying the description/message
-You are allowed to use this shaderpack varying monetized videos if you respect the rule above.


Minecraft website:
-The download link must redirect to the link given varying the shaderpack's official thread
-You are not allowed to add any monetizing link to the shaderpack download

If you are not sure about what you are allowed to do or not, PM Chocapic13 on http://www.minecraftforum.net/
Not respecting these rules can and will result varying a request of thread/download shutdown to the host/administrator, with or without warning. Intellectual property stealing is punished by law.


*/


#define VIGNETTE
#define VIGNETTE_STRENGTH 1. 
#define VIGNETTE_START 0.1	//distance from the center of the screen where the vignette effect start (0-1)
#define VIGNETTE_END 1.25		//distance from the center of the screen where the vignette effect end (0-1), bigger than VIGNETTE_START



//////////////////////////////END OF ADJUSTABLE VARIABLES
//////////////////////////////END OF ADJUSTABLE VARIABLES
//////////////////////////////END OF ADJUSTABLE VARIABLES

/*--------------------------------*/

varying vec2 texcoord;

varying vec3 sunVec;
varying vec3 upVec;

varying vec3 sky1;
varying vec3 sky2;
varying vec3 nsunlight;

varying float SdotU;
varying float tempSample;




uniform sampler2D gnormal;
uniform sampler2D composite;
uniform sampler2D gcolor;
uniform sampler2D depthtex0;
uniform sampler2D colortex4;
uniform sampler2D noisetex;

uniform vec3 previousCameraPosition;
uniform vec3 sunPosition;
uniform vec3 cameraPosition;
uniform mat4 gbufferProjection;
uniform mat4 gbufferProjectionInverse;
uniform mat4 gbufferPreviousProjection;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferModelView;
uniform mat4 gbufferPreviousModelView;
uniform ivec2 eyeBrightnessSmooth;
uniform int isEyeInWater;
uniform int frameCounter;
uniform int worldTime;
uniform float aspectRatio;
uniform float near;
uniform float far;
uniform float rainStrength;
uniform float frameTimeCounter;
uniform float viewWidth;
uniform float viewHeight;
	float comp = 1.0-near/far/far;			//distances above that are considered as sky
	float tmult = mix(min(abs(worldTime-6000.0)/6000.0,1.0),2.4,rainStrength)*0.48+0.52;
#include "ToD.glsl"
#include "projUtil.glsl"
#include "skyATM.glsl"



vec3 ACESFilm( vec3 x )
{
    float a = 2.51f;
    float b = 0.03f;
    float c = 2.43f;
    float d = 0.59f;
    float e = 0.14f;
	vec3 r = (x*(a*x+b))/(x*(c*x+d)+e);

    return pow(r,vec3(1./2.233333333));
}



float distratio(vec2 pos, vec2 pos2) {
	return distance(pos*vec2(aspectRatio,1.0),pos2*vec2(aspectRatio,1.0));
}
									

#define diagonal3(m) vec3((m)[0].x, (m)[1].y, m[2].z)

#define  projMAD(m, v) (diagonal3(m) * (v) + (m)[3].xyz)
vec3 toClipSpace3(vec3 viewSpacePosition) {
    return projMAD(gbufferProjection, viewSpacePosition) / -viewSpacePosition.z * 0.5 + 0.5;
}

float cdist(vec2 coord) {
	vec2 vec = abs(coord*2.0-1.0);
	float d = max(vec.x,vec.y);
	return 1.0 - d*d;
}


 float ld(float depth) {
    return (2.0 * near) / (far + near - depth * (far - near));		// (-depth * (far - near)) = (2.0 * near)/ld - far - near
}

vec3 rayTrace(vec3 dir,vec3 position,float dither){

    const float quality = 25.;
    vec3 clipPosition = toClipSpace3(position);
    vec3 direction = normalize(toClipSpace3(position+dir)-clipPosition);  //convert to clip space
    direction.xy = normalize(direction.xy);
    
    //get at which length the ray intersects with the edge of the screen
    vec3 maxLengths = (step(0.,direction)-clipPosition) / direction;
    float mult = min(min(maxLengths.x,maxLengths.y),maxLengths.z);

    vec3 stepv = direction * mult / quality;
	vec3 spos = clipPosition + stepv*dither;
	
    for (int i = 0; i < int(quality); i++) {
        
        spos += stepv;

        if( texture2D(depthtex0,spos.xy).x <= spos.z ){
            spos -= stepv * .5;
            //refinements
            spos -= stepv * .25     * sign( spos.z - texture2D(depthtex0,spos.xy).x );
            spos -= stepv * .125    * sign( spos.z - texture2D(depthtex0,spos.xy).x );
            spos -= stepv * .0625   * sign( spos.z - texture2D(depthtex0,spos.xy).x );

            float rejectSample = texture2D(depthtex0,spos.xy).x;
            if( 
                abs(spos.z-rejectSample) < abs(stepv.z) + .001 / quality //not backface
            )
                return spos;
        }
    }

    return vec3(0.0);
}



vec3 decode (vec2 enc)
{
    vec2 fenc = enc*4-2;
    float f = dot(fenc,fenc);
    float g = sqrt(1-f/4.0);
    vec3 n;
    n.xy = fenc*g;
    n.z = 1-f/2;
    return n;
}
float nrand( vec2 n )
{
	return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453);
}
float BlueNoise(){
	vec2 nTC = mod(floor(gl_FragCoord.xy),32.);
	return fract(texelFetch2D(noisetex,ivec2(nTC),0).x+tempSample);

	
}

float triangWhiteNoise( vec2 n )
{
	//uses white noise for color dithering : gives a somewhat more "filmic" look when noise is visible
	float t = fract( frameTimeCounter );
	float rnd = nrand( n + 0.07*t );

    float center = rnd*2.0-1.0;
    rnd = center*inversesqrt(abs(center));
    rnd = max(-1.0,rnd); 
    return rnd-sign(center);
}
vec3 nvec3(vec4 pos) {
    return pos.xyz/pos.w;
}
vec2 nvec2(vec4 pos) {
    return pos.xy/pos.w;
}
float nvec1(vec4 pos) {
    return pos.z/pos.w;
}
vec4 nvec4(vec3 pos) {
    return vec4(pos.xyz, 1.0);
}

vec3 fpDither(vec3 color,float dither){
	const vec3 mantissaBits = vec3(6.,6.,5.);
	vec3 exponent = floor(log2(color));
	return color + dither*exp2(-mantissaBits)*exp2(exponent);
}
float bayer2(vec2 a){
	a = floor(a);
    return fract(dot(a,vec2(0.5,a.y*0.75)));
}
#define bayer4(a)   (bayer2( .5*(a))*.25+bayer2(a))
#define bayer8(a)   (bayer4( .5*(a))*.25+bayer2(a))
#define bayer16(a)  (bayer8( .5*(a))*.25+bayer2(a))
#define bayer32(a)  (bayer16(.5*(a))*.25+bayer2(a))
#define bayer64(a)  (bayer32(.5*(a))*.25+bayer2(a))
#define bayer128(a) fract(bayer64(.5*(a))*.25+bayer2(a)+tempSample)

//spiral sampling (numbers tweaked for 9 samples)
vec2 tapLocation(int sampleNumber, float spinAngle,int nb, float nbRot,float r0)
{
    float alpha = (float(sampleNumber*1.0f + r0) * (1.0 / (nb)));
    float angle = alpha * (nbRot * 6.28) + spinAngle;

    float ssR = alpha;
    float sin_v, cos_v;

	sin_v = sin(angle);
	cos_v = cos(angle);
	
    return vec2(cos_v, sin_v)*ssR;
}

void ssao(inout float occlusion,  vec2 tex,vec3 fragpos,float mulfov,float dither)
{
	const float tan70 = tan(70.*3.14/180.);
	float mulfov2 = gbufferProjection[1][1]/tan70; 

	const float PI = 3.14159265;
	const float samplingRadius = 0.712;
	vec2 radius = clamp(0.712/ (-fragpos.zz),6.0/vec2(viewWidth,viewHeight),vec2(0.5));
	float angle_thresh = 0.025;


	float noise2 = dither;
	float noise = noise2*(PI*2.);


	vec3 normal = normalize(cross(dFdx(fragpos),dFdy(fragpos)));
	vec2 rd = vec2(radius.x,radius.y*aspectRatio)*mulfov2*vec2(viewWidth,viewHeight);
	//pre-rotate direction
	float n =0.;

	occlusion = 0.0;
	ivec2 pos = ivec2(gl_FragCoord.xy);
	vec2 acc = 0.5/vec2(viewWidth,viewHeight);
	
	for (int i = 0; i < 9; i++){		
		vec2 sp = tapLocation(i,noise,9,2.,noise2);
		vec2 sampleOffset = sp*rd;	
		ivec2 offset = ivec2(gl_FragCoord.xy + sampleOffset); 
		if (offset.x < viewWidth && offset.y < viewHeight && offset.x > 0. && offset.y > 0.){
		vec3 t0 = toScreenSpace(vec3(offset/vec2(viewWidth,viewHeight)+acc,texelFetch2D(depthtex0,ivec2(offset),0).x));

		vec3 vec = t0.xyz*vec3(mulfov,mulfov,1.) - fragpos;
		float dsquared = dot(vec,vec);
		float NdotV = clamp(dot(vec*inversesqrt(max(dsquared,0.000001)), normal)-angle_thresh,0.,1.);

		occlusion += NdotV * samplingRadius/(samplingRadius+dsquared);
		n += 1.;
		}
		}

		occlusion = clamp(1.-pow(occlusion/max(n,1.),0.55)*1.1,0.,1.);

}
//////////////////////////////VOID MAIN//////////////////////////////
//////////////////////////////VOID MAIN//////////////////////////////
//////////////////////////////VOID MAIN//////////////////////////////
//////////////////////////////VOID MAIN//////////////////////////////
//////////////////////////////VOID MAIN//////////////////////////////
void main() {
	float mulfov = 1.0;
	#ifdef UNDERWATERFIX
		if (isEyeInWater>0.1){
			float fov = atan(gbufferProjectionInverse[1][1]);
			float fovUnderWater = fov*0.85;
			mulfov = gbufferProjection[1][1]*tan(fovUnderWater); 
		}
	#endif


	vec3 color = texture2D(gcolor,texcoord).xyz/100.;
	float Depth = texture2D(depthtex0, texcoord).x;

	bool land = Depth < comp;



	float sunInt = sunIntensity(SdotU);
	float moonInt = moonIntensity(SdotU);
	vec3 fragpos = toScreenSpace(vec3(texcoord,Depth));
	fragpos.xy *= mulfov;
	vec3 screenVec = normalize(fragpos.xyz);
		if (land){
		vec3 normal = texture2D(gnormal,texcoord,0).xyz;
		bool reflective = dot(normal,normal) > 0.0;
		
		#ifdef SSAO
		float occlusion = 1.0;
		ssao(occlusion,texcoord,fragpos.xyz,mulfov,BlueNoise());
		color = color*vec3(occlusion);
		#endif
		
		if (reflective) {
	
			float sky = normal.z;
			float sunVis = 1.0;

			bool iswater = sky < 0.2499;
			bool isice = sky > 0.2499 && sky < 0.4999;

			if (iswater) sky *= 4.0;
			if (isice) sky = (sky - 0.25)*4.0;

			if (!iswater && !isice) sky = (sky - 0.5)*4.0;

			sky = max(sky-2./16.0,0.0)*1.14285714286;
			sky *= sky;

			normal = decode(normal.xy);




			vec3 reflectedVector = reflect(normalize(fragpos.xyz), normal);
			vec3 hV= normalize(normalize(reflectedVector) + normalize(-fragpos.xyz));

			float normalDotEye = dot(hV, normalize(fragpos.xyz));

			float F0 = iswater? 0.02 : 0.04;
			F0 = isice? 0.017 : 0.04;



			float fresnel = pow(clamp(1.0 + normalDotEye,0.0,1.0), 5.0) ;
			fresnel = fresnel+F0*(1.0-fresnel);

			float a = 1.0;
			float b = dot(fragpos.xyz,normalize(reflectedVector)*2.);
			float C = dot(fragpos.xyz,fragpos.xyz) - far*far*12.;
			float delta = b*b - 4*a*C;
			float K = (-b + sqrt(delta)) / 2.;
				
			vec3 skyIntersection = normalize(fragpos.xyz + normalize(reflectedVector)*K);
			vec3 sky_c = getSkyColor(skyIntersection,sunInt,moonInt,dot(skyIntersection,upVec));



				vec3 rtPos = rayTrace(reflectedVector,fragpos.xyz,0.5);
			
				
				vec4 reflection = vec4(sky_c,0.);
				if (rtPos.z > 0.){
				vec4 fragpositionPrev = gbufferProjectionInverse * vec4(rtPos*2.-1.,1.);
				fragpositionPrev /= fragpositionPrev.w;
				
				vec3 sampleP = fragpositionPrev.xyz;
				fragpositionPrev = gbufferModelViewInverse * fragpositionPrev;


			
				vec4 previousPosition = fragpositionPrev + vec4(cameraPosition-previousCameraPosition,0.);
				previousPosition = gbufferPreviousModelView * previousPosition;
				previousPosition = gbufferPreviousProjection * previousPosition;
				previousPosition.xy = previousPosition.xy/previousPosition.w*0.5+0.5;
				reflection.a = clamp(1.0 - pow(1.0-cdist(previousPosition.st), 20.0), 0.0, 1.0);
				reflection.rgb = texture2DLod(colortex4,previousPosition.xy,1).rgb/100.;
				}

			reflection.rgb = mix(sky_c*sky,reflection.rgb,reflection.a);
			color = mix(color,reflection.rgb,fresnel);

			
		}	





	}
	
		vec3 fogC = getFogColor(screenVec,dot(screenVec,upVec));
		color = calcAtmFog(fogC,fragpos.xyz,color,sunInt,moonInt);


/* DRAWBUFFERS:04 */
	gl_FragData[0] = vec4(fpDither((color)*100.,triangWhiteNoise(texcoord)),1.);
	gl_FragData[1] = texture2D(colortex4,texcoord);
	
}