//
// Project:
//
//	D3D RightMark
//
// Description:
//
//	Effect file for "Pixel Shading 3" test.
//
// Effect Version: 
//
//	DirectX 9.0c (Oct 2005 SDK Update)
//
// Shader Profile:
//
//	Pixel Shaders 3.0
//
//	Vertex Shaders 3.0
//

//
// Global Variables
//

#define rm_PI 3.141592654

float fGeometryScale = 0.01;
float fParallaxOffset = 0.03;

// Lights

struct rm_light
{
	float4 vPosition;

	float4 vDiffuse;
	float4 vSpecular;

	float  fAttenuation;
};

rm_light Lights[3];

// Material

float fMaterialPower = 16.0;

// Misc

float4	vCameraPos;			// Camera Position

float4x4 mW;				// World Matrix
float4x4 mWVP;				// World * View * Projection Matrix

// Textures

texture texDiffuse;				// Diffuse Map Texture			( A8R8G8B8 format )
texture texSpecular;			// Specular Map Texture			( A8R8G8B8 format )
texture texHeight;				// Height Map Texture			( A8R8G8B8 format )
texture texNormal;				// Normal Map Texture			( Q8W8V8U8 format )

texture texFurDiffuse;
texture texFurFill;
texture texFurHeight;
texture texFurFin;

// Procedural Texture Variables

float4 vTime;

// Misc

int nPixelShader;
int nVertexShader;

//
// Samplers
//

sampler DiffuseMapSampler = sampler_state
{
	Texture = (texDiffuse);

	MinFilter = Anisotropic;
	MagFilter = Linear;
	MipFilter = Linear;

	AddressU  = Wrap;
	AddressV  = Wrap;

	MaxAnisotropy = 16;
};

sampler NormalMapSampler = sampler_state
{
	Texture = (texNormal);

	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Point;

	AddressU  = Wrap;
	AddressV  = Wrap;

	MaxAnisotropy = 16;
};

sampler SpecularMapSampler = sampler_state
{
	Texture = (texSpecular);

	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;

	AddressU  = Wrap;
	AddressV  = Wrap;

	MaxAnisotropy = 16;
};

sampler HeightMapSampler = sampler_state
{
	Texture = (texHeight);

	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;

	AddressU  = Wrap;
	AddressV  = Wrap;

	MaxAnisotropy = 16;
};

sampler FurDiffuseSampler = sampler_state
{
	Texture = (texFurDiffuse);

	MinFilter = Anisotropic;
	MagFilter = Linear;
	MipFilter = Linear;

	AddressU  = Wrap;
	AddressV  = Wrap;

	MaxAnisotropy = 16;
};

sampler FurHeightSampler = sampler_state
{
	Texture = (texFurHeight);

	MinFilter = Linear;
	MagFilter = Linear;
	MipFilter = Linear;

	AddressU  = Wrap;
	AddressV  = Wrap;

	MaxAnisotropy = 16;
};

//
// Shaders
//

//
// Steep parallax mapping shader
// Based on shaders:
//   Steep parallax mapping     (c) 2005 Morgan McGuire and Max McGuire (Iron Lore Entertainment)
//   Parallax occlusion mapping (c) 2005 Natalya Tatarchuk (ATI Research, Inc.)
//

struct PARALLAX_DATA
{
	float4 vPosition	: POSITION;
	float2 vTexCoord	: TEXCOORD0;
	float3 vEye			: TEXCOORD1;
	float3 vLight		: TEXCOORD2;
};

PARALLAX_DATA ParallaxVS(
	float4 vPosition	: POSITION,
	float4 vNormal		: NORMAL,
	float2 vTexCoord	: TEXCOORD0,
	float3 vTangent		: TEXCOORD1,
	float3 vBinormal	: TEXCOORD2
)
{
	PARALLAX_DATA o;

	// Tangent Space

	float3 binormal = mul( vBinormal.xyz, mW );
	float3 tangent  = mul( vTangent.xyz,  mW );
	float3 normal	= mul( vNormal.xyz,	mW );

	// Vertex Position -> World Space

	float4 vPositionWorld = mul( vPosition, mW );// * fGeometryScale;

	// Eye Vector

	float3 eye = vCameraPos.xyz - vPositionWorld.xyz;
	o.vEye.x = dot( eye, binormal ); 
	o.vEye.y = dot( eye, tangent ); 
	o.vEye.z = dot( eye, normal ); 

	vPositionWorld *= fGeometryScale;

	// Lighting

	float3 light = (Lights[0].vPosition.xyz - vPositionWorld.xyz)*Lights[0].fAttenuation;
	o.vLight.x = dot( light, binormal );
	o.vLight.y = dot( light, tangent );
	o.vLight.z = dot( light, normal );

	// Texture Coords

	o.vTexCoord  = vTexCoord;

	// Position

	o.vPosition = mul( vPosition, mWVP );

	// Finalize

	return o;
}

const float fThreshold = 4;
const float fTexSize = 512;
const int maxSamples = 50;
const int minSamples = 8;

float4 ParallaxPS ( PARALLAX_DATA In ) : COLOR
{
	float fHeight = 0.0;
	float2 vTexCoord = In.vTexCoord;
	float3 vEye = normalize( In.vEye );

	// Compute current gradients:
	float2 fTexCoordsPerSize = vTexCoord * fTexSize;

	// Compute all 4 derivatives in x and y in one instruction:
	float2 dxSize, dySize;
	float2 dx, dy;

	float4( dxSize, dx ) = ddx( float4( fTexCoordsPerSize, vTexCoord ) );
	float4( dySize, dy ) = ddy( float4( fTexCoordsPerSize, vTexCoord ) );
					
	// Find min of change in u and v across quad: compute du and dv magnitude across quad
	float2 dTexCoords = dxSize * dxSize + dySize * dySize;
	// standard mipmapping uses max here
	float fMinTexCoordDelta = max( dTexCoords.x, dTexCoords.y );
	//compute mip level  (* 0.5 is effectively computing a square root before the )
	float fMipLevel = max( 0.5 * log2( fMinTexCoordDelta ), 0 );
	
	if ( fMipLevel <= fThreshold )
	{
		int nNumSteps = (int) lerp( maxSamples, minSamples, vEye.z );
	
		float fStep = 1.0 / (float)nNumSteps;
		float2 vDelta = float2( In.vEye.x, In.vEye.y ) * fParallaxOffset * fStep / In.vEye.z;

		float fCurHeight = 1.0;
		int nStepIndex = 0;

		while ( nStepIndex < nNumSteps ) 
		{
			vTexCoord += vDelta;
			fHeight = tex2Dgrad( HeightMapSampler, vTexCoord, dx, dy ).x;
			fCurHeight -= fStep;
			if ( fHeight > fCurHeight ) 
				nStepIndex = nNumSteps + 1;
			else
				nStepIndex++;
		}
	}

	// Bump Mapping

	float3 vN = tex2D( NormalMapSampler, vTexCoord ) * 2.0 - 1.0;

	// Lighting
	float3 vLight = normalize( In.vLight );
	float3 vHalfAngle = vLight + vEye;
	float3 vHalf = normalize( vHalfAngle );

	float fNdotL = saturate( dot( vN.xyz, vLight.xyz ) );
	float fNdotH = saturate( dot( vN.xyz, vHalf.xyz ) );

	float fSpec = pow( fNdotH, fMaterialPower );
	float vAttenuation = saturate( 1.0 - dot( In.vLight, In.vLight ) );
	float4 vDiffuse  = Lights[0].vDiffuse * fNdotL * vAttenuation;
	float4 vSpecular = Lights[0].vSpecular * fSpec * vAttenuation;

	float selfShadow = 1.0;

	if ( fMipLevel <= fThreshold && fNdotL > 0 )
	{
		// Trace a shadow ray along the light vector.
		int nNumShadowSteps = (int) lerp( maxSamples, minSamples, vLight.z );
		float fStep = 1.0 / (float)nNumShadowSteps;
		float2 vShadowCoord = vTexCoord;
		float2 vDelta = float2( vLight.x, vLight.y ) * fParallaxOffset * fStep / vLight.z;

		float fCurHeight = fHeight + fStep * 0.1;
		int nStepIndex = 0;

		while ( nStepIndex < nNumShadowSteps ) 
		{
			vShadowCoord -= vDelta;
			fHeight = tex2Dgrad( HeightMapSampler, vShadowCoord, dx, dy ).x;
			fCurHeight += fStep;
			if ( fHeight > fCurHeight || fCurHeight >= 1.0 ) 
				nStepIndex = nNumShadowSteps + 1;
			else
				nStepIndex++;
		}

		// We are in shadow if we left the loop because
		// we hit a point
		selfShadow = fHeight < fCurHeight;
	}

	// Finalize

	return  vDiffuse * selfShadow * tex2D( DiffuseMapSampler, vTexCoord ) +
			vSpecular * tex2D( SpecularMapSampler, vTexCoord );
}

//
// Fur Shader ( (c) 2005 Alexander Sakharoff )
//

struct FUR_DATA
{
	float4 vPosition	: POSITION;
	float2 vTexCoord	: TEXCOORD0;
	float3 vEye			: TEXCOORD1;
	float fDepth		: TEXCOORD2;
};

FUR_DATA FurVS(
	float4 vPosition : POSITION,
	float4 vNormal	: NORMAL,
	float2 vTexCoord : TEXCOORD0,
	float3 vTangent  : TEXCOORD1,
	float3 vBinormal : TEXCOORD2
)
{
	FUR_DATA o;

	// Tangent Space

	float3 binormal = mul( vBinormal.xyz, mW );
	float3 tangent  = mul( vTangent.xyz,  mW );
	float3 normal	= mul( vNormal.xyz,	mW );

	// Vertex Position -> World Space

	float4 vPositionWorld = mul( vPosition, mW );// * fGeometryScale;

	// Eye Vector

	float3 eye = vCameraPos.xyz - vPositionWorld.xyz;
	o.vEye.x = dot( eye, binormal ); 
	o.vEye.y = dot( eye, tangent ); 
	o.vEye.z = dot( eye, normal ); 

	vPositionWorld *= fGeometryScale;

	// Texture Coords

	o.vTexCoord  = vTexCoord * 2;

	// Position

	o.vPosition = mul( vPosition, mWVP );
	o.fDepth = o.vPosition.z;

	// Finalize

	return o;
}

const float fFurOffset = 0.04;
const float fFurTexSize = 512;
const float fMaxDepth = 500;
const float fFurThreshold = 4;
const int furMaxSamples = 50;
const int furMinSamples = 20;

float4 FurPS( FUR_DATA In ) : COLOR
{
	float fHeight = 0.0;
	float2 vTexCoord = In.vTexCoord;
	float3 vEye = normalize( In.vEye );

	// Compute current gradients:
	float2 fTexCoordsPerSize = vTexCoord * fFurTexSize;

	// Compute all 4 derivatives in x and y in one instruction:
	float2 dxSize, dySize;
	float2 dx, dy;

	float4( dxSize, dx ) = ddx( float4( fTexCoordsPerSize, vTexCoord ) );
	float4( dySize, dy ) = ddy( float4( fTexCoordsPerSize, vTexCoord ) );
					
	// Find min of change in u and v across quad: compute du and dv magnitude across quad
	float2 dTexCoords = dxSize * dxSize + dySize * dySize;
	// standard mipmapping uses max here
	float fMinTexCoordDelta = max( dTexCoords.x, dTexCoords.y );
	//compute mip level  (* 0.5 is effectively computing a square root before the )
	float fMipLevel = max( 0.5 * log2( fMinTexCoordDelta ), 0 );

	//also check depth	
	if ( fMipLevel <= fFurThreshold && In.fDepth < fMaxDepth )
	{
		int nNumSteps = (int) lerp( furMaxSamples, furMinSamples, vEye.z );
		nNumSteps = (int)( nNumSteps * ( 0.5 + fFurThreshold - fMipLevel ) );
		float fBlend = min( 2.0, fFurThreshold - fMipLevel );
	
		float fStep = 1.0 / (float)nNumSteps;
		float2 vDelta = 
			( float2( In.vEye.x, In.vEye.y ) / In.vEye.z + float2( 0.0, 0.3 ) ) * 
			fFurOffset * fBlend * fStep;
		float fCurHeight = 1.0;
		int nStepIndex = 0;

		while ( nStepIndex < nNumSteps ) 
		{
			vTexCoord += vDelta;
			fHeight = tex2Dgrad( FurHeightSampler, vTexCoord, dx, dy ).x;
			fCurHeight -= fStep;
			if ( fHeight > fCurHeight ) 
				nStepIndex = nNumSteps + 1;
			else
				nStepIndex++;
		}
		fHeight = lerp( 0.5, fHeight, min( 1.0, fBlend ) );
	}
	else
		fHeight = 0.5;
	fHeight = lerp( 0.8, 1.2, fHeight );

	// Finalize

	return  fHeight * 0.625 * 
		( tex2D( FurDiffuseSampler, vTexCoord ) + tex2D( FurDiffuseSampler, vTexCoord * 0.125 ) );
}

VertexShader vsArray[2] = 
{ 
		compile vs_3_0 FurVS(),
		compile vs_3_0 ParallaxVS(),
}; 

PixelShader psArray[2] = 
{ 
		compile ps_3_0 FurPS(),
		compile ps_3_0 ParallaxPS(),
}; 

//
// Default Technique
//

technique TDefault
{
	pass p0
	{
		PixelShader = ( psArray[nPixelShader] );
		VertexShader = ( vsArray[nVertexShader] );
	}
}