///////////////////////////////////////////////////////////////////////  
//  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

#include "Textures.hlsl"


///////////////////////////////////////////////////////////////////////  
//  Constants

#define c_fBillboardAlphaScalar 1.7f
#define c_fBillboardNoiseScalar 800.0f
#define c_f3dGeometryNoiseScalar 100.0f
#define c_fTwoPi 6.28318530f
#define c_fPi 3.141592653f

// max amount to scale texture's alpha values to make
// alpha-to-coverage work cleanly with our textures
#define c_fGlobalMaxAlphaScalar 3.0


///////////////////////////////////////////////////////////////////////  
//	ComputeSpecularHalfVector

float3 ComputeSpecularHalfVector(float3 vNormal, float3 vBinormal, float3 vTangent, float3 vPosition)
{
	float3 vHalfVector = 0.5f * (g_vLightDir - g_vCameraDirection);

	return float3(dot(vHalfVector, vTangent),
				  dot(vHalfVector, vBinormal),
				  dot(vHalfVector, vNormal)); 
}


///////////////////////////////////////////////////////////////////////  
//	Fade

float Fade(float fValue, float fStart, float fEnd)
{
	float fFade = saturate((fValue - fStart) / (fEnd - fStart));

	return fFade;
}


///////////////////////////////////////////////////////////////////////  
//	Fade

float Fade(float fValue, float2 vRange)
{
	const float c_fStart = vRange.x;
	const float c_fEnd = vRange.y;

	return saturate((fValue - c_fStart) / (c_fEnd - c_fStart));
}


///////////////////////////////////////////////////////////////////////  
//	AlphaTestNoise

float AlphaTestNoise(float2 vTexCoords)
{
	return abs(frac(vTexCoords.x * vTexCoords.y));
}


///////////////////////////////////////////////////////////////////////  
//	EncodeVectorToColor

float3 EncodeVectorToColor(float3 vVector)
{
	return vVector * 0.5f + 0.5f;
}


///////////////////////////////////////////////////////////////////////  
//	DecodeVectorFromColor

float3 DecodeVectorFromColor(float3 vVector)
{
	return vVector * 2.0f - 1.0f;
}


///////////////////////////////////////////////////////////////////////  
//	UncompressVector4

float4 UncompressVector4(float4 vVector4)
{
	#ifdef SPEEDTREE_DIRECTX10
		return vVector4 * float4(2.0f, 2.0f, 2.0f, 1.0f) - float4(1.0f, 1.0f, 1.0f, 0.0f);
	#elif defined(SPEEDTREE_XBOX_360)
		return vVector4.wzyx / float4(127.5f, 127.5f, 127.5f, 255.0f) - float4(1.0f, 1.0f, 1.0f, 0.0);
	#else
		return vVector4 / float4(127.5f, 127.5f, 127.5f, 255.0f) - float4(1.0f, 1.0f, 1.0f, 0.0);
	#endif
}


///////////////////////////////////////////////////////////////////////  
//	UncompressWindVector

float4 UncompressWindVector(float4 vRawValue, float fScalar)
{
	#ifdef SPEEDTREE_DIRECTX10
		float4 vValue = vRawValue * 2.0f - 1.0f;
	#elif defined(SPEEDTREE_XBOX_360)
		float4 vValue = vRawValue.wzyx / 127.5f - 1.0f;
	#else
		float4 vValue = vRawValue / 127.5f - 1.0f;
	#endif

	vValue.xyz *= fScalar;
	vValue.w *= c_fPi;

	return vValue;
}


///////////////////////////////////////////////////////////////////////  
//  FogPixel

float3 FogPixel(float4 vFogData, float3 vColor)
{
	return lerp(vFogData.rgb, vColor, vFogData.w);
}


///////////////////////////////////////////////////////////////////////  
//  ProjectToScreen_NoTranslation

float4 ProjectToScreen_NoTranslation(float3 vPos)
{
	return mul(g_mModelViewProj, float4(vPos, 1.0f));
}


///////////////////////////////////////////////////////////////////////  
//  ProjectToScreen

float4 ProjectToScreen(float3 vPos)
{
	#ifdef SPEEDTREE_WORLD_TRANSLATE
		vPos -= g_vCameraPosition;
	#endif

	return ProjectToScreen_NoTranslation(vPos);
}


///////////////////////////////////////////////////////////////////////  
//  ComputeBillboardFade
//
//	Use to fade the geometry to billboard images

const float c_fOffsetLength = 0.45; // shouldn't be higher than 0.5

float ComputeBillboardFade(float fDistance)
{
	float fOffset = c_fOffsetLength * (g_vLodProfile.w - g_vLodProfile.z);
	float fStart = g_vLodProfile.z;
	float fEnd = g_vLodProfile.w - fOffset;

	return Fade(fDistance, fStart, fEnd);
}


///////////////////////////////////////////////////////////////////////  
//  Compute3dFade
//
//	Use to fade the geometry to billboard images

float Compute3dFade(void)
{
	const float c_fAlphaScalar = g_vMaterialExtras.y;

	float fDistance = distance(g_vCameraPosition.xyz, g_vTreePosAndScale.xyz);

	float fOffset = c_fOffsetLength * (g_vLodProfile.w - g_vLodProfile.z);
	float fStart = g_vLodProfile.z + fOffset;
	float fEnd = g_vLodProfile.w;

	return c_fAlphaScalar * (1.0f - Fade(fDistance, fStart, fEnd));
}


///////////////////////////////////////////////////////////////////////  
//  Compute3dFade
//
//	Use to fade the geometry to billboard images

float Compute3dFade(out float fDistance)
{
	const float c_fAlphaScalar = g_vMaterialExtras.y;

	fDistance = distance(g_vCameraPosition.xyz, g_vTreePosAndScale.xyz);

	float fOffset = c_fOffsetLength * (g_vLodProfile.w - g_vLodProfile.z);
	float fStart = g_vLodProfile.z + fOffset;
	float fEnd = g_vLodProfile.w;

	return c_fAlphaScalar * (1.0f - Fade(fDistance, fStart, fEnd));
}


///////////////////////////////////////////////////////////////////////  
//  RotationMatrix_zAxis
//
//  Constructs a Z-axis rotation matrix

float3x3 RotationMatrix_zAxis(float fAngle)
{
    // compute sin/cos of fAngle
    float2 vSinCos;
    sincos(fAngle, vSinCos.x, vSinCos.y);
    
    return float3x3(vSinCos.y, -vSinCos.x, 0.0f, 
                    vSinCos.x, vSinCos.y, 0.0f, 
                    0.0f, 0.0f, 1.0f);
}


///////////////////////////////////////////////////////////////////////  
//  Rotate_zAxis
//
//  Returns an updated .xy value

float2 Rotate_zAxis(float fAngle, float3 vPoint)
{
    float2 vSinCos;
    sincos(fAngle, vSinCos.x, vSinCos.y);
    
    return float2(dot(vSinCos.yx, vPoint.xy), dot(float2(-vSinCos.x, vSinCos.y), vPoint.xy));
}


///////////////////////////////////////////////////////////////////////  
//  RotationMatrix_yAxis
//
//  Constructs a Y-axis rotation matrix

float3x3 RotationMatrix_yAxis(float fAngle)
{
    // compute sin/cos of fAngle
    float2 vSinCos;
    sincos(fAngle, vSinCos.x, vSinCos.y);
    
    return float3x3(vSinCos.y, 0.0f, vSinCos.x,
                    0.0f, 1.0f, 0.0f,
                    -vSinCos.x, 0.0f, vSinCos.y);
}


///////////////////////////////////////////////////////////////////////  
//  Rotate_yAxis
//
//  Returns an updated .xz value

float2 Rotate_yAxis(float fAngle, float3 vPoint)
{
    float2 vSinCos;
    sincos(fAngle, vSinCos.x, vSinCos.y);
    
    return float2(dot(float2(vSinCos.y, -vSinCos.x), vPoint.xz), dot(vSinCos.xy, vPoint.xz));
}


///////////////////////////////////////////////////////////////////////  
//  RotationMatrix_xAxis
//
//  Constructs a X-axis rotation matrix

float3x3 RotationMatrix_xAxis(float fAngle)
{
    float2 vSinCos;
    sincos(fAngle, vSinCos.x, vSinCos.y);
    
    return float3x3(1.0f, 0.0f, 0.0f,
                    0.0f, vSinCos.y, -vSinCos.x,
                    0.0f, vSinCos.x, vSinCos.y);
}


///////////////////////////////////////////////////////////////////////  
//  Rotate_xAxis
//
//  Returns an updated .yz value

float2 Rotate_xAxis(float fAngle, float3 vPoint)
{
    float2 vSinCos;
    sincos(fAngle, vSinCos.x, vSinCos.y);
    
    return float2(dot(vSinCos.yx, vPoint.yz), dot(float2(-vSinCos.x, vSinCos.y), vPoint.yz));
}


///////////////////////////////////////////////////////////////////////  
//  Rotate_UpAxis

float3 Rotate_UpAxis(float fAngle, float3 vPoint)
{
	#ifdef SPEEDTREE_Z_UP
		return float3(Rotate_zAxis(fAngle, vPoint), vPoint.z);
	#else
		float2 vRotatedVector = Rotate_yAxis(fAngle, vPoint);
		return float3(vRotatedVector.x, vPoint.y, vRotatedVector.y);
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  Rotate_OutAxis

float3 Rotate_OutAxis(float fAngle, float3 vPoint)
{
	#ifdef SPEEDTREE_Z_UP
		float2 vRotatedVector = Rotate_yAxis(fAngle, vPoint);
		return float3(vRotatedVector.x, vPoint.y, vRotatedVector.y);
	#else
		return float3(Rotate_zAxis(fAngle, vPoint), vPoint.z);
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  RotationMatrix_AroundUpAxis
//
//	Up axis changes depending on the coordinate system used by the client application

float3x3 RotationMatrix_AroundUpAxis(float fAngle)
{
	#ifdef SPEEDTREE_Z_UP
		return RotationMatrix_zAxis(fAngle);
	#else
		return RotationMatrix_yAxis(fAngle);
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  RotationMatrix_AroundOutAxis
//
//	Up axis changes depending on the coordinate system used by the client application

float3x3 RotationMatrix_AroundOutAxis(float fAngle)
{
	#ifdef SPEEDTREE_Z_UP
		return RotationMatrix_yAxis(fAngle);
	#else
		return RotationMatrix_zAxis(fAngle);
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  ConvertToStd

float3 ConvertToStd(float3 vCoord)
{
	#ifdef SPEEDTREE_Z_UP
		#ifdef SPEEDTREE_LHCS
			return float3(vCoord.x, -vCoord.y, vCoord.z);
		#else
			return vCoord;
		#endif
	#else
		#ifdef SPEEDTREE_LHCS
			return float3(vCoord.x, vCoord.z, vCoord.y);
		#else
			return float3(vCoord.x, -vCoord.z, vCoord.y);
		#endif
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  ConvertFromStd

float3 ConvertFromStd(float3 vCoord)
{
	#ifdef SPEEDTREE_Z_UP
		#ifdef SPEEDTREE_LHCS
			return float3(vCoord.x, -vCoord.y, vCoord.z);
		#else
			return vCoord;
		#endif
	#else
		#ifdef SPEEDTREE_LHCS
			return float3(vCoord.x, vCoord.z, vCoord.y);
		#else
			return float3(vCoord.x, vCoord.z, -vCoord.y);
		#endif
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  TerrainNormalMapLookup

float3 TerrainNormalMapLookup(float2 vTexCoords)
{
	// setup aliases (taken from global variables)
	const float c_fNormalMapBlueScale = g_vSplatMapTiles.w;

	// normal map
	float3 texNormal = SampleTexture(TerrainNormalMap, vTexCoords).xyz;
	texNormal -= 0.5f;
	texNormal.b *= c_fNormalMapBlueScale;
	
	#ifdef SPEEDTREE_LHCS
		#ifdef SPEEDTREE_Y_UP
			texNormal.xyz = texNormal.xzy;
		#else
			texNormal.y *= -1.0f;
		#endif
	#else
		#ifdef SPEEDTREE_Y_UP
			texNormal.xyz = texNormal.xzy;
			texNormal.z *= -1.0f;
		#endif
	#endif
	
	return normalize(texNormal);
}


///////////////////////////////////////////////////////////////////////  
//  TerrainNormalMapLookupVS

float3 TerrainNormalMapLookupVS(float2 vTexCoords)
{
	// setup aliases (taken from global variables)
	const float c_fNormalMapBlueScale = g_vSplatMapTiles.w;

	// normal map
	float3 texNormal = SampleTextureLod(TerrainNormalMap, vTexCoords, 0.0).xyz;
	texNormal -= 0.5f;
	texNormal.b *= c_fNormalMapBlueScale;
	
	#ifdef SPEEDTREE_LHCS
		#ifdef SPEEDTREE_Y_UP
			texNormal.xyz = texNormal.xzy;
		#else
			texNormal.y *= -1.0f;
		#endif
	#else
		#ifdef SPEEDTREE_Y_UP
			texNormal.xyz = texNormal.xzy;
			texNormal.x *= -1.0f;
		#endif
	#endif
	
	return normalize(texNormal);
}


///////////////////////////////////////////////////////////////////////  
//  AdjustForTreeRotation
//
//	Each instance can be rotated about its up-axis arbitrarily.  This function
//	performs quick rotations for positions, tangent vectors, etc.  It also
//	compensates for the current coordinate system.

float3 AdjustForTreeRotation(float3 vComponent)
{
	float4 vRotationValues = float4(g_vTreeRotation.xy, -g_vTreeRotation.x, 0.0f);

	#ifdef SPEEDTREE_LHCS
		#ifdef SPEEDTREE_Z_UP
			return float3(dot(vRotationValues.yxw, vComponent.xyz), dot(vRotationValues.zyw, vComponent.xyz), vComponent.z);
		#else
			return float3(dot(vRotationValues.ywz, vComponent.xyz), vComponent.y, dot(vRotationValues.xwy, vComponent.xyz));
		#endif 
	#else // must be SPEEDTREE_RHCS
		#ifdef SPEEDTREE_Z_UP
			return float3(dot(vRotationValues.yxw, vComponent.xyz), dot(vRotationValues.zyw, vComponent.xyz), vComponent.z);
		#else
			return float3(dot(vRotationValues.ywz, vComponent.xyz), vComponent.y, dot(vRotationValues.xwy, vComponent.xyz));
		#endif 
	#endif // SPEEDTREE_LHCS
}


///////////////////////////////////////////////////////////////////////  
//  SkyColorWithSun
// 
// The direction gives you a point in the sphere around the camera. The 
// sky color at this point is computed using an s-curve function using the normalized 
// up component. An s-curve function is 1 / (1 - e^-t), with the basic range being 
// -6 to 6. Sun color is added on top of this.

float3 SkyColor(float3 vDirection, bool bAddSun = true)
{
	#ifdef SPEEDTREE_Z_UP
		float fInterp = vDirection.z;
	#else
		float fInterp = vDirection.y;
	#endif
	
	// sigmoid (s-curve) function to ensure smooth sky blending
	fInterp = 12.0 * ((fInterp + g_vFogParams.z) * g_vFogParams.w + 0.5);
	fInterp = 1.0f / (1.0f + exp(fInterp));
	float3 vSkyWithFog = lerp(g_vFogColor.xyz, g_vSkyColor.xyz, fInterp);
	
	if (bAddSun)
	{
		float fSunLight = saturate(dot(vDirection, g_vLightDir) + g_vSunParams.x);
			
		fSunLight = pow(fSunLight, g_vSunParams.y);
		float3 vTotalColor = lerp(vSkyWithFog, g_vSunColor, fSunLight);
	    
		return vTotalColor;
	}
	else
	{
		return vSkyWithFog;
	}
}


///////////////////////////////////////////////////////////////////////  
//  FogVertex

float4 FogVertex(float3 vWorldPosition)
{
    float3 vSkyDirection = vWorldPosition - g_vCameraPosition;
	float fDistance = length(vSkyDirection);
    vSkyDirection = vSkyDirection / fDistance;
    
    float fFogValue = saturate((g_vFogParams.x - fDistance) / g_vFogParams.y);
    
    float fSunlight = (g_vSunParams.z * dot(-g_vCameraDirection, -g_vLightDir) + 1.0f);
    fSunlight *= fSunlight;
    fSunlight = saturate(fSunlight);
    
    return float4(SkyColor(vSkyDirection, false), fFogValue * fSunlight);
}


///////////////////////////////////////////////////////////////////////  
//  SpecialEffectFade
//
//	Used to fade the specular and transmission effect so that the 3D tree
//  will better match the billboards (that don't have these special effects).

float SpecialEffectFade(float fDistance)
{
	#if defined(SPEEDTREE_SPECULAR_LIGHTING) || defined(SPEEDTREE_TRANSMISSION_LIGHTING)
		const float c_f3dLowLod = g_vLodProfile.y;
		const float c_fBillboardStart = g_vLodProfile.z;
		const float c_fFadeStart = c_f3dLowLod;
		const float c_fFadeRange = c_fBillboardStart - c_f3dLowLod;

		float fFade = 1.0f - (fDistance - c_fFadeStart) / c_fFadeRange;

		return saturate(fFade);
	#else
		return 1.0f;
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  VertexTextureFetch

#define VertexTextureFetch(_sampler_, _st_texcoords_) \
	tex2Dlod((_sampler_), float4((_st_texcoords_), 0.0f, 0.0f))


///////////////////////////////////////////////////////////////////////  
//  PossibleEarlyOut

void PossibleEarlyOut(float fAlpha, float fOffset = SPEEDTREE_CLIP_OFFSET)
{
	#if defined(SPEEDTREE_DIRECTX10) || defined(SPEEDTREE_PS3) || !defined(SPEEDTREE_Z_PREPASS)
		clip(fAlpha - fOffset);
	#endif
}


///////////////////////////////////////////////////////////////////////  
//  PossibleEarlyOut_Shadow

void PossibleEarlyOut_Shadow(float fAlpha, float fOffset = SPEEDTREE_CLIP_OFFSET)
{
	clip(fAlpha - fOffset);
}

