cbuffer ParamConstants : register(b0)
{
    float4x4 TransformMatrix;
    float4 LineColor;
    float4 BackgroundColor;
}

cbuffer Transforms : register(b1)
{
    float4x4 CameraToClipSpace;
    float4x4 ClipSpaceToCamera;
    float4x4 WorldToCamera;
    float4x4 CameraToWorld;
    float4x4 WorldToClipSpace;
    float4x4 ClipSpaceToWorld;
    float4x4 ObjectToWorld;
    float4x4 WorldToObject;
    float4x4 ObjectToCamera;
    float4x4 ObjectToClipSpace;
}

cbuffer Params : register(b2)
{
    /*{FLOAT_PARAMS}*/
}

struct vsOutput
{
    float4 position : SV_POSITION;
    float2 texCoord : TEXCOORD;
    float3 posInWorld : POSITION;
    float distToCamera : DEPTH;
};

static const float3 Quad[] =
    {
        float3(-1, -1, 0),
        float3(1, -1, 0),
        float3(1, 1, 0),
        float3(1, 1, 0),
        float3(-1, 1, 0),
        float3(-1, -1, 0),
};

vsOutput vsMain4(uint vertexId : SV_VertexID)
{
    vsOutput output;
    float2 quadVertex = Quad[vertexId].xy;
    float2 quadVertexInObject = quadVertex;
    float4 posInObj = mul(float4(quadVertexInObject, 0, 1), TransformMatrix);

    output.position = mul(posInObj, ObjectToClipSpace);
    output.posInWorld = mul(posInObj, ObjectToWorld).xyz;
    output.texCoord = quadVertex * float2(0.5, -0.5) + 0.5;
    return output;
}

sampler ClampedSampler : register(s0);
sampler WrappedSampler : register(s1);


//=== Resources =====================================================
/*{RESOURCES(t0)}*/

//=== Globals =======================================================
/*{GLOBALS}*/

//=== Field functions ===============================================
/*{FIELD_FUNCTIONS}*/

//-------------------------------------------------------------------
float4 GetField(float4 p)
{
    float4 f = 1;
    /*{FIELD_CALL}*/
    return f;
}

float GetDistance(float3 p3)
{
    return GetField(float4(p3.xyz, 0)).w;
}

//===================================================================

const static float NormalSamplingDistance = 0.01;

float3 GetNormalNonNormalized(float3 p)
{
    // return normalize(
    return GetDistance(p + float3(NormalSamplingDistance, -NormalSamplingDistance, -NormalSamplingDistance)) * float3(1, -1, -1) +
           GetDistance(p + float3(-NormalSamplingDistance, NormalSamplingDistance, -NormalSamplingDistance)) * float3(-1, 1, -1) +
           GetDistance(p + float3(-NormalSamplingDistance, -NormalSamplingDistance, NormalSamplingDistance)) * float3(-1, -1, 1) +
           GetDistance(p + float3(NormalSamplingDistance, NormalSamplingDistance, NormalSamplingDistance)) * float3(1, 1, 1);
    //);
}

#define PI acos(-1.)
#define INFINITY pow(2., 8.)

float4 DistanceMeter(float dist, float rayLength, float3 rayDir, float camHeight, float horizonFade)
{
    float idealGridDistance = camHeight;
    float nearestBase = floor(log(idealGridDistance) / log(10.0));
    float relativeDist = abs(dist / camHeight);

    float largerDistance = pow(10.0, nearestBase + 1.0);
    float smallerDistance = pow(10.0, nearestBase);

    float3 col = 1;
    col = max(float3(0.0, 0.0, 0.0), col);
    if (sign(dist) < 0.0)
    {
        // col = col.grb * 0.1;
    }

    float l0 = pow(0.5 + 0.5 * cos(dist * 3.14159265359 * 2.0 * smallerDistance), 20.0);
    float l1 = pow(0.5 + 0.5 * cos(dist * 3.14159265359 * 2.0 * largerDistance), 5.0);
    float l = log(idealGridDistance) / log(10.0);
    float x = frac(l);
    l0 = lerp(l0, 0.0, smoothstep(0.4, 1.0, x));
    l1 = lerp(0.0, l1, smoothstep(0.0, 0.4, x));

    float lines = pow(1 - (0.01 + 0.99 * (1.0 - l0) * (1.0 - l1)), 20);
    float cutline = pow(1 - clamp(abs(dist), 0, 1) + 0.01 / camHeight, 20);

    float inside = sign(dist) < 0 ? 0.3 : 0;

    float lineAlpha = (lines * 0.7 + cutline + inside) * horizonFade;
    return lerp(BackgroundColor, LineColor, lineAlpha);
}

float4 psMain(vsOutput input) : SV_TARGET
{
    float dist = GetDistance(input.posInWorld);
    float2 s = length(float2(ddy(input.texCoord.x), ddy(input.texCoord.y)));

    float3 camPos = mul(float4(0, 0, 0, 1), CameraToWorld).xyz;
    float dToCam = length(camPos - input.posInWorld);
    float3 ray = float3(0, 0, 1);

    float sideView = saturate(max(abs(s.x), abs(s.y)) * 1000);

    float lipschitz = pow(length(GetNormalNonNormalized(input.posInWorld)) - 0.0, 0.5);
    // return float4( lipschitz.xxx,1);
    return DistanceMeter(dist, 0.1, ray, 15 / dToCam, 1 - sideView);
    // + float4( lipschitz.xxx,1);
    //+ float4( length(lipschitz).xxx / 3,1);
}
