///////////////////////////////////////////////////////////////////////  
//  Lighting.hlsl
//
//
//	*** 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
	#include "Shadows.hlsl"
#endif


///////////////////////////////////////////////////////////////////////  
//  Structure SStandardPixel

struct SStandardPixel
{
    float4  m_vPosition					: SPEEDTREE_VS_OUTPUT;
	float4	m_vDiffuseTexCoords			: TEXCOORD0;	// xy = diffuse texcoords, z = transmission, w = alpha scalar
	float4	m_vNormalVec				: TEXCOORD1;	// xyz = normal map vector, w = special effect fade (specular & transmission)
	float4	m_vFogData					: COLOR0;		// for sky color fogging
	float4	m_vDetailTexCoords			: TEXCOORD2;	// xy = detail texcoords, z = shadow depth, w = ambient occlusion
	float3	m_vSpecularHalfVector		: TEXCOORD3;	// traditional specular half vector, in tangent space
#ifdef SPEEDTREE_SHADOWS_ENABLED
	float4	m_av2dPosInLightSpace[SPEEDTREE_NUM_SHADOW_MAPS] : TEXCOORD4;	// light space coordinates
#endif
};


///////////////////////////////////////////////////////////////////////  
//  Structure SStandardPixel_Depth

struct SStandardPixel_Depth
{
    float4  m_vPosition					: SPEEDTREE_VS_OUTPUT;
	float3	m_vDiffuseTexCoords			: TEXCOORD0;	// xy = diffuse texcoords, z = fade
};


///////////////////////////////////////////////////////////////////////  
//  Structure SStandardPixel_Shadow

struct SStandardPixel_Shadow
{
    float4  m_vPosition					: SPEEDTREE_VS_OUTPUT;
	float3	m_vDiffuseTexCoords			: TEXCOORD0;	// xy = diffuse texcoords, z = fade
};


///////////////////////////////////////////////////////////////////////  
//  SpeedTreeNormalize

float3 SpeedTreeNormalize(float3 vVector)
{
	return normalize(vVector);
}


///////////////////////////////////////////////////////////////////////  
//  StandardPixel
//
//	This one function most likely represents where a good deal of render
//	time is spent for any given frame.  Branches, fronds, leaf meshes,
//	and leaf cards all use this standard pixel shader.  It will compute
//	colored lighting for one light, standard normal mapping, apply
//	ambient occlusion as computed in the Modeler.
//
//	While this function looks long, the conditionals are made at 
//  compile-time.

float4 StandardPixel(SStandardPixel sIn, 
					 uniform bool bUseSpecular,
					 uniform bool bUseTransmission,
					 uniform bool bUseAmbientContrast,
					 uniform bool bUseDetail,
					 uniform bool bUseDetailNormal,
					 uniform bool bSmoothingFilter)
{
	float4 vOutput;

	// setup aliases (taken from incoming data)
	const float2 c_vDiffuseTexCoords = sIn.m_vDiffuseTexCoords.xy;
	const float2 c_vDetailTexCoords = sIn.m_vDetailTexCoords.xy;
	const float3 c_vNormalMapVec = SpeedTreeNormalize(sIn.m_vNormalVec.rgb);
	const float	 c_fAmbientOcclusion = sIn.m_vDetailTexCoords.w;
	const float  c_fDepth = sIn.m_vDetailTexCoords.z;
	const float4 c_vFogData = sIn.m_vFogData;
	const float  c_fSpecialEffectFade = sIn.m_vNormalVec.w;

	// setup aliases (taken from global variables)
	const float3 c_vAmbientMaterial = g_vMaterialAmbient.rgb;
	const float3 c_vDiffuseMaterial = g_vMaterialDiffuse.rgb;
	const float3 c_vSpecularMaterial = g_vMaterialSpecular.rgb;
	const float3 c_vTransmissionColor = g_vMaterialEmissive.rgb;
	const float  c_fShininess = g_vMaterialExtras.z;

	// diffuse texture lookup with early exit based on alpha mask
    float4 texDiffuse = SampleTexture(Diffuse, c_vDiffuseTexCoords);
	float fDiffuseAlpha = texDiffuse.a;

	// compute alpha scalar
	float fAlphaScalar = sIn.m_vDiffuseTexCoords.w; 

	#ifdef SPEEDTREE_ALPHA_TESTING
		const float c_fMaxAlphaScalar = g_vMaterialExtras.y;
		float fAlphaScalarNorm = fAlphaScalar / c_fMaxAlphaScalar;

		fDiffuseAlpha -= (1.0 - fAlphaScalarNorm) * AlphaTestNoise(c_vDiffuseTexCoords * c_f3dGeometryNoiseScalar);
	#endif

	float fAlphaOut = fDiffuseAlpha * fAlphaScalar;

	#ifdef SPEEDTREE_ALPHA_TESTING
		fAlphaOut -= 0.1;
		PossibleEarlyOut(fAlphaOut, 0.0f);
	#else
		PossibleEarlyOut(fAlphaOut);
	#endif

	// normal map computation
    float3 texNormal = SampleTexture(Normal, c_vDiffuseTexCoords).rgb;

	float3 vMainNormal = DecodeVectorFromColor(texNormal);
	if (bUseDetail)
	{
		float4 c_texDetailDiffuse =  SampleTexture(DetailDiffuse, c_vDetailTexCoords);
		texDiffuse.rgb = lerp(texDiffuse.rgb, c_texDetailDiffuse.rgb, c_texDetailDiffuse.a);
		const float3 c_texDetailNormal = SampleTexture(DetailNormal, c_vDetailTexCoords).rgb;

		if (bUseDetailNormal)
		{
			// combine detail map normal with diffuse normal
			float3 vDetailNormal = DecodeVectorFromColor(c_texDetailNormal);
			vMainNormal = lerp(vMainNormal, vDetailNormal, c_texDetailDiffuse.a);
		}
	}
	vMainNormal = SpeedTreeNormalize(vMainNormal);

	// ambient contribution
	float3 vAmbientComponent;
	float fNormalMapLighting = dot(vMainNormal, c_vNormalMapVec);
	if (bUseAmbientContrast)
	{
		const float c_fAmbientContrastParameter = g_vMaterialExtras.w;
		float fAmbientContrast = lerp(c_fAmbientContrastParameter, 1.0f, abs(fNormalMapLighting));
		vAmbientComponent = c_vAmbientMaterial.rgb * fAmbientContrast;
	}
	else
	{
		vAmbientComponent = c_vAmbientMaterial.rgb;
	}

	// get shadow contribution if enabled (optimizer should remove
	// multiplications of 1.0 if shadows aren't enabled)
	float fShadow = 1.0f;
	#ifdef SPEEDTREE_SHADOWS_ENABLED
		fShadow = ShadowMapLookup(sIn.m_av2dPosInLightSpace, c_fDepth, SPEEDTREE_SMOOTH_SHADOWS);
	#endif

	fNormalMapLighting = fShadow * saturate(fNormalMapLighting);

	// combine ambient and diffuse materials
	vOutput.rgb = lerp(vAmbientComponent, c_vDiffuseMaterial.rgb, fNormalMapLighting);
	vOutput.a = fAlphaOut;

	// if specular & transmission are enabled, both their masks can be retrieved in a single texture fetch
	float4 texSpecularMask;
	float fTransmissionMask;
	if (bUseSpecular)
	{
		texSpecularMask = SampleTexture(Specular, c_vDiffuseTexCoords);

		if (bUseTransmission)
		{
			fTransmissionMask = texSpecularMask.a;
		}
	}
	else if (bUseTransmission)
	{
		fTransmissionMask = SampleTexture(Specular, c_vDiffuseTexCoords).a;
	}

	// transmission
	if (bUseTransmission)
	{
		// grab incoming transmission data
		const float  c_fTransmissionAttenuation = sIn.m_vDiffuseTexCoords.z;
		const float  c_fShadowDarkness = g_vMaterialTransmission.x;

		// compute unattenuated transmission color
		float3 vTransmissionComponent = lerp(vAmbientComponent.rgb, c_vTransmissionColor, fTransmissionMask * c_fSpecialEffectFade);

		// lerp from standard output to transmission tint based on transmission attenuation & shadow parameters
		vOutput.rgb = lerp(vOutput.rgb, vTransmissionComponent, c_fTransmissionAttenuation * saturate(fShadow + c_fShadowDarkness));
	}

	// diffuse texture contribution
	vOutput.rgb *= texDiffuse.rgb;

	// specular
	if (bUseSpecular)
	{
		// grab incoming specular data
		const float3 c_vSpecularHalfVector = SpeedTreeNormalize(sIn.m_vSpecularHalfVector);

		// directional component of specular effect 
		float3 vSpecularComponent = pow(saturate(dot(c_vSpecularHalfVector, vMainNormal)), c_fShininess);

		// mask and color
		vSpecularComponent *= texSpecularMask.rgb * c_vSpecularMaterial.rgb * c_fSpecialEffectFade;

		// add component to output
		vOutput.rgb += vSpecularComponent * fShadow;
	}

	// ambient occlusion affects the entire pixel
	vOutput.rgb *= c_fAmbientOcclusion;

	// fog the output (non-trivial with sky contribution)
	vOutput.rgb = FogPixel(c_vFogData, vOutput.xyz);

	return vOutput;
}


///////////////////////////////////////////////////////////////////////  
//  StandardPixel_Depth

float4 StandardPixel_Depth(SStandardPixel_Depth sIn)
{
	const float2 c_vDiffuseTexCoords = sIn.m_vDiffuseTexCoords.xy;

	// diffuse texture lookup
	float fDiffuseAlpha = SampleTexture(Diffuse, c_vDiffuseTexCoords).a;

	// compute alpha scalar
	float fAlphaScalar = sIn.m_vDiffuseTexCoords.z;
	#ifdef SPEEDTREE_ALPHA_TESTING
		const float c_fMaxAlphaScalar = g_vMaterialExtras.y;
		float fAlphaScalarNorm = fAlphaScalar / c_fMaxAlphaScalar;

		fDiffuseAlpha -= (1.0 - fAlphaScalarNorm) * AlphaTestNoise(c_vDiffuseTexCoords);
	#endif

	float fAlphaOut = fDiffuseAlpha * fAlphaScalar;

	#ifdef SPEEDTREE_ALPHA_TESTING
		fAlphaOut -= 0.1;
		PossibleEarlyOut(fAlphaOut, 0.0f);
	#else
		PossibleEarlyOut(fAlphaOut);
	#endif

	return float4(1.0f, 1.0f, 1.0f, fAlphaOut);
}


///////////////////////////////////////////////////////////////////////  
//  StandardPixel_Shadow

float4 StandardPixel_Shadow(SStandardPixel_Shadow sIn)
{
	// extract values
	const float2 c_vTexCoords = sIn.m_vDiffuseTexCoords.xy;
	const float	 c_fFade = sIn.m_vDiffuseTexCoords.z;

	float4 texDiffuse = SampleTexture(Diffuse, c_vTexCoords);
	PossibleEarlyOut_Shadow(texDiffuse.a);

	return float4(1.0f, 1.0f, 1.0f, texDiffuse.a * c_fFade);
}


///////////////////////////////////////////////////////////////////////  
//  TransmissionBackContribution

float TransmissionBackContribution(float3 vNormal)
{
	const float c_fViewDependency = g_vMaterialTransmission.y;
	const float c_fNormalBackfaceAdjustment = g_vMaterialTransmission.z;

	// adjust normal to compensate for backfacing
	vNormal *= c_fNormalBackfaceAdjustment;

	// how much are we looking at the light?
	#ifdef SPEEDTREE_LHCS
		float fBackContribution = (dot(-g_vCameraDirection, g_vLightDir) + 1.0) / 2.0;
	#else
		float fBackContribution = (dot(g_vCameraDirection, g_vLightDir) + 1.0) / 2.0;
	#endif
	fBackContribution = fBackContribution * fBackContribution;
	fBackContribution *= fBackContribution;

	// how much does the reverse normal say we should get?
	float fScatterDueToNormal = (dot(-g_vLightDir, -vNormal) + 1.0) / 2.0;
	
	// choose between them based on artist controlled parameter
	fBackContribution = lerp(fScatterDueToNormal, fBackContribution, c_fViewDependency);
	
	// back it off based on how much leaf is in the way
	#ifdef SPEEDTREE_LHCS
		float fReductionDueToNormal = (dot(-g_vCameraDirection, vNormal) + 1.0) / 2.0;
	#else
		float fReductionDueToNormal = (dot(g_vCameraDirection, vNormal) + 1.0) / 2.0;
	#endif
	fBackContribution *= fReductionDueToNormal;

	return fBackContribution;
}

