//-----------------------------------------------------------------------------

texture ShadowMap;
texture SpotLight;

//-----------------------------------------------------------------------------

float4x4 WorldViewProj : WORLDVIEWPROJECTION;
float4x4 WorldIT : WORLDINVERSETRANSPOSE;
float4x4 WorldView : WORLDVIEW;
float4x4 TexTransform;
float3   LightVec;

//-----------------------------------------------------------------------------

struct VS_INPUT {
    float4 Position : POSITION;
    float3 Normal   : NORMAL;
};

struct VS_OUTPUT {
    float4 Position  : POSITION;
    float4 TexCoord0 : TEXCOORD0;
    float4 TexCoord1 : TEXCOORD1;
    float3 Color0 : COLOR0;
};

struct VS_OUTPUT_3 {
    float2 Position  : VPOS;
    float4 TexCoord0 : TEXCOORD0;
    float4 TexCoord1 : TEXCOORD1;
    float3 Color0 : COLOR0;
};

//-----------------------------------------------------------------------------

sampler ShadowMapSampler = sampler_state
{
    Texture = <ShadowMap>;
    MinFilter = LINEAR;  
    MagFilter = LINEAR;
    MipFilter = None;
    AddressU  = Clamp;
    AddressV  = Clamp;
};

sampler ShadowMapSamplerPoint = sampler_state
{
    Texture = <ShadowMap>;
    MinFilter = POINT;  
    MagFilter = POINT;
    MipFilter = None;
    AddressU  = Clamp;
    AddressV  = Clamp;
};

sampler SpotLightSampler = sampler_state
{
    Texture = <SpotLight>;
    MinFilter = Linear;  
    MagFilter = Linear;
    MipFilter = Linear;
    AddressU  = Clamp;
    AddressV  = Clamp;
};

//-----------------------------------------------------------------------------

VS_OUTPUT MainVS(VS_INPUT IN)
{
    VS_OUTPUT OUT;

    // transform normal into world space, then dot with world space light vector 
    // to determine how much light is falling on the surface:
    
    float3 worldNormal = normalize(mul(IN.Normal, (float3x3)WorldIT));
    float ldotn = max(dot(LightVec, worldNormal), 0.0);
    OUT.Color0 = ldotn;

    // transform model-space vertex position to light-space:
    
    OUT.TexCoord0 = mul(IN.Position, TexTransform);
    OUT.TexCoord1 = mul(IN.Position, TexTransform);
    
    // transform model-space vertex position to screen space:
    
    OUT.Position = mul(IN.Position, WorldViewProj);

    return OUT;
}

//-----------------------------------------------------------------------------

float4 MainPS(VS_OUTPUT IN) : COLOR 
{
    float3 shadow    = tex2D(ShadowMapSampler, IN.TexCoord0).rgb;
    //float3 spotlight = tex2D(SpotLightSampler, IN.TexCoord1).rgb;
    float3 color = shadow;// * IN.Color0;
    
    return float4(color, 1.0);
}

//-----------------------------------------------------------------------------

float4 BlackPS(void) : COLOR 
{
    // just return opaque black (shadow)
    return float4(1, 1, 1, 1);
}

//-----------------------------------------------------------------------------
VS_OUTPUT RenderSceneVS(VS_INPUT IN)
{
    VS_OUTPUT OUT;

    // transform normal into world space, then dot with world space light vector 
    // to determine how much light is falling on the surface:
    
    float3 worldNormal = normalize(mul(IN.Normal, (float3x3)WorldIT));
    float ldotn = max(dot(LightVec, worldNormal), 0.0);
    OUT.Color0 = ldotn;

    // transform model-space vertex position to light-space:
    
    OUT.TexCoord0 = mul(IN.Position, TexTransform);
    OUT.TexCoord1 = mul(IN.Position, WorldView);
    
    // transform model-space vertex position to screen space:
    
    OUT.Position = mul(IN.Position, WorldViewProj);

    return OUT;
}


float4 FilterShadowLamePS(VS_OUTPUT IN) : COLOR
{
const float SHADOW_SIZE = 512.f;

    float shadow = 0;
    for (int y=-4; y<4; y++)
        for (int x=-4; x<4; x++)
        {
            float4 coord = IN.TexCoord0;
            coord.xy += (float2(x,y)/SHADOW_SIZE) * IN.TexCoord0.w;
            shadow += tex2Dproj( ShadowMapSampler, coord );
        }
        
    shadow= smoothstep(2, 58, shadow);

    float3 color = shadow;// * IN.Color0;


    return float4(color,0);
}

float4 FilterShadowPS(VS_OUTPUT_3 IN) : COLOR 
{  
const float SHADOW_SIZE = 512.f;
    
    const float2 sample_array[20] = {
        //float2(  0.f,  0.f ) / SHADOW_SIZE,

        float2(  1.f,  0.f ) / SHADOW_SIZE,
        float2(  0.f,  1.f ) / SHADOW_SIZE,
        float2(  0.f, -1.f ) / SHADOW_SIZE,
        float2( -1.f,  0.f ) / SHADOW_SIZE,

        float2( -1.f, -1.f ) / SHADOW_SIZE,
        float2(  1.f, -1.f ) / SHADOW_SIZE,
        float2( -1.f,  1.f ) / SHADOW_SIZE,
        float2(  1.f,  1.f ) / SHADOW_SIZE,       

        float2(  0.f, -2.f ) / SHADOW_SIZE,
        float2( -2.f,  0.f ) / SHADOW_SIZE,
        float2(  0.f,  2.f ) / SHADOW_SIZE,
        float2(  2.f,  0.f ) / SHADOW_SIZE,

        float2( -1.f, -2.f ) / SHADOW_SIZE,
        float2(  1.f, -2.f ) / SHADOW_SIZE,
        float2( -2.f, -1.f ) / SHADOW_SIZE,
        float2( -2.f,  1.f ) / SHADOW_SIZE,
        
        float2( -1.f,  2.f ) / SHADOW_SIZE,
        float2(  1.f,  2.f ) / SHADOW_SIZE,
        float2(  2.f, -1.f ) / SHADOW_SIZE,
        float2(  2.f,  1.f ) / SHADOW_SIZE,
     };
  
    float2 dSdX = SHADOW_SIZE * ddx( IN.TexCoord0.xy / IN.TexCoord0.w );
    float2 dSdY = SHADOW_SIZE * ddy( IN.TexCoord0.xy / IN.TexCoord0.w );
    
    float approx_major_len = max( dot(dSdX, dSdX), dot(dSdY, dSdY) );
    float rho = 0.5f*log2(approx_major_len);

    float shadow = tex2Dproj(ShadowMapSampler, IN.TexCoord0);
    float4 offset = (float4) 0;
       
    for ( int i=0; i<4; i++ )
    {
        offset.x = dot(sample_array[i].xy, float2(0.793353,-0.608761));
        offset.y = dot(sample_array[i].xy, float2(0.608761, 0.793353));
        shadow += tex2Dproj(ShadowMapSampler, IN.TexCoord0 + offset*IN.TexCoord0.w);       
    }
    
    float3 result;
    
    if ( rho < 0.f )
    {
        float4 shadowCoord = (float4) 0;
        shadowCoord.xyz = IN.TexCoord0.xyz / IN.TexCoord0.w;
        float shadow2 = shadow;
        
        for ( int i=4; i<20; i++ )
        {
            offset.x = dot(sample_array[i].xy, float2(0.793353,-0.608761));
            offset.y = dot(sample_array[i].xy, float2(0.608761, 0.793353));
            shadow2 += tex2Dlod(ShadowMapSampler, shadowCoord + offset);
        }
        shadow2 = shadow + shadow2;
        shadow2 = smoothstep(1.f, 19.f, shadow2);
        shadow = smoothstep(1,5,shadow);
     
        float lerp_fac = saturate(-(rho));
                     
        result = lerp(float3(0,1,0), float3(1,0,0), lerp_fac);
        shadow = lerp(shadow, shadow2, lerp_fac);
    }
    else
    {
        shadow = smoothstep(1,5,shadow);
        result = float3(0,1,0);
    }  

    float3 color = result * (0.75*IN.Color0*shadow + 0.25);
      
    return float4(color, 1.0);
}

//-----------------------------------------------------------------------------

technique UseHardwareShadowMap
{
    pass P0
    {
        VertexShader = compile vs_1_1 MainVS();
        PixelShader = compile ps_1_1 MainPS();
        
        ZEnable          = True;
        AlphaBlendEnable = False;
        Lighting         = False;
        CullMode         = CCW;
    
        TexCoordIndex[0] = 0;
        TexCoordIndex[1] = 1;
        TextureTransformFlags[0] = Projected;
        TextureTransformFlags[1] = Projected;
    }
}

technique UseHardwareShadowMapGoodNoRot
{
    pass P0
    {
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader = compile ps_3_0 FilterShadowPS();
        
        ZEnable          = True;
        AlphaBlendEnable = False;
        Lighting         = False;
        CullMode         = CCW;
    
        TexCoordIndex[0] = 0;
        TexCoordIndex[1] = 1;
        //TextureTransformFlags[0] = Projected;
        //TextureTransformFlags[1] = Projected;
    }
}


technique UseHardwareShadowMapLame
{
    pass P0
    {
        VertexShader = compile vs_3_0 RenderSceneVS();
        PixelShader = compile ps_3_0 FilterShadowLamePS();
        
        ZEnable          = True;
        AlphaBlendEnable = False;
        Lighting         = False;
        CullMode         = CCW;
    
        TexCoordIndex[0] = 0;
        TexCoordIndex[1] = 1;
        //TextureTransformFlags[0] = Projected;
        //TextureTransformFlags[1] = Projected;
    }
}

technique GenHardwareShadowMap
{
    pass P0
    {
        VertexShader = compile vs_1_1 MainVS();
        PixelShader = compile ps_1_1 BlackPS();

        ZEnable          = True;
        //ZWriteEnable		 = True;
        AlphaBlendEnable = False;
        Lighting         = False;
        CullMode         = CW;  // note: not quite optimal
        
        ColorWriteEnable = 0;     // no need to render to color, we only need z
    }
}

//-----------------------------------------------------------------------------

