///////////////////////////////////////////////////////////////////////  
//  Wind.hlsl
//
//	This file contains the shaders used for computations.  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


///////////////////////////////////////////////////////////////////////  
//  Configurations

#if defined(SPEEDTREE_DIRECTIONAL_WIND) && !defined(SPEEDTREE_BASIC_WIND)
	#define SPEEDTREE_BASIC_WIND
#endif

#if defined(SPEEDTREE_BASIC_WIND) || defined(SPEEDTREE_DIRECTIONAL_WIND)

	///////////////////////////////////////////////////////////////////////  
	//  CommonWindMotion
	//
	//  This function positions any tree geometry based on their untransformed position and 4 wind floats.

	float3 CommonWindMotion(float3 vPos, float4 vWindData, out float3 vOffset)
	{
		// get the oscillation times (they changed independently to allow smooth frequency changes in multiple components)
		float fPrimaryTime = g_vWindTimes.x + g_vTreePosAndScale.x;
		float fSecondaryTime = g_vWindTimes.y + g_vTreePosAndScale.y;

		float3 vOrigPos = vPos;

		// compute how much the height contributes
		#ifdef SPEEDTREE_Z_UP
			float fAdjust = max(vPos.z * g_vWindDistances.z, 0.0f);
		#else
			float fAdjust = max(vPos.y * g_vWindDistances.z, 0.0f);
		#endif
		fAdjust = pow(fAdjust, g_vWindDistances.w);

		// move a bare minimum due to gusting/strength
		float fMoveAmount = g_vWindGust.y;

		// primary oscillation
		fMoveAmount += g_vWindDistances.x * sin(fPrimaryTime * 0.3f) * cos(fPrimaryTime * 0.95f);
		fMoveAmount *= fAdjust;

		#ifdef SPEEDTREE_Z_UP
			// xy component
			vPos.xy += g_vWindDir.xy * fMoveAmount;

			// move down a little to hide the sliding effect
			vPos.z -= fMoveAmount * g_vWindGustHints.x;
		#else
			// xz component
			vPos.xz += g_vWindDir.xz * fMoveAmount;

			// move down a little to hide the sliding effect
			vPos.y -= fMoveAmount * g_vWindGustHints.x;
		#endif

		// secondary oscillation
		float fOsc = sin((fSecondaryTime + vWindData.w) * 0.3f) * cos((fSecondaryTime + vWindData.w) * 0.95f);

		// reported wind direction (this vector is not normalized and shouldn't be!)
		float3 vDir = vWindData.xyz;

		#ifdef SPEEDTREE_DIRECTIONAL_WIND
			float3 vNewWindDir = g_vWindDir;

			// adjust based on artist's tuning
			vNewWindDir.z += g_vWindGustHints.y;
			vNewWindDir = normalize(vNewWindDir);

			// how long is it?  this length controls how much it should oscillate
			float fLength = length(vDir);

			// make it oscillate as much as it would have if it wasn't going with the wind
			vNewWindDir *= fLength;

			// add the normal term and the 'with the wind term'
 			float fDirectionality = g_vWindGust.x * g_vWindGustHints.z;
			vPos.xyz += (1.0f - fDirectionality) * vDir.xyz * fOsc * g_vWindDistances.y;
 			vPos.xyz += fDirectionality * vNewWindDir.xyz * lerp(fOsc, 1.0f, g_vWindGustHints.z) * g_vWindDistances.y * g_vWindGust.z;
		#else
			vPos.xyz += vDir.xyz * fOsc * g_vWindDistances.y;
		#endif

		vOffset = 0.6f * (vPos - vOrigPos);

		return vPos;
	}


	///////////////////////////////////////////////////////////////////////  
	//  LeafWindMotion

	float3 LeafWindMotion(float3 vPos, inout float3 vDirection, float fScale)
	{
		#ifdef SPEEDTREE_Z_UP
			float2 vDir = -normalize(vPos.xy);
			float fDot = saturate(dot(vDir.xy, g_vWindDir.xy));
		#else
			float2 vDir = -normalize(vPos.xz);
			float fDot = saturate(dot(vDir.xy, g_vWindDir.xz));
		#endif

		float fDirContribution = 0.5f + fDot * 2.0f;
		fDirContribution *= g_vWindLeaves.z;

		float fLeavesTime = g_vWindTimes.w;

		float fMoveAmount = (g_vWindLeaves.x + fDirContribution * g_vWindLeaves.x) * sin(fLeavesTime + vDirection.y * 1000.0f);
		vPos.xyz += vDirection.xyz * fMoveAmount * fScale;

		#ifdef SPEEDTREE_Z_UP
			vDirection += float3(0.0f, 0.0f, saturate(fScale * fMoveAmount * g_vWindLeaves.y));
		#else
			vDirection += float3(0.0f, saturate(fScale * fMoveAmount * g_vWindLeaves.y), 0.0f);
		#endif
		vDirection = normalize(vDirection);

		return vPos;
	}


	///////////////////////////////////////////////////////////////////////  
	//  BillboardMotion
	//
	//	Billboard wind animation.

	float3 BillboardMotion(in float3 vPos, float2 vOffset)
	{
		// get the oscillation times (they changed independently to allow smooth frequency changes in multiple components)
		float fPrimaryTime = g_vWindTimes.x + vOffset.x;
		float fSecondaryTime = g_vWindTimes.y + vOffset.y;

		// compute how much the height contributes
		#ifdef SPEEDTREE_Z_UP
			float fAdjust = max(vPos.z * g_vWindDistances.z, 0.0f);
		#else
			float fAdjust = max(vPos.y * g_vWindDistances.z, 0.0f);
		#endif
		fAdjust = pow(fAdjust, g_vWindDistances.w);

		// move a bare minimum due to gusting/strength
		float fMoveAmount = g_vWindGust.y;

		// primary oscillation
		fMoveAmount += g_vWindDistances.x * sin(fPrimaryTime * 0.3f) * cos(fPrimaryTime * 0.95f);
		fMoveAmount *= fAdjust;

		#ifdef SPEEDTREE_Z_UP
			// xy component
			vPos.xy += g_vWindDir.xy * fMoveAmount;

			// move down a little to hide the sliding effect
			vPos.z -= fMoveAmount * g_vWindGustHints.x;
		#else
			// xz component
			vPos.xz += g_vWindDir.xz * fMoveAmount;

			// move down a little to hide the sliding effect
			vPos.y -= fMoveAmount * g_vWindGustHints.x;
		#endif

		return vPos;
	}


	///////////////////////////////////////////////////////////////////////  
	//  GrassMotion
	//
	//	Grass wind animation.

	void GrassMotion(inout float3 vPos, out float3 vNormalShift)
	{
		// get some global parameters together
		const float  c_fPrimaryTime = g_vWindTimes.x;
		const float  c_fSecondaryTime = g_vWindTimes.y;
		const float  c_fPrimaryThrow = g_vWindDistances.x;
		const float  c_fSecondaryThrow = g_vWindDistances.y;
		const float3 c_vWindDir = g_vWindDir;
		const float  c_fWindStrength = g_vWindGust.x;

		// rolling effect 
		float2 vSway;
		{
			// tunable parameters
			const float	 c_fBroadness = 27.0f;				// higher = broader the rolling waves
			const float  c_fMinimumWindStrength = 0.35f;	// there should always be a little rolling wind; this controls how much
			const float  c_fRollingSpeed = 0.25f;			// higher numbers yield higher frequency
			const float  c_fThrowScale = 0.9f;				// higher numbers means more sway length

			// a few magic numbers are used
			const float c_fThrow = saturate(c_fMinimumWindStrength + c_fWindStrength);

			// determine sway amount
			float fWindAngle = dot(vPos, -c_vWindDir) / c_fBroadness;
			sincos(fWindAngle + c_fRollingSpeed * c_fPrimaryTime, vSway.x, vSway.y);
			vSway *= c_fThrowScale * c_fPrimaryThrow * vSway;
		}

		// add a minor sinusoidal oscillation; each blade will have a unique trig offset
		{
			const float c_fUniqueVariance = 0.5f;
			const float c_fSmallUnique = abs(frac(vPos.x));
			const float	c_fThrowScale = c_fUniqueVariance * (c_fUniqueVariance - c_fSmallUnique * 0.4f);
			const float c_fOscSpeed = c_fUniqueVariance * (c_fUniqueVariance + c_fSmallUnique) + 0.35;

			float2 vOsc;
			sincos(c_fOscSpeed * c_fSecondaryTime, vOsc.x, vOsc.y);

			vSway += c_fThrowScale * vOsc;
		}

		// add  directional effect
		{
			const float  c_fThrowScale = 0.8f;				// higher number means more sway length

			float2 vLeanAmount = c_fThrowScale * g_vWindGust.y * c_vWindDir.xy;

			vSway += vLeanAmount;
		}

		// push down on blade, based on length of sway
		float fSwapMag = length(vSway);
		vPos.z -= fSwapMag * 0.2f;

		// final blade motion
		#ifdef SPEEDTREE_Y_UP
			vPos.xz += vSway;

			// adjust the normal so that lighting changes as a function of wind
			vSway *= 0.2f;
			vNormalShift = float3(vSway.x, 0.0f, vSway.y);
		#else
			vPos.xy += vSway;

			// adjust the normal so that lighting changes as a function of wind
			vSway *= 0.2f;
			vNormalShift = float3(vSway, 0.0f);
		#endif
	}


	///////////////////////////////////////////////////////////////////////  
	//  ComputeFrondRippleOffset

	float3 ComputeFrondRippleOffset(float3 vLodData, float3 vPosition, float2 vRipple, float3 vNormal)
	{
		const float fTime = g_vWindTimes.z;

		float3 vOffset = abs(vNormal.xyz) * (sin(vRipple[0] + fTime) * cos(vRipple[0] + fTime + vLodData.z));
		vOffset *= 0.5f * vRipple[1] * g_vWindFrondRipple.x;

		return vOffset;
	}


	///////////////////////////////////////////////////////////////////////  
	//  RippleFrond

	float3 RippleFrond(float3 vLodData, float3 vPosition, float2 vRipple, float3 vNormal)
	{
		float3 vOffset = ComputeFrondRippleOffset(vLodData, vPosition, vRipple, vNormal);

		const float c_fLodLerp = g_vTreeRotation.z;
		float3 vLod = lerp(vLodData.xyz, vPosition.xyz, c_fLodLerp) + vOffset;

		return vLod;
	}


	///////////////////////////////////////////////////////////////////////  
	//  RippleFrondAndLighting

	float3 RippleFrondAndLighting(float3 vLodData, float3 vPosition, float2 vRipple, inout float3 vNormal, inout float3 vBinormal, inout float3 vTangent)
	{
		float3 vOffset = ComputeFrondRippleOffset(vLodData, vPosition, vRipple, vNormal);

		const float c_fLodLerp = g_vTreeRotation.w;
		float3 vLod = lerp(vLodData.xyz, vPosition.xyz, c_fLodLerp) + vOffset;

		float3 vScaledOffset = vOffset * 2.0f;
		vNormal += vScaledOffset;
		vTangent += vScaledOffset;
		vBinormal += vScaledOffset;

		return vLod;
	}


#endif // defined(SPEEDTREE_BASIC_WIND) || defined(SPEEDTREE_DIRECTIONAL_WIND)

