struct VIn
{
    float4 p   : POSITION;
    float3 n   : NORMAL;
    float2 uv  : TEXCOORD0;
};

struct VOut
{
    float4 p   : POSITION;
    float2 uv  : TEXCOORD0;
    float3 ray : TEXCOORD1;
};

struct PIn
{
    float2 uv  : TEXCOORD0;
    float3 ray : TEXCOORD1;
};

VOut ssao_vs(VIn IN, uniform float4x4 wvp, uniform float3 farCorner)
{
    VOut OUT;
    OUT.p = mul(wvp, IN.p);
    // clean up inaccuracies for the UV coords
    float2 uv = sign(IN.p);
    // convert to image space
    uv = (float2(uv.x, -uv.y) + 1.0) * 0.5;
    OUT.uv = uv;
    // calculate the correct ray (modify XY parameters based on screen-space quad XY)
    OUT.ray = farCorner * float3(sign(IN.p.xy), 1);
    return OUT;
}

float3 computeZ(float2 xy)
{
    return float3(xy, sqrt(1.0 - dot(xy, xy)));
}

// for ps_3_0, we want to use tex2Dlod because it's faster
ps_3_0 float4 TEX2DLOD(sampler2D map, float2 uv)
{
    return tex2Dlod(map, float4(uv.xy, 0, 0));
}

float4 TEX2DLOD(sampler2D map, float2 uv)
{
    return tex2D(map, uv);
}

float4 ssao_ps(
    PIn IN,
    uniform float4x4 ptMat,
    uniform float far,
    uniform sampler2D geomMap : TEXUNIT0,
    uniform sampler2D randMap  : TEXUNIT1): COLOR0
{
    #define MAX_RAND_SAMPLES 14

    const float3 RAND_SAMPLES[MAX_RAND_SAMPLES] =
    {
        float3(1, 0, 0),
        float3(	-1, 0, 0),
        float3(0, 1, 0),
        float3(0, -1, 0),
        float3(0, 0, 1),
        float3(0, 0, -1),
        normalize(float3(1, 1, 1)),
        normalize(float3(-1, 1, 1)),
        normalize(float3(1, -1, 1)),
        normalize(float3(1, 1, -1)),
        normalize(float3(-1, -1, 1)),
        normalize(float3(-1, 1, -1)),
        normalize(float3(1, -1, -1)),
        normalize(float3(-1, -1, -1))
    };

    // constant expressin != const int :(
    #define NUM_BASE_SAMPLES 6

    // random normal lookup from a texture and expand to [-1..1]
    float3 randN = TEX2DLOD(randMap, IN.uv * 24).xyz * 2.0 - 1.0;
    float4 geom = TEX2DLOD(geomMap, IN.uv);
    float depth = geom.w;

    // IN.ray will be distorted slightly due to interpolation
    // it should be normalized here
    float3 viewPos = IN.ray * depth;

    // by computing Z manually, we lose some accuracy under extreme angles
    // considering this is just for bias, this loss is acceptable
    float3 viewNorm = geom.xyz;//computeZ(geom.yz);

    // accumulated occlusion factor
    float occ = 0;
    for (int i = 0; i < NUM_BASE_SAMPLES; ++i)
    {
        // reflected direction to move in for the sphere
        // (based on random samples and a random texture sample)
        // bias the random direction away from the normal
        // this tends to minimize self occlusion
        float3 randomDir = reflect(RAND_SAMPLES[i], randN) + viewNorm;

        // move new view-space position back into texture space
        #define RADIUS 0.2125
        float4 nuv = mul(ptMat, float4(viewPos.xyz + randomDir * RADIUS, 1));
        nuv.xy /= nuv.w;

        // compute occlusion based on the (scaled) Z difference
        float zd = saturate(far * (depth - TEX2DLOD(geomMap, nuv.xy).w));
        // this is a sample occlusion function, you can always play with
        // other ones, like 1.0 / (1.0 + zd * zd) and stuff
        occ += saturate(pow(1.0 - zd, 11) + zd);
    }
    occ /= NUM_BASE_SAMPLES;

    return float4(occ, occ, occ, 1);
}

#define NUM_BLUR_SAMPLES 8

float4 ssaoBlurX_ps(float2 uv : TEXCOORD0,
    uniform float4 invTexSize,
    uniform sampler2D map : TEXUNIT0, uniform sampler2D geomMap : TEXUNIT1): COLOR0
{
//    return TEX2DLOD(ssaoMap, uv);
    float2 o = float2(invTexSize.x, 0);
    float4 sum = TEX2DLOD(map, uv) * (NUM_BLUR_SAMPLES + 1);
    float denom = NUM_BLUR_SAMPLES + 1;
    float4 geom = TEX2DLOD(geomMap, uv);
    for (int i = 1; i <= NUM_BLUR_SAMPLES; ++i)
    {
        float2 nuv = uv + o * i;
        float4 nGeom = TEX2DLOD(geomMap, nuv);
        float coef = (NUM_BLUR_SAMPLES + 1 - i) * (dot(geom.xyz, nGeom.xyz) > 0.9);
        sum += TEX2DLOD(map, nuv) * coef;
        denom += coef;
    }
    for (int i = 1; i <= 4; ++i)
    {
        float2 nuv = uv + o * -i;
        float4 nGeom = TEX2DLOD(geomMap, nuv);
        float coef = (NUM_BLUR_SAMPLES + 1 - i) * (dot(geom.xyz, nGeom.xyz) > 0.9);
        sum += TEX2DLOD(map, nuv) * coef;
        denom += coef;
    }
    return sum / denom;
}

float4 ssaoBlurY_ps(float2 uv : TEXCOORD0,
    uniform float4 invTexSize,
    uniform sampler2D map : TEXUNIT0, uniform sampler2D geomMap : TEXUNIT1): COLOR0
{
//    return TEX2DLOD(map, uv);
    float2 o = float2(0, invTexSize.y);
    float4 sum = TEX2DLOD(map, uv) * (NUM_BLUR_SAMPLES + 1);
    float denom = NUM_BLUR_SAMPLES + 1;
    float4 geom = TEX2DLOD(geomMap, uv);
    for (int i = 1; i <= NUM_BLUR_SAMPLES; ++i)
    {
        float2 nuv = uv + o * i;
        float4 nGeom = TEX2DLOD(geomMap, nuv);
        float coef = (NUM_BLUR_SAMPLES + 1 - i) * (dot(geom.xyz, nGeom.xyz) > 0.9);
        sum += TEX2DLOD(map, nuv) * coef;
        denom += coef;
    }
    for (int i = 1; i <= 4; ++i)
    {
        float2 nuv = uv + o * -i;
        float4 nGeom = TEX2DLOD(geomMap, nuv);
        float coef = (NUM_BLUR_SAMPLES + 1 - i) * (dot(geom.xyz, nGeom.xyz) > 0.9);
        sum += TEX2DLOD(map, nuv) * coef;
        denom += coef;
    }
    return sum / denom;
}


