//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 		Original 3d depth shader code based off CryTech's 																			//
//		http://www.slideshare.net/TiagoAlexSousa/secrets-of-cryengine-3-graphics-technology											//
//		Special thanks to Jose Negrete AKA BlueSkyDefender for his SuperDepth3D implementation which started me down this path.		//
// 		Contact: sgsrules3dfixes@gmail.com																							//
//																																	//
//																																	//
//																																	//
// UPDATED by Helifax for 3D Vision usage																							//
// The original formula was incomplete. Based on the above link, the dynamic convergence was never used.							//					
// Contact: tavyhome@gmail.com																										//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#version 450

#define farClip 1.0
#define nearClip 0.01
// Tweak the depth map to make the close objects, like hands have depth
#define handDepthFactor 1.0
// Tweak the convergence formula for the blurring - hidding missing geome
#define blurFactor 0.15

in vec2 texCoord;
in vec4 clipcoord;
in vec4 minmax;
out vec4 fragColor;

uniform float isLeft;
uniform float g_eye_separation;
uniform float g_convergence;
uniform float pixelSize;
uniform float isVertFlipped;
uniform float linearisationMethod;
uniform sampler2D colorTex;
uniform sampler2D depthTex;

/*
/////////////////////////////////////////////////////////////
PROTOTYPES
/////////////////////////////////////////////////////////////
*/
float GetDepth(vec2 texcoord, float viewMode);
float RawDepth(vec2 texcoord,float viewMode);
float RawDepthInverse(vec2 texcoord,float viewMode);
float LinearizeDepth(vec2 texcoord,float viewMode);
float LinearizeDepthInverse(vec2 texcoord,float viewMode);
float LogarithmicDepth(vec2 texcoord,float viewMode);
float LogarithmicDepthInverse(vec2 texcoord,float viewMode);

/*
/////////////////////////////////////////////////////////////
STEREO PASS
/////////////////////////////////////////////////////////////
*/
vec4 StereoPass()
{
	vec2 stereoTexCoord = texCoord;
	float minDepthL = 1.0, minDepthR = 1.0;
	
	// Crytek's single pass stereo code
	// Extract Samples for BLUR
	float depth = GetDepth(texCoord, 0.0);
	float offset = 0;
	float s = 0;

	for (int j = 0; j < 3; j++) 
	{
		offset = g_eye_separation * (1.0 - g_convergence / depth) *s;
		s+= blurFactor;
		// Crytek's single pass stereo code
		minDepthL =  min(minDepthL,GetDepth(vec2(stereoTexCoord.x+offset, stereoTexCoord.y),0.0));
		minDepthR =  min(minDepthR,GetDepth(vec2(stereoTexCoord.x-offset, stereoTexCoord.y),0.0));
	}

	offset =  g_eye_separation * (1.0 - g_convergence / depth);
	
	// Left Eye
	minDepthL =  min(minDepthL,GetDepth(vec2(stereoTexCoord.x+offset, stereoTexCoord.y), -1.0));
	// Right Eye
	minDepthR =  min(minDepthR,GetDepth(vec2(stereoTexCoord.x-offset, stereoTexCoord.y), 1.0));

	minDepthL = max(g_eye_separation * (1 - g_convergence/minDepthL),-.0);
	minDepthR = max(g_eye_separation * (1 - g_convergence/minDepthR),-.0);

	if(isLeft>.5)
		stereoTexCoord.x+=minDepthL;
	else 
		stereoTexCoord.x-=minDepthR;
	
	vec4 color = texture2D(colorTex, stereoTexCoord);
	return color;
}
//-----------------------------------------------------------------------------

/*
/////////////////////////////////////////////////////////////
MAIN FUNCTION
/////////////////////////////////////////////////////////////
*/

void main()
{
	fragColor = StereoPass();
}
//-----------------------------------------------------------------------------

/*
/////////////////////////////////////////////////////////////
Helper Functions
/////////////////////////////////////////////////////////////
*/

// Get Depth:
// 0.0 Mono
// -1.0 Left
// 1.0 Right

float GetDepth(vec2 texcoord, float viewMode)
{
	// Select on how to liniearise the depth!
	if (linearisationMethod == 0.0)
		return RawDepth(texcoord, viewMode);
	else 
	if (linearisationMethod == 1.0)
		return LinearizeDepth(texcoord, viewMode);
	else 
	if (linearisationMethod == 2.0)
		return LogarithmicDepth(texcoord, viewMode);
	else 
	if (linearisationMethod == 3.0)
		return RawDepthInverse(texcoord, viewMode);
	else 
	if (linearisationMethod == 4.0)
		return LinearizeDepthInverse(texcoord, viewMode);
	else 
	if (linearisationMethod == 5.0)
		return LogarithmicDepthInverse(texcoord, viewMode);

	/* 
	Use the below code to use a different depth for close plane
	See the following video on how to use this: http://Youtube.com/bla
 	*/

/*
	float depth = RawDepth(texcoord, viewMode);;
	float logDepth = LogarithmicDepth(texcoord, viewMode);
	float linearDepth = LinearizeDepth(texcoord, viewMode);
	if (logDepth < nearClip && depth < 0.5)
	{
		// Separation of the HAND
		// We still need some accuracy in the depth map
		// So we "downgrade" the depth map to get meaningful values
		depth = depth * handDepthFactor;
	}
	else
	{
		depth = linearDepth;
	}
	return depth;
*/
}
//-----------------------------------------------------------------------------

float RawDepth(vec2 texcoord,float viewMode)
{
	// Flip on Y axis?
	if (isVertFlipped > 0.0)
		texcoord.y = 1 - texcoord.y;

	float depth = 0.0;
	if (viewMode == 0.0)
		depth = texture2D(depthTex, texcoord).x;
	else 
	if (viewMode == -1.0)
		depth = texture2D(depthTex, texcoord).y;
	else 
	if (viewMode == 1.0)
		depth = texture2D(depthTex, texcoord).z;

	return depth;
}
//-----------------------------------------------------------------------------

float RawDepthInverse(vec2 texcoord,float viewMode)
{
	return 1.0 - RawDepth(texcoord, viewMode);
}
//-----------------------------------------------------------------------------

float LinearizeDepth(vec2 texcoord,float viewMode)
{
	float depth = RawDepth(texcoord, viewMode);
	return 2.0 * nearClip * farClip / (farClip + nearClip - (2.0 * depth - 1.0) * (farClip - nearClip));
}
//-----------------------------------------------------------------------------

float LinearizeDepthInverse(vec2 texcoord,float viewMode)
{
	float depth = RawDepth(texcoord, viewMode);
	return 2.0 * farClip * nearClip / (nearClip + farClip - (2.0 * depth - 1.0) * (nearClip - farClip));
}
//-----------------------------------------------------------------------------

float LogarithmicDepth(vec2 texcoord,float viewMode)
{
	float depth = RawDepth(texcoord, viewMode);
	return (exp (pow (depth, 150 * pow (depth, 55) + 32.75f / pow(depth, 5) - 1850f * (pow ((1 - depth), 2)))) - 1) / (exp (depth) - 1);
}
//-----------------------------------------------------------------------------

float LogarithmicDepthInverse(vec2 texcoord,float viewMode)
{
	float depth = RawDepthInverse(texcoord, viewMode);
	return (exp (pow (depth, 150 * pow (depth, 55) + 32.75f / pow(depth, 5) - 1850f * (pow ((1 - depth), 2)))) - 1) / (exp (depth) - 1);
}
//-----------------------------------------------------------------------------