///////////////////////////////////////////////////////////////////////  
//  Grass.hlsl
//
//	This file contains the shaders used for the frond geometry.  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

#ifndef SPEEDTREE_USING_EFFECTS
	#include "Global.hlsl"
	#include "UniformVariables.hlsl"
	#include "Wind.hlsl"
	#include "Utility.hlsl"
	#ifdef SPEEDTREE_SHADOWS_ENABLED
		#include "Shadows.hlsl"
	#endif
#endif


//#define SPEEDTREE_GRASS_LOD_SHRINK // scale grass blades down to zero area between start and fade distances
#define SPEEDTREE_GRASS_LOD_FADE // scale grass alpha value from 1.0 to 0.0 between start and fade distances
#define SPEEDTREE_GRASS_SHADOW_STREAK_DEFEAT // shadow streak terribly down the sides of the grass cards; enabled this to minimize it


// SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX will do most of the heavy lifting in the vertex shader
// through texture fetches; only works in vs_3_0 or better; also, older cards require that
// the textures being fetched are in floating format, which we're not using in the demo setup.
//
// Comment #define SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX out for better compatibility but
// lesser performance

#ifndef SPEEDTREE_OPENGL
	// we believe there is a bug in Cg compiler that causes the grass to render white
	// when this is set under OpenGL, plus arbvp1/fp1 doesn't support vertex texture
	// fetches but arbvp1/fp1 supports the widest range of cards
	#define SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX
#endif


///////////////////////////////////////////////////////////////////////  
//  Grass-specific global variables

float4		g_vGrassInfo				GLOBAL_REG(50);	// x = fade_start, y = (fade_end - fade_start), z = terrain match factor (top), w = terrain match factor (bottom)
float4		g_avGrassSplatColors[3]		GLOBAL_REG(51);	// used to approximate the terrain colors; faster than texture lookups
float4x4	g_mGrassUnitBillboard		GLOBAL_REG(54);	// single billboard that faces the camera; uniquely scaled and positioned for each blade


///////////////////////////////////////////////////////////////////////  
//  Shader input/output structures

struct SGrassVertex
{
	float4	m_vPosition					: SPEEDTREE_ATTR0;	// xyz = position, w = corner index
	float4	m_vTexCoords				: SPEEDTREE_ATTR8;	// xy = diffuse texcoords, zw = terrain texcoords
	float4  m_vSizes					: SPEEDTREE_ATTR9;	// x = width, y = height, z = wind weight, w = ambient occlusion
	float2	m_vNormalPerturb			: SPEEDTREE_ATTR10;	// xy = perturb change normal per blade
};

struct SGrassPixel
{
	float4  m_vPosition					: SPEEDTREE_VS_OUTPUT;	// xyzw = position
	float4	m_vTexCoords				: TEXCOORD0;			// xy = diffuse texcoords, z = top-or-bottom, w = lod scale
#ifdef SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX
	float4  m_vTerrainColor				: TEXCOORD2;			// xyz = color of terrain at this point, w = lighting dot product
#else
	float4	m_vNormalPerturb			: TEXCOORD1;			// xy = normal perturb, zw = terrain texcoords
	float	m_fAmbientOcclusion			: TEXCOORD2;			// ambient occlusion value
#endif
#ifdef SPEEDTREE_SHADOWS_ENABLED
	float	m_fDepth					: TEXCOORD3;			// distance from the camera, in view space
	float4	m_av2dPosInLightSpace[SPEEDTREE_NUM_SHADOW_MAPS]	: TEXCOORD4;	// light space coordinates
#endif
};


#ifdef SPEEDTREE_GRASS_SHADOW_STREAK_DEFEAT
	///////////////////////////////////////////////////////////////////////  
	//  AdjustGrassBladeDepth

	float3 AdjustGrassBladeDepth(float3 vCorner, float fHeight, float fWindWeight)
	{
		// setup
		const float c_fOffsetScalar = 2.0f;

		// streak ajustments aren't necessary when facing the light or away from the light, only
		// at angles;  we use the 2D dot product between the light direction and camera direction
		// to adjust the about of the effect we use
		float fEffectDot = dot(g_vLightDir.xy, g_vCameraDirection.xy);
		float fEffectFade = 1.0f - (fEffectDot * fEffectDot);
		const float c_fOffsetMax = c_fOffsetScalar * fEffectFade * fHeight;
		float fDepthOffset = c_fOffsetMax * fWindWeight;

		return vCorner + (0.5f * g_vCameraDirection * fDepthOffset);
	}
#endif


///////////////////////////////////////////////////////////////////////  
//  GrassVertex

SGrassPixel GrassVertex(SGrassVertex sIn)
{
	SGrassPixel sOut;

	// setup aliases (taken from global variables)
	const float  c_fFadeStart = g_vGrassInfo.x;
	const float  c_fFadeLength = g_vGrassInfo.y;
	const float  c_fTerrainMatchFactorTop = g_vGrassInfo.z;
	const float  c_fTerrainMatchFactorBottom = g_vGrassInfo.w;
	const float  c_fLightingPerturbScale = 1.45f;

	// setup aliases (taken from incoming vertex)
	const float3 c_vPosition = sIn.m_vPosition.xyz;
	const int    c_nCornerIndex = int(sIn.m_vPosition.w);
	const float2 c_vTexCoords = sIn.m_vTexCoords.xy;
	const float2 c_vTerrainTexCoords = sIn.m_vTexCoords.zw;
	const float  c_fWidth = sIn.m_vSizes.x;
	const float  c_fHeight = sIn.m_vSizes.y;
	const float  c_fWindWeight = sIn.m_vSizes.z;
	const float2 c_vNormalPerturb = sIn.m_vNormalPerturb;
	const float	 c_fAmbientOcclusion = sIn.m_vSizes.w;

	// compute LOD scalar value
	float fLodScale = 1.0f;
	#if defined(SPEEDTREE_GRASS_LOD_SHRINK) || defined (SPEEDTREE_GRASS_LOD_FADE)
		float fDistance = distance(g_vCameraPosition, c_vPosition);
		fLodScale = 1.0f - saturate((fDistance - c_fFadeStart) / c_fFadeLength);
	#endif

	// compute the corner of the grass card
	#ifdef SPEEDTREE_Z_UP
		float3 vSize = float3(c_fWidth, c_fWidth, c_fHeight);
	#else
		float3 vSize = float3(c_fWidth, c_fHeight, c_fWidth);
	#endif

	// break up the monotany of a perfect billboard by offsetting the corners based on the random
	float2 vHorzOffset = (c_nCornerIndex - 1) * c_vNormalPerturb * 0.75f;
	float vVertOffset = (c_nCornerIndex + 1) * c_vNormalPerturb.x * 1.0f;
	#ifdef SPEEDTREE_Z_UP
		float3 vRandomOffset = c_fWindWeight * float3(vHorzOffset, vVertOffset);
	#else
		float3 vRandomOffset = c_fWindWeight * float3(vHorzOffset.x, vVertOffset, vHorzOffset.y);
	#endif
	#ifdef SPEEDTREE_GRASS_LOD_SHRINK
		float3 vCorner = (g_mGrassUnitBillboard[c_nCornerIndex].xyz + vRandomOffset) * fLodScale.xxx * vSize;
	#else
		float3 vCorner = (g_mGrassUnitBillboard[c_nCornerIndex].xyz + vRandomOffset) * vSize;
	#endif

	// wind will affect both the vertex position & normal
	float3 vNormalShift;
	float3 vAdjustedPosition = c_vPosition;
	#ifdef SPEEDTREE_BASIC_WIND
		if (c_fWindWeight > 0.0f)
			GrassMotion(vAdjustedPosition, vNormalShift);
		else
			vNormalShift = float3(0.0f, 0.0f, 0.0f);
	#else
		vNormalShift = float3(0.0f, 0.0f, 0.0f);
	#endif

	// put the vertex into the correct world location
	vCorner += vAdjustedPosition;

	// final position
	sOut.m_vPosition = ProjectToScreen(vCorner);

	// misc
	sOut.m_vTexCoords.xy = c_vTexCoords;
	sOut.m_vTexCoords.z = lerp(c_fTerrainMatchFactorBottom, c_fTerrainMatchFactorTop, c_fWindWeight);
	sOut.m_vTexCoords.w = fLodScale * c_fGlobalMaxAlphaScalar; // scale up for fuller grass look

	#ifdef SPEEDTREE_SHADOWS_ENABLED

		#ifdef SPEEDTREE_GRASS_SHADOW_STREAK_DEFEAT
			float3 vDepthCorner = AdjustGrassBladeDepth(vCorner, c_fHeight, c_fWindWeight);
			float4 vProjectedCorner = ProjectToScreen(vDepthCorner);
		#else
			float3 vDepthCorner = vCorner;
			float4 vProjectedCorner = sOut.m_vPosition;
		#endif

		// shadow projection
		for (int i = 0; i < SPEEDTREE_NUM_SHADOW_MAPS; ++i)
			sOut.m_av2dPosInLightSpace[i] = ProjectToLightSpace(vDepthCorner, g_amLightViewProjs[i]);

		sOut.m_fDepth = vProjectedCorner.z / g_fFarClip;
	#endif

	#ifdef SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX
		const float  c_fSplat0Repeat = g_vSplatMapTiles.x;
		const float  c_fSplat1Repeat = g_vSplatMapTiles.y;
		const float  c_fSplat2Repeat = g_vSplatMapTiles.z;

		// compute the normal at this point on the terrain
		float3 vNormal = TerrainNormalMapLookupVS(c_vTerrainTexCoords);

		// perturb the normal for more interesting lighting effects
		vNormal.xy += c_fLightingPerturbScale * c_vNormalPerturb * c_fWindWeight; // multiply by c_fWindWeight to perturb only the top two vertices

		// move normal based on wind
		vNormal += vNormalShift;
		vNormal = normalize(vNormal);

		// compute the lighting value 
		float fDot = saturate(dot(vNormal, g_vLightDir));
		#ifdef SPEEDTREE_SHADOWS_ENABLED
			sOut.m_vTerrainColor.w = fDot * FadeByDepth(c_fAmbientOcclusion, sOut.m_fDepth, g_vSplitDistances[SPEEDTREE_NUM_SHADOW_MAPS - 1], g_vShadowFadeParams.y);
		#else
			sOut.m_vTerrainColor.w = fDot * c_fAmbientOcclusion;
		#endif

		// compute the color of the terrain at this point
		float3 texSplats = SampleTextureLod(TerrainSplatMap, c_vTerrainTexCoords.xy, 0.0f).xyz;

		// use actual terrain textures (more accurate than color approximations)
		float3 texSplat0 = SampleTextureLod(TerrainSplatLayer0, c_vTerrainTexCoords * c_fSplat0Repeat, 0.0f).xyz;
		float3 texSplat1 = SampleTextureLod(TerrainSplatLayer1, c_vTerrainTexCoords * c_fSplat1Repeat, 0.0f).xyz;
		//float3 texSplat2 = SampleTextureLod(TerrainSplatLayer2, c_vTerrainTexCoords * c_fSplat2Repeat, 0.0f).xyz;

		// use approximate colors (more useful with high frequency textures)
		//float3 texSplat0 = g_avGrassSplatColors[0].xyz;
		//float3 texSplat1 = g_avGrassSplatColors[1].xyz;
		float3 texSplat2 = g_avGrassSplatColors[2].xyz;

		// final composite terrain color
		sOut.m_vTerrainColor.xyz = texSplat0 * texSplats.x + texSplat1 * texSplats.y + texSplat2 * texSplats.z;

	#else // SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX

		sOut.m_vNormalPerturb.xy = c_fLightingPerturbScale * c_vNormalPerturb;
		sOut.m_vNormalPerturb.zw = c_vTerrainTexCoords;
		#ifdef SPEEDTREE_SHADOWS_ENABLED
			sOut.m_fAmbientOcclusion = FadeByDepth(c_fAmbientOcclusion, sOut.m_fDepth, g_vSplitDistances[SPEEDTREE_NUM_SHADOW_MAPS - 1], g_vShadowFadeParams.y);
		#else
			sOut.m_fAmbientOcclusion = c_fAmbientOcclusion;
		#endif

	#endif // SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX

	return sOut;
}


///////////////////////////////////////////////////////////////////////  
//  GrassPixel

float4 GrassPixel(SGrassPixel sIn) : SPEEDTREE_PS_OUTPUT
{
	#ifdef SPEEDTREE_TERRAIN_MATCHING_IN_VERTEX
		const float2 c_vTexCoords = sIn.m_vTexCoords.xy;
		const float  c_fTopOrBottom = sIn.m_vTexCoords.z;		// 1.0 = top vertex, 0.0 = bottom vertex
		const float  c_fLodScale = sIn.m_vTexCoords.w;
		const float  c_fLightingDotProduct = sIn.m_vTerrainColor.w;
		const float3 c_vTerrainColor = sIn.m_vTerrainColor.xyz;
		const float4 c_vAmbientMaterial = g_vMaterialAmbient;
		const float4 c_vDiffuseMaterial = g_vMaterialDiffuse;

		// texture lookup
		float4 texGrassImage = SampleTexture(GrassBlades, c_vTexCoords);
		clip(texGrassImage.a - 0.1f);

		// get shadow contribution if enabled
		float fShadow = 1.0f;
		#ifdef SPEEDTREE_SHADOWS_ENABLED
			fShadow = saturate(ShadowMapLookup(sIn.m_av2dPosInLightSpace, sIn.m_fDepth, false));
		#endif

		// push the texture to look more like the terrain based on its proximity to the ground
		float3 vUnderlyingImage = lerp(c_vTerrainColor, texGrassImage.rgb, c_fTopOrBottom);

		// compute top color
		float3 vTerrainLighting = lerp(c_vAmbientMaterial.xyz, c_vDiffuseMaterial.xyz, c_fLightingDotProduct * fShadow);

		// assemble final output with scaled alpha component (if alpha fading was enabled)
		float4 vOut = float4(vUnderlyingImage * vTerrainLighting, texGrassImage.a * c_fLodScale);

		return vOut;
	#else
		// setup aliases (taken from global variables)
		const float4 c_vAmbientMaterial = g_vMaterialAmbient;
		const float4 c_vDiffuseMaterial = g_vMaterialDiffuse;
		const float  c_fSplat0Repeat = g_vSplatMapTiles.x;
		const float  c_fSplat1Repeat = g_vSplatMapTiles.y;
		const float  c_fSplat2Repeat = g_vSplatMapTiles.z;

		// setup alias (taken from incoming data)
		const float2 c_vBladeTexCoords = sIn.m_vTexCoords.xy;
		const float2 c_vTerrainTexCoords = sIn.m_vNormalPerturb.zw;
		const float  c_fTopOrBottom = sIn.m_vTexCoords.z;
		const float2 c_vNormalPerturb = sIn.m_vNormalPerturb.xy;
		const float  c_fLodScale = sIn.m_vTexCoords.w;
		const float  c_fAmbientOcclusion = sIn.m_fAmbientOcclusion;

		// texture lookup
		float4 texGrassImage = SampleTexture(GrassBlades, c_vBladeTexCoords);
		clip(texGrassImage.a - 0.1f);

		// get shadow contribution if enabled
		float fShadow = 1.0f;
		#ifdef SPEEDTREE_SHADOWS_ENABLED
			fShadow = ShadowMapLookup(sIn.m_av2dPosInLightSpace, sIn.m_fDepth, false);
		#endif

		// splat map and individual textures
		float3 texSplats = SampleTexture(TerrainSplatMap, c_vTerrainTexCoords).rgb;

		// using approximate colors for the terrain splat matching is much faster.  Use the texture lookups
		// for more accurate color matching
		float3 texSplat0 = g_avGrassSplatColors[0].rgb;
		float3 texSplat1 = g_avGrassSplatColors[1].rgb;
		float3 texSplat2 = g_avGrassSplatColors[2].rgb;
		//float3 texSplat0 = SampleTexture(TerrainSplatLayer0, c_vTerrainTexCoords * c_fSplat0Repeat).rgb;
		//float3 texSplat1 = SampleTexture(TerrainSplatLayer1, c_vTerrainTexCoords * c_fSplat1Repeat).rgb;
		//float3 texSplat2 = SampleTexture(TerrainSplatLayer2, c_vTerrainTexCoords * c_fSplat2Repeat).rgb;

		// compute composite terrain color
		float3 vTerrainColor = texSplat0 * texSplats.x + texSplat1 * texSplats.y + texSplat2 * texSplats.z;

		// push the texture to look more like the terrain based on its proximity to the ground
		float3 vUnderlyingImage = lerp(vTerrainColor, texGrassImage.rgb, c_fTopOrBottom);

		// extract normal from terrain map
		float3 vNormal = TerrainNormalMapLookup(c_vTerrainTexCoords);

		// perturb the normal for more interesting lighting effects
		vNormal.xy += c_vNormalPerturb * c_fTopOrBottom; // multiply by c_fTopOrBottom to perturb only the top two vertices

		// compute the lighting value
		float fLighting = saturate(dot(vNormal, g_vLightDir)) * fShadow;
		float3 vTerrainLighting = lerp(c_vAmbientMaterial.xyz, c_vDiffuseMaterial.xyz, min(c_fAmbientOcclusion, fLighting));

		// assemble final output with scaled alpha component (if alpha fading was enabled)
		float4 vOut = float4(vUnderlyingImage * vTerrainLighting, texGrassImage.a * c_fLodScale);

		return vOut;
	#endif
}

