/*********************************************************************
CHARACTER SHADER FOR SKINNED MESHES - Diffuse + Illumination + Normal + Specular  + Planar Perspective Shadows
//Uses 4 textures:
D: diffuse texture
I: illumination texture
N: normal maps
S: specular texture

Pixel shader by Mark Blosser 
mjblosser@gmail.com
http://www.mjblosser.com

Description:


****************************************************************************/

/************* UNTWEAKABLES **************/

float4x4 WorldIT : WorldInverseTranspose;
float4x4 WorldViewProj : WorldViewProjection;
float4x4 ViewProjection   : ViewProjection;
float4x4 World : World;
float4x4 ViewInv : ViewInverse;
float time : Time;
float4 clipPlane : ClipPlane = { 0.000000, 0.000000, 0.000000, 0.000000 };
float4 eyePos : CameraPosition;


/*********** DBPRO UNTWEAKABLES **********/
float4x4 boneMatrix[60] : BoneMatrixPalette;

float4 SpotFlashPos;


float4 SpotFlashColor;


float SpotFlashRange   //fixed value that FPSC uses
<
    string UIName =  "SpotFlash Range";
    
> = {600.00};



/******SURFACE AND AMBIENT LIGHTING*******************/

float SubPower <
    string UIWidget = "slider";
    float UIMin = 0.0;
    float UIMax = 10.0;
    float UIStep = 0.01;
    string UIName =  "Subsurface Rolloff Range";
> = 1.500000;

float4 SubColor <
	string UIWidget = "Color";
    string UIName =  "Subsurface 'Bleed-thru'";
>  = { 1.000000, 0.500000, 0.500000, 1.000000 };

float RollOff <
    string UIWidget = "slider";
    float UIMin = 0.0;
    float UIMax = 0.99;
    float UIStep = 0.01;
    string UIName =  "Subsurface Rolloff Range";
> = 0.200000;

//this value is pulled from FPSC - non tweakable
float4 AmbiColor : Ambient
<
    string UIName =  "Ambient Light Color";
    
> = { 0.100000, 0.100000, 0.100000, 1.000000 };

//this value is also pulled from fpsc - used with Ambicolor to calculate light decay
float4 SurfColor : Diffuse
<
    string UIName =  "Surface Color";
    string UIType = "Color";
> = { 1.000000, 1.000000, 1.000000, 1.000000 };

float Alphavalue : AlphaOverride
<
    string UIName =  "Alpha Override";
> = 1.0f;

//this value is also pulled from fpsc
//FloorPositionY.x holds the shadow offset value which places the shadow at character's feet
float4 FloorPositionY : Diffuse
<
> = { 0.000000, 0.000000, 0.000000, 0.000000 };



/************* LIGHTS **************/
float4 LightSource
<
    string UIType = "Fixed Light Source";
> = { -250.000000, 500.000000, -750.000000, 1.000000 };




/*******PLANAR SHADOW TWEAKABLES**************************/
/*
//offset shadow 1/2 character's height in FPSC units to place at ground level
float ShadowOffset //no longer required since FPSC now supplies this value in FloorPositionY.x
<
    string UIWidget = "slider";
    float UIMin = -100.0;
    float UIMax = 0.0;
    float UIStep = 0.1;
    string UIName =  "bumpiness";
> = 0.000000;

float ShadowDilation 
<
    string UIWidget = "slider";
    float UIMin = 1.0;
    float UIMax = 3.0;
    float UIStep = 0.1;
    string UIName =  "bumpiness";
> = 1.100000;

float ShadowSkew 
<
    string UIWidget = "slider";
    float UIMin = -1.0;
    float UIMax = 1.0;
    float UIStep = 0.1;
    string UIName =  "bumpiness";
> = 0.0;

*/

float ShadowAlpha 
<
    string UIWidget = "slider";
    float UIMin = 0.0;
    float UIMax = 1.0;
    float UIStep = 0.1;
    string UIName =  "bumpiness";
> = 0.250000;


/*
//Shader Variables pulled from FPI scripting 
float4 ShaderVariables : ShaderVariables
<
    string UIName =  "Shader Variables";
    string UIType = "Color";
> = {1.0f, 1.0f, 1.0f, 1.0f};

*/





/************* TEXTURES AND SAMPLERS **************/

texture colorTexture : DiffuseMap
<
    string Name = "default_color.dds";
    string type = "2D";
 string ResourceName = ""; >;
 

texture illumMap : SpecularMap
<
	string Name = "default_mask.dds";
    string type = "2D";
 string ResourceName = ""; >; 

texture normalTexture : DiffuseMap
<
    string Name = "default_bump_normal.dds";
    string type = "2D";
 string ResourceName = ""; >;



texture specularMap : SpecularMap
<
	string Name = "default_mask.dds";
    string type = "2D";
 string ResourceName = ""; >;



sampler2D colorSampler = sampler_state
{
	Texture = <colorTexture>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler2D illumSampler = sampler_state
{
	Texture = <illumMap>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler2D normalSampler = sampler_state
{
	Texture = <normalTexture>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

sampler2D specularSampler = sampler_state
{
	Texture = <specularMap>;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
	MipFilter = LINEAR;
};

/************* DATA STRUCTS **************/

struct appdata {
    float3 Position 	: POSITION;
    float4 UV		    : TEXCOORD0;
    float4 Normal   	: NORMAL;
    float4 Tangent  	: TANGENT0;
    float4 Binormal 	: BINORMAL0;
    float4 Blendweight	: TEXCOORD1;
    float4 Blendindices	: TEXCOORD2;
};



/* data passed from vertex shader to pixel shader */
struct vertexOutput {
    float4 HPosition	: POSITION;
    float4 TexCoord	    : TEXCOORD0;
    float3 LightVec	    : TEXCOORD1;
    float3 WorldNormal	: TEXCOORD2;
    float3 WorldEyeVec	: TEXCOORD3;
    float3 WorldTangent	: TEXCOORD4;
    float3 WorldBinorm	: TEXCOORD5;
    float3 Wpos         : TEXCOORD6;
    float clip : TEXCOORD7;
    
};

struct shadow_vs_out
{
	float4 pos : POSITION;
	
	
};


/***********SHADOW VERTEX SHADER ******/



shadow_vs_out ShadowVS(appdata IN)
{
	shadow_vs_out OUT;
    float3 netPosition = 0, netNormal = 0;
    for (int i = 0; i < 4; i++)
    {
     float index = IN.Blendindices[i];
     float3x4 model = float3x4(boneMatrix[index][0], boneMatrix[index][1], boneMatrix[index][2]);
     
     
     float3 vec3 = mul(model, float4(IN.Position , 1));
     
     vec3 = vec3 + boneMatrix[index][3].xyz;
     float3x3 rotate = float3x3(model[0].xyz, model[1].xyz, model[2].xyz); 
     
     netPosition += vec3.xyz * IN.Blendweight[i];
     
    }
    
    
    LightSource.xyz = LightSource.xyz + float3(1,75,1);
    //LightSource.xyz = float3(1800,750*1.3,-1700);  
    //just stick a light way up in the sky for basic top-down shadows
    //LightSource.xyz = float3(1,20000,1);  //just stick a light way up in the sky
    
    //-------persp shadow projection code--------
    float4 tempPos = float4(netPosition,1);  //convert netPosition to float4
    float4 convert = mul(float4(0,FloorPositionY.x,0,1),World); //put ShadowOffset into world space so it sticks to feet of character
    float4 wpos = mul(tempPos,World);  //put unflattened model in world space
    //float projectRatio = (ShadowOffset - LightSource.y)/(wpos.y - LightSource.y); //shadow stays at constant floor position "0" regardless of character vertical position, not want we want for FPSC
    float projectRatio = (convert.y - LightSource.y)/(wpos.y - LightSource.y); 
    float4 projectWPos = LightSource + (wpos - LightSource) * projectRatio; //project flattened shadow in world space 
    OUT.pos = mul(projectWPos, ViewProjection);  //put projected shadow in clip space
   
   //----------------------------------------------- 
    
   
    return OUT;
}

/*shadow_vs_out ShadowVS(appdata IN)
{
	shadow_vs_out OUT;
    float3 netPosition = 0, netNormal = 0;
    for (int i = 0; i < 4; i++)
    {
     float index = IN.Blendindices[i];
     float3x4 model = float3x4(boneMatrix[index][0], boneMatrix[index][1], boneMatrix[index][2]);
     
     
     float3 vec3 = mul(model, float4(IN.Position , 1));
     
     vec3 = vec3 + boneMatrix[index][3].xyz;
     float3x3 rotate = float3x3(model[0].xyz, model[1].xyz, model[2].xyz); 
     
     netPosition += vec3.xyz * IN.Blendweight[i];
     
    }
    //--------------------------------------
    float4 wpos = mul(netPosition,World);
    float4 lightvec = -normalize(LightSource - wpos);
    
    //geometry flattening matrix
	float4x4 project = { ShadowDilation , 0  ,0 , 0 ,
                        0 , 0.01 , ShadowSkew , 0 ,  //second value controls Y-flattening 
                        0  ,0 , ShadowDilation   ,0 ,
                        0 , FloorPositionY.x  ,0  , 1 };
      
   //----------------------------------------------- 
    float4 tempPos = float4(netPosition,1.0);
    tempPos = mul(tempPos,project); //flatten rotated
    


    float3 worldSpacePos = mul(tempPos, World).xyz;  //project down y axis (flatten)
    OUT.pos = mul(tempPos,WorldViewProj);
    return OUT;
}*/

//------------SHADOW PIXEL SHADER-------------------//

float4 ShadowPS(shadow_vs_out IN) : COLOR
{
   float4 result = float4(0,0,0,ShadowAlpha);
   return result;
   
}

//------------MAIN CHARACTER VERTEX SHADER-------------------//

vertexOutput mainVS(appdata IN)
{
    vertexOutput OUT;
    float3 netPosition = 0, netNormal = 0;
    for (int i = 0; i < 4; i++)
    {
     float index = IN.Blendindices[i];
     float3x4 model = float3x4(boneMatrix[index][0], boneMatrix[index][1], boneMatrix[index][2]);
     
     
     float3 vec3 = mul(model, float4(IN.Position, 1));
     vec3 = vec3 + boneMatrix[index][3].xyz;
     float3x3 rotate = float3x3(model[0].xyz, model[1].xyz, model[2].xyz); 
     float3 norm3 = mul(rotate, IN.Normal);
     netPosition += vec3.xyz * IN.Blendweight[i];
     netNormal += norm3.xyz * IN.Blendweight[i];
    }
    float4 tempPos = float4(netPosition,1.0);
    netNormal = normalize(netNormal);

    LightSource.xyz = LightSource.xyz; // + float3(1,100,1);
    float3 worldSpacePos = mul(tempPos, World).xyz;
    OUT.WorldNormal = mul(netNormal, WorldIT).xyz;
    OUT.WorldTangent = mul(IN.Tangent, WorldIT).xyz;
    OUT.WorldBinorm = mul(IN.Binormal, WorldIT).xyz;
    OUT.LightVec = LightSource - worldSpacePos;
    OUT.TexCoord = IN.UV;
    OUT.WorldEyeVec = (ViewInv[3].xyz - worldSpacePos);
    
    OUT.HPosition = mul(tempPos, WorldViewProj);
    OUT.Wpos = worldSpacePos;
    
    // all shaders should send the clip value to the pixel shader (for refr/refl)                  // OUT.clip = dot(worldSpacePos, clipPlane); // too expensive for VS2.0
    OUT.clip = (worldSpacePos.y * clipPlane.y) + clipPlane.a; // good for water plane
    
    return OUT;
}


//------------MAIN CHARACTER PIXEL SHADER-------------------//


float4 CalcSpotFlash( float3 worldNormal, float3 worldPos )
{
    float4 output = (float4)0.0;
    float3 toLight = SpotFlashPos.xyz - worldPos.xyz;
    float3 lightDir = normalize( toLight );
    float lightDist = length( toLight );
    
    float MinFalloff = 200;  //falloff start distance - 50,0,.01 are very cool too for lanterns
    float LinearFalloff = 1;
    float ExpFalloff = .005;  // 1/200
    
    float fAtten = 1.0/(MinFalloff + (LinearFalloff*lightDist)+(ExpFalloff*lightDist*lightDist));
    
    SpotFlashPos.w = clamp(0,1,SpotFlashPos.w -.2);
    
    
    output += max(0,dot( lightDir, worldNormal ) * 1*SpotFlashColor*fAtten * (SpotFlashPos.w) );
    
    return output;
}



float4 mainPS(vertexOutput IN) : COLOR
{
    // all shaders should receive the clip value                                                   
	clip(IN.clip);
    
    float4 diffusemap = tex2D(colorSampler,IN.TexCoord.xy);
    float4 specmap = tex2D(specularSampler,IN.TexCoord.xy);
    float4 illummap = tex2D(illumSampler,IN.TexCoord.xy);
    

    float4 normalmap = tex2D(normalSampler,IN.TexCoord.xy); //sample normal map
    //normalmap.xyzw = normalmap.wyxx;  //swizzle for quality DXT5nm normal map format
    normalmap.xyz = normalmap.xyz *2 -1;  //expand normal map 
    float3 Ln = normalize(IN.LightVec);
    float3 Nn = (IN.WorldNormal);
    float3 Tn = (IN.WorldTangent);
    float3 Bn = (IN.WorldBinorm);
    float3 Nb =  (normalmap.z * Nn) + (1.0*(normalmap.x * Tn + normalmap.y * Bn));
    Nb = normalize(Nb);
    float3 Vn = normalize(IN.WorldEyeVec);
    float3 Hn = normalize(Vn + Ln);
    
    //Main lighting equation 
    float4 lighting3 = lit(pow(0.5*(dot(Ln,Nb))+0.5,1.1),dot(Hn,Nb),12);       
    float4 lighting = lit(pow(0.5*(dot(Ln,Nb))+.5,4),dot(Hn,Nb),32); 
    lighting = lit((dot(Ln,Nb)),dot(Hn,Nb),32);
    
    //float4 ldn = lighting.y;
    float4 ldn = dot(Ln,Nb);
    float4 diffComp = max(0,ldn);
    float4 Diffuse = diffComp ;
    float4 SurfColor2 = pow(SurfColor,10)/5;
    float4 subLamb = smoothstep(-(RollOff+(illummap.a/2.5))-SurfColor2,1.0,ldn) - smoothstep(0.0,1.0,ldn);
    subLamb = max(0,subLamb);
    float4 Subsurface = (subLamb * diffusemap-((diffusemap.g+diffusemap.b)/2));
    Subsurface = (subLamb * illummap);
    
    float4 spotflashlighting = CalcSpotFlash ( IN.WorldNormal, IN.Wpos.xyz ); 
    float4 diffContrib = diffusemap * (lighting.y + (SubPower*Subsurface)) * (SurfColor) ;
    float4 specContrib = (lighting.z+lighting3.z) * specmap*(SurfColor);
    



	//calculate the angle between the normal and the camera
	float4 angle = saturate( dot( Nb, Vn) );
	
	//limit the angle
	angle = smoothstep((1-specmap) , 0, angle );
	
	//use this value to produce a color
	float4 fresnel = lerp( float4(0.75,0.75,0.75,0), float4( 0.0,0.0,0.0,0), angle );
	fresnel= (1-fresnel);
    specContrib=(fresnel*(2.5*specContrib));

    float4 sky = lerp(SurfColor, (lighting3.y+SurfColor)/1.5, 2*Nb.g); 
    
    //float4 ambContrib =   (AmbiColor*herolightfinal);
    float4 ambContrib =  ( AmbiColor*(2*sky+AmbiColor)+spotflashlighting)*diffusemap;
    	
    //create some pulsing self-illumination
    //float pulsing = 0.5 * sin(time * 20) + 0.5;
    //float4 selfIll = illummap * pulsing   ;
    //float4 selfIll = illummap   ;
    

    float4 result = diffContrib  +2*specContrib + ambContrib ;
    //result = ((lighting.y + (2*SubPower*Subsurface))+(AmbiColor*(2*sky+0.25*AmbiColor)))+5*((lighting.z+lighting3.z)*specmap*fresnel);
    result.a= diffusemap.a * Alphavalue;

    return result;
}


/****** technique *******/

technique dx9textured
{
	pass drawcharacter 
	{		
	        // lighting
        	Lighting       = FALSE;
        	SpecularEnable = FALSE;
	        FogEnable      = FALSE;

		// same vertex shader for both DX9 techniques
		VertexShader = compile vs_2_0 mainVS();
		ZEnable = true;
		SrcBlend = one;
		DestBlend = one;
		AlphaBlendEnable = false;
		//AlphaTestEnable = true;
		ZWriteEnable = true;
		CullMode = ccw;
		PixelShader = compile ps_2_b mainPS();
	}
	
	pass shadowstencil
	{
		VertexShader = compile vs_2_0 ShadowVS( );
		ZEnable = false;
		ZWriteEnable = true;
		stencilenable = true;
        stencilfunc = equal;
        stencilref = 0;
        //stencilmask = 0xffffffff;
        //stencilwritemask = 0xffffffff;
    
   
    stencilzfail= keep;
    stencilfail = keep;
    stencilpass = decr;
    alphaBlendEnable = true;
		
		//AlphaTestEnable = true;
		CullMode = ccw;
		PixelShader = compile ps_2_0 ShadowPS( ); //must still call pixel shader for correct alpha blending
		
		
		
	}
	
	
	pass drawshadow
	{
		VertexShader = compile vs_2_0 ShadowVS( );
		ZEnable = true;
		ZWriteEnable = true;
		stencilenable = true;
     stencilfunc = less;
    stencilref = 0;
   //stencilmask = 0xffffffff;
    //stencilwritemask = 0xffffffff;
    
   
   stencilzfail= keep;
    stencilfail = keep;
    stencilpass = zero;
    alphaBlendEnable = true;
    srcBlend = srcAlpha;
    destBlend = srcAlpha;
    zenable = true;
    zwriteEnable = true;
		
		//AlphaTestEnable = true;
		CullMode = cw;
		PixelShader = compile ps_2_0 ShadowPS( );
	
	
	
	
}
}
/***************************** eof ***/






