///////////////////////////////////////////////////////////////////////  
//  Core.fx
//
//	This file contains most of the common functions used by the SpeedTree
//	vertex and pixel shaders.  These shaders will compile with HLSL and Cg compilers.
//
//
//	*** INTERACTIVE DATA VISUALIZATION (IDV) CONFIDENTIAL AND PROPRIETARY INFORMATION ***
//
//	This software is supplied under the terms of a license agreement or
//	nondisclosure agreement with Interactive Data Visualization, Inc. and
//  may not be copied, disclosed, or exploited except in accordance with 
//  the terms of that agreement.
//
//      Copyright (c) 2003-2010 IDV, Inc.
//      All rights reserved in all media.
//
//      IDV, Inc.
//      Web: http://www.idvinc.com


#ifdef SPEEDTREE_SHADOWS_ENABLED

///////////////////////////////////////////////////////////////////////  
//  FadeByDepth

float FadeByDepth(float fValue, float fDepth, float fStart, float fFadeFactor)
{
	float fFade = saturate((fDepth - fStart) * fFadeFactor);

	return lerp(fValue, 1.0f, fFade);
}


///////////////////////////////////////////////////////////////////////  
//  SampleShadowMap

//#define SPEEDTREE_8_TAP_FILTER

#ifdef SPEEDTREE_DIRECTX10
	inline float SampleShadowMap(Texture2D tShadowMap, SamplerComparisonState samShadowMap, float4 vPosInLightSpace, bool bSmoothingFilter)
#else
	inline float SampleShadowMap(texture tShadowMap, sampler2D samShadowMap, float4 vPosInLightSpace, bool bSmoothingFilter)
#endif
{
#ifndef SPEEDTREE_XBOX_360
	float fSample;

	if (bSmoothingFilter)
	{
		// filter offset table
		const float c_fOffset = 1.0 / SPEEDTREE_SHADOW_MAP_RES;
		float4 avOffsets[7] =
		{
			float4(0.0, c_fOffset, 0.0, 0.0),
			float4(-c_fOffset, -c_fOffset, 0.0, 0.0),
			float4(-c_fOffset, 0.0, 0.0, 0.0),
			float4(c_fOffset, c_fOffset, 0.0, 0.0),
			float4(c_fOffset, 0.0, 0.0, 0.0),
			float4(0.0, -c_fOffset, 0.0, 0.0),
			float4(-c_fOffset, c_fOffset, 0.0, 0.0)
		};

		#ifdef SPEEDTREE_8_TAP_FILTER
			float4 vSum0;
			vSum0.x = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace);
			vSum0.y = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[0]);
			vSum0.z = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[1]);
			vSum0.w = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[2]);

			float4 vSum1;
			vSum1.x = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[3]);
			vSum1.y = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[4]);
			vSum1.z = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[5]);
			vSum1.w = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[6]);

			fSample = dot(vSum0 + vSum1, 1.0 / 8.0);
		#else
			float4 vSum0;
			vSum0.x = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace);
			vSum0.y = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[0]);
			vSum0.z = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[1]);
			vSum0.w = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace + avOffsets[2]);

			fSample = dot(vSum0, 1.0 / 4.0);
		#endif
	}
	else
	{
		fSample = SampleTextureCompare(tShadowMap, samShadowMap, vPosInLightSpace);
	}

	#ifdef SPEEDTREE_PS3
		return 1.0f - fSample;
	#else
		return fSample;
	#endif

#else // !SPEEDTREE_XBOX_360

	// use a 4-tap bilinear lookup

	// note: bSmoothingFilter is ignored on 360 version
	float4 vWeights;
	float fLOD;
	float4 vSampledDepth;
	asm 
	{
		tfetch2D vSampledDepth.x___, vPosInLightSpace.xy, samShadowMap, OffsetX = -0.5, OffsetY = -0.5
		tfetch2D vSampledDepth._x__, vPosInLightSpace.xy, samShadowMap, OffsetX =  0.5, OffsetY = -0.5
		tfetch2D vSampledDepth.__x_, vPosInLightSpace.xy, samShadowMap, OffsetX = -0.5, OffsetY =  0.5
		tfetch2D vSampledDepth.___x, vPosInLightSpace.xy, samShadowMap, OffsetX =  0.5, OffsetY =  0.5
	
		getWeights2D vWeights, vPosInLightSpace.xy, samShadowMap, MagFilter=linear, MinFilter=linear
	};

	vWeights = float4((1.0 - vWeights.x) * (1.0 - vWeights.y), vWeights.x * (1.0 - vWeights.y), (1.0 - vWeights.x) * vWeights.y, vWeights.x * vWeights.y);
	float4 vAttenuation = step(vPosInLightSpace.z, vSampledDepth);
    
	return dot(vAttenuation, vWeights);

#endif // !SPEEDTREE_XBOX_360
}


///////////////////////////////////////////////////////////////////////  
//  ShadowMapLookup

float ShadowMapLookup(float4 vPosInLightSpace[SPEEDTREE_NUM_SHADOW_MAPS], float fDepth, bool bSmoothingFilter)
{
	float4 vGreater = (g_vSplitDistances.xyzw < fDepth.xxxx);

	float fShadow;
	#if (SPEEDTREE_NUM_SHADOW_MAPS > 3)
		if (vGreater.z > 0.0)
			fShadow = SampleShadowMap(g_tShadowMap3, samShadowMap3, vPosInLightSpace[3], bSmoothingFilter);
		else
	#endif
	#if (SPEEDTREE_NUM_SHADOW_MAPS > 2)
		if (vGreater.y > 0.0)
			fShadow = SampleShadowMap(g_tShadowMap2, samShadowMap2, vPosInLightSpace[2], bSmoothingFilter);
		else
	#endif
	#if (SPEEDTREE_NUM_SHADOW_MAPS > 1)
		if (vGreater.x > 0.0)
			fShadow = SampleShadowMap(g_tShadowMap1, samShadowMap1, vPosInLightSpace[1], bSmoothingFilter);
		else
	#endif
	#if (SPEEDTREE_NUM_SHADOW_MAPS > 0)
			fShadow = SampleShadowMap(g_tShadowMap0, samShadowMap0, vPosInLightSpace[0], bSmoothingFilter);
	#endif

	return FadeByDepth(fShadow, fDepth, g_vShadowFadeParams.x, g_vShadowFadeParams.z);
}


///////////////////////////////////////////////////////////////////////  
//  TintShadowMapRegion
//
//	Diagnositc function; speed not important

float3 TintShadowMapRegion(float fDepth)
{
	const float3 avTints[5] = 
	{
		float3(0.1, 0.0, 0.0),
		float3(0.0, 0.1, 0.0),
		float3(0.0, 0.0, 0.1),
		float3(0.1, 0.0, 0.1),
		float3(1.0, 0.0, 0.0)
	};

	float4 vGreater = (g_vSplitDistances.xyzw < fDepth.xxxx);
	float nIndex = dot(vGreater, 1.0f);

	#if (SPEEDTREE_NUM_SHADOW_MAPS == 1)
		if (nIndex == 0)
			return avTints[0];
		else
			return avTints[1];
	#elif (SPEEDTREE_NUM_SHADOW_MAPS == 2)
		if (nIndex == 0)
			return avTints[0];
		else if (nIndex == 1)
			return avTints[1];
		else
			return avTints[2];
	#elif (SPEEDTREE_NUM_SHADOW_MAPS == 3)
		if (nIndex == 0)
			return avTints[0];
		else if (nIndex == 1)
			return avTints[1];
		else if (nIndex == 2)
			return avTints[2];
		else
			return avTints[3];
	#else // cannot exceed 4
		if (nIndex == 0)
			return avTints[0];
		else if (nIndex == 1)
			return avTints[1];
		else if (nIndex == 2)
			return avTints[2];
		else if (nIndex == 3)
			return avTints[3];
		else
			return avTints[4];
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  ProjectToLightSpace

float4 ProjectToLightSpace(float3 vPos, float4x4 mLightViewProj)
{
	float4 vProjection = mul(mLightViewProj, float4(vPos, 1.0f));

	const float2 c_vShadowMapTexelCenterOffset = float2(0.5f / SPEEDTREE_SHADOW_MAP_RES, 0.5f / SPEEDTREE_SHADOW_MAP_RES);
	#if defined(SPEEDTREE_DIRECTX9) || defined(SPEEDTREE_DIRECTX10)
		vProjection.xy = 0.5f * vProjection.xy / vProjection.w + float2(0.5f, 0.5f) + c_vShadowMapTexelCenterOffset;
		vProjection.y = 1.0f - vProjection.y;
	#else
		vProjection.xyz += vProjection.www;
		vProjection.xy += c_vShadowMapTexelCenterOffset;
		vProjection.xyz *= 0.5;
	#endif

	return vProjection;	
}

#endif // SPEEDTREE_SHADOWS_ENABLED