/////////////////////////////////
// BEGIN Custom Sampling Functions
Texture1D HHGGTex;

// cubic b-spline
float bsW0(float a)
{
    return (1.0 / 6.0 * (-(a * a * a) + (3.0 * a * a) - (3.0 * a) + 1.0));
}

float bsW1(float a)
{
    return (1.0 / 6.0 * ((3.0 * a * a * a) - (6.0 * a * a) + 4.0));
}

float bsW2(float a)
{
    return (1.0 / 6.0 * (-(3.0 * a * a * a) + (3.0 * a * a) + (3.0 * a) + 1.0));
}

float bsW3(float a)
{
    return (1.0 / 6.0 * a * a * a);
}

float g0(float a)
{
    return (bsW0(a) + bsW1(a));
}

float g1(float a)
{
    return (bsW2(a) + bsW3(a));
}

float h0texels(float a)
{
    return (1.0 + a - (bsW1(a) / (bsW0(a) + bsW1(a))));
}

float h1texels(float a)
{
    return (1.0 - a + (bsW3(a) / (bsW2(a) + bsW3(a))));
}
/// end cubic-bspline

// first derivative of cubic b-spline
float bsfdW0(float a)
{
    return (1.0 / 6.0 * (-(3.0 * a * a) + (6.0 * a) - 3.0));
}

float bsfdW1(float a)
{
    return (1.0 / 6.0 * ((9.0 * a * a) - (12.0 * a)));
}

float bsfdW2(float a)
{
    return (1.0 / 6.0 * (-(9.0 * a * a) + (6.0 * a) + 3.0));
}

float bsfdW3(float a)
{
    return (1.0 / 6.0 * 3.0 * a * a);
}

float gfd0(float a)
{
    return (bsfdW0(a) + bsfdW1(a));
}

float gfd1(float a)
{
    return (bsfdW2(a) + bsfdW3(a));
}

float hfd0texels(float a)
{
    return (1.0 + a - (bsfdW1(a) / (bsfdW0(a) + bsfdW1(a))));
}

float hfd1texels(float a)
{
    return (1.0 - a + (bsfdW3(a) / (bsfdW2(a) + bsfdW3(a))));
}
/// end first derivative of cubic b-spline

float4 getHHGG(float xTexels)
{
    //    float a = frac(xTexels);
    //    return float4( -h0texels(a), h1texels(a), 1.0-g0(a), g0(a) );

    return HHGGTex.SampleLevel(samRepeat, xTexels, 0);
}

float4 getfdHHGG(float xTexels)
{
    float a = frac(xTexels);
    return float4(-hfd0texels(a), hfd1texels(a), gfd1(a), -gfd1(a));
}

float4 SampleTricubicGeneric(Texture3D tex, float3 tc, float4 hg_x, float4 hg_y, float4 hg_z)
{
    float3 tc100, tc000, tc110, tc010,
        tc101, tc001, tc111, tc011;

    tc100 = tc;
    tc000 = tc;
    tc100.x += (hg_x.x * recGridDim.x);
    tc000.x += (hg_x.y * recGridDim.x);

    tc110 = tc100;
    tc010 = tc000;
    tc110.y += (hg_y.x * recGridDim.y);
    tc010.y += (hg_y.x * recGridDim.y);
    tc100.y += (hg_y.y * recGridDim.y);
    tc000.y += (hg_y.y * recGridDim.y);

    tc111 = tc110;
    tc011 = tc010;
    tc101 = tc100;
    tc001 = tc000;
    tc111.z += (hg_z.x * recGridDim.z);
    tc011.z += (hg_z.x * recGridDim.z);
    tc101.z += (hg_z.x * recGridDim.z);
    tc001.z += (hg_z.x * recGridDim.z);

    float4 v001 = tex.SampleLevel(samLinearClamp, tc001, 0);
    float4 v011 = tex.SampleLevel(samLinearClamp, tc011, 0);
    float4 v101 = tex.SampleLevel(samLinearClamp, tc101, 0);
    float4 v111 = tex.SampleLevel(samLinearClamp, tc111, 0);

    float4 v0Y1 = (v001 * hg_y.z) + (v011 * hg_y.w);
    float4 v1Y1 = (v101 * hg_y.z) + (v111 * hg_y.w);

    float4 vXY1 = (v0Y1 * hg_x.z) + (v1Y1 * hg_x.w);

    tc110.z += (hg_z.y * recGridDim.z);
    tc010.z += (hg_z.y * recGridDim.z);
    tc100.z += (hg_z.y * recGridDim.z);
    tc000.z += (hg_z.y * recGridDim.z);

    float4 v000 = tex.SampleLevel(samLinearClamp, tc000, 0);
    float4 v010 = tex.SampleLevel(samLinearClamp, tc010, 0);
    float4 v100 = tex.SampleLevel(samLinearClamp, tc100, 0);
    float4 v110 = tex.SampleLevel(samLinearClamp, tc110, 0);

    float4 v0Y0 = (v000 * hg_y.z) + (v010 * hg_y.w);
    float4 v1Y0 = (v100 * hg_y.z) + (v110 * hg_y.w);

    float4 vXY0 = (v0Y0 * hg_x.z) + (v1Y0 * hg_x.w);

    float4 vXYZ = (vXY0 * hg_z.z) + (vXY1 * hg_z.w);

    return vXYZ;
}

float4 SampleTricubic(Texture3D tex, float3 tc)
{
    float3 tcTexels = (tc * gridDim.xyz) - 0.49;

    float4 hg_x = getHHGG(tcTexels.x);
    float4 hg_y = getHHGG(tcTexels.y);
    float4 hg_z = getHHGG(tcTexels.z);

    return SampleTricubicGeneric(tex, tc, hg_x, hg_y, hg_z);
}

float4 SampleGradientTricubic(Texture3D tex, float3 tc)
{
    float3 tcTexels = (tc * gridDim.xyz) - 0.49;

    float4 hg_x = getHHGG(tcTexels.x);
    float4 hg_y = getHHGG(tcTexels.y);
    float4 hg_z = getHHGG(tcTexels.z);
    float4 hgfd_x = getfdHHGG(tcTexels.x);
    float4 hgfd_y = getfdHHGG(tcTexels.y);
    float4 hgfd_z = getfdHHGG(tcTexels.z);

    return float4(SampleTricubicGeneric(tex, tc, hgfd_x, hg_y, hg_z).r,
        SampleTricubicGeneric(tex, tc, hg_x, hgfd_y, hg_z).r,
        SampleTricubicGeneric(tex, tc, hg_x, hg_y, hgfd_z).r, 1.0);
}

float4 SampleTrilinear(Texture3D tex, float3 tc)
{
    return tex.SampleLevel(samLinearClamp, tc, 0);
}

float4 SampleGradientTrilinear(Texture3D tex, float3 tc)
{
#define LEFTCELL float3(tc.x - (1.0 / gridDim.x), tc.y, tc.z)
#define RIGHTCELL float3(tc.x + (1.0 / gridDim.x), tc.y, tc.z)
#define BOTTOMCELL float3(tc.x, (tc.y - (1.0 / gridDim.y)), tc.z)
#define TOPCELL float3(tc.x, (tc.y + (1.0 / gridDim.y)), tc.z)
#define DOWNCELL float3(tc.x, tc.y, tc.z - (1.0 / gridDim.z))
#define UPCELL float3(tc.x, tc.y, tc.z + (1.0 / gridDim.z))

    float4 texL = tex.SampleLevel(samLinearClamp, LEFTCELL, 0);
    float4 texR = tex.SampleLevel(samLinearClamp, RIGHTCELL, 0);
    float4 texB = tex.SampleLevel(samLinearClamp, BOTTOMCELL, 0);
    float4 texT = tex.SampleLevel(samLinearClamp, TOPCELL, 0);
    float4 texU = tex.SampleLevel(samLinearClamp, UPCELL, 0);
    float4 texD = tex.SampleLevel(samLinearClamp, DOWNCELL, 0);
    return float4(texR.r - texL.r, texT.r - texB.r, texU.r - texD.r, 1);
}

float4 Sample(Texture3D tex, float3 tc)
{
    if (g_bRaycastFilterTricubic)
    {
        return SampleTricubic(tex, tc);
    }
    else
    {
        return SampleTrilinear(tex, tc);
    }
}

float4 SampleGradient(Texture3D tex, float3 tc)
{
    if (g_bRaycastFilterTricubic)
    {
        return SampleGradientTricubic(tex, tc);
    }
    else
    {
        return SampleGradientTrilinear(tex, tc);
    }
}
// END Custom Sampling Functions
/////////////////////////////////
