//------------------------------------------------------------------------
//			Filename: 	dsGeometryBasic.fx
//			Author:		Brian Arpidone
//			Modified:	6/03/2011
//			Created:	5/24/2011
//			Summary:	Basic geometry shader for deferred shading
//------------------------------------------------------------------------

#include "dsPool.fxh"

//Defined structure for cleaner pow() calls
#define gFloat4 float4(1.0f/g_fGamma, 1.0f/g_fGamma, 1.0f/g_fGamma, 1.0f)

// Globals
float		g_fPanU;
float		g_fPanV;

// Matrices
float4x4 	g_mWorld;
float4x4	g_mInvTransWorld;

// Textures
texture2D	g_tTextureMap;
texture2D	g_tSpecMap;
texture2D	g_tNormMap;
texture2D	g_tGlowMap;

//------------------------------------------------------------------------
// BUILD PASS STRUCTS
//------------------------------------------------------------------------

struct InputVS_Depth
{
	float3 vPos			: POSITION0;
	float3 vNorm		: NORMAL0;
	float2 vTex0		: TEXCOORD0;
};

struct OutputVS_Depth
{
	float4 vWorldPos	: POSITION0;
	float2 vTex0		: TEXCOORD0;
	float2 vDepthCoords	: TEXCOORD1;
};

struct OutputPS_Depth
{
	float4 vDepth		: COLOR0;
};

struct ForwardPS_Depth
{
	float4 noColor		: COLOR0;
	float4 glow			: COLOR1;
	float4 vDepth		: COLOR2;
};

struct InputVS_Build
{
	float3 vPos			: POSITION0;
	float3 vNorm		: NORMAL0;
	float2 vTex0		: TEXCOORD0;
	float4 vTangent		: TANGENT0;
	float4 vBitangent	: BINORMAL0;
};

struct OutputVS_Build
{
	float4	vPos		: POSITION0;
	float2  vTex0		: TEXCOORD0;
	float4	vWorldNrm	: TEXCOORD1;	
	float4 	vTangent	: TEXCOORD2;
	float4 	vBitangent	: TEXCOORD3;
	float2	vDepthCrds	: TEXCOORD4;
};

struct OutputPS_Build
{
	float4 	normal 		: COLOR0;
	float4 	diffuse 	: COLOR1;
	float4 	material 	: COLOR2;
	float4	glow		: COLOR3;
};

struct OutputPS_Forward
{
	float4 color		: COLOR0;
	float4 glow			: COLOR1;
};

// Object Texture Sampler
sampler	TextureMap = sampler_state
{
	Texture = <g_tTextureMap>;
	// texture filtering
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	// texture address modes (available options are WRAP, MIRROR, BORDER, and CLAMP)
	AddressU  = WRAP;
	AddressV  = WRAP;
};

sampler	SpecularMap = sampler_state
{
	Texture = <g_tSpecMap>;
	// texture filtering
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	// texture address modes (available options are WRAP, MIRROR, BORDER, and CLAMP)
	AddressU  = WRAP;
	AddressV  = WRAP;
};

sampler	NormalMap = sampler_state
{
	Texture = <g_tNormMap>;
	// texture filtering
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	// texture address modes (available options are WRAP, MIRROR, BORDER, and CLAMP)
	AddressU  = WRAP;
	AddressV  = WRAP;
};

sampler	GlowMap = sampler_state
{
	Texture = <g_tGlowMap>;
	// texture filtering
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
	// texture address modes (available options are WRAP, MIRROR, BORDER, and CLAMP)
	AddressU  = WRAP;
	AddressV  = WRAP;
};

//------------------------------------------------------------------------
// DEPTH PASS PROGRAMS
//------------------------------------------------------------------------
// Vertex Program
OutputVS_Depth DeferredVSDepth( InputVS_Depth i )
{
	OutputVS_Depth o = (OutputVS_Depth)0;
	
	//Transform vertex coordinates to world space
	float4 worldPos = mul(float4(i.vPos, 1.0f), g_mWorld);
	float4 vWVPos = mul(worldPos, g_mViewProj);
	
	//Pass texture coordinates modified by panning values
	o.vTex0 = i.vTex0 + float2( g_fPanU, g_fPanV );
	
	o.vWorldPos = vWVPos;
	o.vDepthCoords.xy = o.vWorldPos.zw;
	
	return o;
}

// Fragment Program
OutputPS_Depth DeferredPSDepth( OutputVS_Depth vs )
{
	//Output depth values for lighting
	OutputPS_Depth o = (OutputPS_Depth)0;
	o.vDepth = (vs.vDepthCoords.x / vs.vDepthCoords.y);
	
	return o;
}

//------------------------------------------------------------------------
// BUILD PASS PROGRAMS
//------------------------------------------------------------------------

//Normal mapping transform function
float4 CalculateWorldFromTangentNormal( float4 tanNorm, float4 normal, float4 tangent, float4 bitangent )
{
	float3x3 mat = { 	normalize(tangent.xyz),
						normalize(bitangent.xyz),
						normalize(normal.xyz) };
						
	return float4(normalize(mul( tanNorm.xyz, mat )), 0.0f);
}

OutputVS_Build DeferredVSBuild( InputVS_Build i )
{
	OutputVS_Build o = (OutputVS_Build)0;
	
	//Pass TexCoords
	o.vTex0 = i.vTex0 + float2(g_fPanU, g_fPanV);
	
	//Translate normals into world space
	o.vWorldNrm = normalize(mul(float4(i.vNorm, 0.0f), g_mWorld));
	o.vTangent = normalize(mul(float4(i.vTangent.xyz, 0.0f), g_mWorld));
	o.vBitangent = normalize(mul(float4(i.vBitangent.xyz, 0.0f), g_mWorld));
	
	//Translate position into MVP-space.  I hate doing this twice.
	float4 worldPos = mul(float4(i.vPos, 1.0f), g_mWorld);
	float4 vWVPos = mul( worldPos, g_mViewProj );
	o.vPos = vWVPos;
	
	//Depth for transparencies, to prevent fogging artifacts
	o.vDepthCrds.xy = o.vPos.zw;
	
	return o;
}

// Fragment Program

//Standard fragment program for opaque objects
OutputPS_Build DeferredPSBuild( OutputVS_Build vs )
{
	OutputPS_Build o = (OutputPS_Build)0;
	
	o.diffuse = tex2D( TextureMap, vs.vTex0 );
	
	//Sampling normal map and transforming out of color space
	float4 tanNormal = (2.0f * tex2D( NormalMap, vs.vTex0 )) - 1.0f;
	float4 worldNorm = CalculateWorldFromTangentNormal( tanNormal, vs.vWorldNrm, vs.vTangent, vs.vBitangent );
	
	//Collapse back into color space
	o.normal.rgb = normalize(0.5f * (worldNorm.xyz + 1.0f));
	
	o.material = tex2D( SpecularMap, vs.vTex0 );
	
	o.glow = tex2D( GlowMap, vs.vTex0 );
	o.glow.rgb *= o.glow.a;
	o.glow.a = 1.0f;
	
	return o;
}

//Opaque objects without normal maps
OutputPS_Build DeferredPSBuildNoNorm( OutputVS_Build vs )
{
	OutputPS_Build o = (OutputPS_Build)0;
	
	o.diffuse = tex2D( TextureMap, vs.vTex0 );
	
	o.normal.rgb = normalize(0.5f * (vs.vWorldNrm.xyz + 1.0f));
	
	o.material = tex2D( SpecularMap, vs.vTex0 );
	
	o.glow = tex2D( GlowMap, vs.vTex0 );
	o.glow.rgb *= o.glow.a;
	o.glow.a = 1.0f;
	
	return o;
}

//Transparency shaders.  Since transparent objects are rendered outside of the deferred passes, 
//depth values must be printed.  Lighting is calculated before this pass, so modifying the depth buffer
//will only affect the depth fog postprocess.
ForwardPS_Depth ForwardPSDepth( OutputVS_Depth vs )
{
	float4 color = pow( abs(tex2D( TextureMap, vs.vTex0 )), gFloat4 );
	
	if( color.a <= 0.0 )
		discard;
	
	ForwardPS_Depth o = (ForwardPS_Depth)0;
	
	o.noColor = pow(abs(color * g_fScreenAlpha), gFloat4);
	o.vDepth.r = (vs.vDepthCoords.x / vs.vDepthCoords.y);
	
	return o;
}

OutputPS_Forward ForwardPS( OutputVS_Build vs )
{
	OutputPS_Forward o = (OutputPS_Forward)0;
	
	float4 texColor = tex2D(TextureMap, vs.vTex0);
	texColor.rgb = pow(abs(texColor.rgb * g_fScreenAlpha), (float3)gFloat4);
	o.color = texColor;
	if( o.color.a <= 0.0f )
		discard;
	
	o.glow	= tex2D( GlowMap, vs.vTex0 );
	o.glow.rgb *= o.glow.a;
	o.glow.a = 1.0f;
	
	return o;
}

technique depthPass
{
	pass p0
	{
		VertexShader =	compile vs_3_0 DeferredVSDepth();
		PixelShader =	compile ps_3_0 DeferredPSDepth();
		
		CullMode = ccw;
		FillMode = solid;
		Zenable = true;
		ZWriteEnable = true;
		ZFunc = less;
		StencilEnable = false;
		AlphaBlendEnable = false;
		AlphaTestEnable = false;
		ColorWriteEnable = red;
	}
}

technique buildPass
{
	pass p0
	{
		VertexShader = 	compile vs_3_0 DeferredVSBuild();
		PixelShader = 	compile ps_3_0 DeferredPSBuild();
		
		CullMode = ccw;
		FillMode = solid;
		Zenable = true;
		ZWriteEnable = false;
		StencilEnable = false;
		AlphaBlendEnable = false;
		AlphaTestEnable = false;		
		ColorWriteEnable = red | green | blue;
	}
}

technique noNormalMap
{
	pass p0
	{
		VertexShader = 	compile vs_3_0 DeferredVSBuild();
		PixelShader = 	compile ps_3_0 DeferredPSBuildNoNorm();
		
		CullMode = ccw;
		FillMode = solid;
		Zenable = true;
		ZWriteEnable = false;
		StencilEnable = false;
		AlphaBlendEnable = false;
		AlphaTestEnable = false;		
		ColorWriteEnable = red | green | blue;
	}
}

technique forwardPass
{
	pass p0
	{
		VertexShader = 	compile vs_3_0 DeferredVSDepth();
		PixelShader =	compile ps_3_0 ForwardPSDepth();
		
		CullMode = ccw;
		FillMode = solid;
		Zenable = true;
		ZWriteEnable = false;
		StencilEnable = false;
		
		AlphaBlendEnable = true;
		SrcBlend = SrcAlpha;
		DestBlend = InvSrcAlpha;
		
		AlphaTestEnable = false;
		ColorWriteEnable = red | green | blue;
	}
	
	pass p1
	{
		VertexShader = 	compile vs_3_0 DeferredVSBuild();
		PixelShader = 	compile ps_3_0 ForwardPS();
		
		CullMode = cw;
		FillMode = solid;
		Zenable = true;
		ZWriteEnable = false;
		StencilEnable = false;
		AlphaBlendEnable = true;
		SrcBlend = SrcAlpha;
		DestBlend = InvSrcAlpha;
		AlphaTestEnable = false;
		ColorWriteEnable = red | green | blue;
	}
}