uniform sampler2D texture0;		// gdepth
uniform sampler2D rotMap;		// gnormals
uniform sampler2D normalTex;	// grandom

varying vec2 	texCoord1;
varying vec2 	texCoord;
varying vec2	VPOS;
uniform float 	contrast;
uniform float 	aorange;
uniform vec3 	shk[32];		// sphere kernel
uniform float 	aobias;
uniform mat4 	projTM;
uniform float 	aoscale;
uniform int  	numSamples;
uniform int	samplePerRay;
uniform float	farOffset;

vec4 decode(vec4 enc)
{
    vec2 fenc = enc.xy*4.0-2.0;
    float f = dot(fenc,fenc);
    float g = sqrt(1.0-f/4.0);
    vec4 n;
    n.xy = fenc.xy*g;
    n.z = 1.0-f/2.0;
	n.w=enc.w;
    return n;
}

float invAOrange;

vec2 getScreenPos(vec4 pos)
{
	vec4 ppos;
	vec2 spos;
		
	// compute texture space pos
	ppos=projTM*pos;
	ppos=ppos/ppos.w;
	spos=vec2(1.0)-ppos.xy;
	return spos;
}

vec3 getEyePos(vec2 coords)
{
	vec3 pos = texture2D(texture0, coords.xy).xyz;
	pos.xy=VPOS*-pos.z;
	return pos;
}

float testAO(in vec3 cPos, vec3 cNormal, vec3 sampleOfs) 
{
	// get sample pos
	vec3 samplePos;
	vec2 screenCoords;
	float sampleDepth;
	float depthDist;
	float dist;
	vec3 diff;
	float ofsSign=sign(dot(cNormal, sampleOfs));
	vec3 ofsInc=(sampleOfs/float(samplePerRay))*10.0*ofsSign;
	vec3 accSample=ofsInc;
	
	// raymarch occlusion 
	for(int i=0; i<samplePerRay; i++)
	{
		samplePos=cPos+(accSample/*ofsSign*/);
		
		// project into screen space
		screenCoords=getScreenPos(vec4(samplePos,1.0));
		
		// get depth from occluder screen coords
		sampleDepth = texture2D(texture0, screenCoords).x;
		
		// return 1 if sample is occluded by depthBuffer
		depthDist = sampleDepth - samplePos.z;
		if(depthDist>0.0)
		{
			// compute real distance
			diff=samplePos-getEyePos(screenCoords);
			dist=length(diff);
			
			// compute distance between sample and 
			return max(1.0 - pow(dist*invAOrange, aoscale), 0.0);
		}
		accSample+=ofsInc;
	}
	return 0.0;
}

void main(void)
{	
	vec3 centerPos=getEyePos(texCoord.xy);
	vec4 encoded=texture2D(normalTex,texCoord.st);
	vec4 centerNormal = decode(encoded);
	centerNormal.xyz=normalize(centerNormal.xyz);	
	centerNormal.xyz*=sign(dot(centerNormal.xyz,normalize(-centerPos))+0.2);// check if normal is opposed to view vector, in that case invert the normal (two sided trick)

	float ao=0.0;

	// create random rot matrix
	vec3 rotSample = texture2D(rotMap, texCoord1).rgb;
	rotSample = (2.0 * rotSample - 1.0);
	
	float occMul;
	vec3 vSample;
	vec4 prevCenterPos;
	vec2 reprojCoords;
	
	float farOffsetMin=farOffset-1500.0;
	if(-centerPos.z<farOffsetMin)
	{
		invAOrange=1.0/(aorange*0.3);
		for(int i=0; i<numSamples; i++)
		{
			vSample = reflect(shk[i], rotSample)*0.03;
			occMul=max(0.0,dot(normalize(vSample), centerNormal.xyz) - aobias);
			ao+=(testAO(centerPos,centerNormal.xyz, vSample*aorange)*occMul);
		}
	}
	else
	{
		float t=( clamp( (-centerPos.z-farOffsetMin)/(farOffset-farOffsetMin),0.0,1.0));
		float farRange=mix(aorange,aorange*10.0,t);
									
		invAOrange=1.0/(farRange*0.3);
		for(int i=0; i<numSamples; i++)
		{
			vSample = reflect(shk[i], rotSample)*0.03;
			occMul=max(0.0,dot(normalize(vSample), centerNormal.xyz) - aobias);
			ao+=(testAO(centerPos,centerNormal.xyz, vSample*farRange)*occMul);
		}
	}
	gl_FragColor = vec4(ao/float(numSamples));
}