float invLerp(const float from, const float to, const float value)
{
    return (value - from) / (to - from);
}

float remap(const float origFrom, const float origTo, const float targetFrom, const float targetTo, const float value)
{
    const float rel = invLerp(origFrom, origTo, value);
    return lerp(targetFrom, targetTo, rel);
}

float3 rotate(float3 p, float4 quaternion)
{
    const float x = -quaternion.x;
    const float y = -quaternion.y;
    const float z = -quaternion.z;
    const float w = quaternion.w;

    const float3x3 rotation_matrix = float3x3(
        1 - 2 * (y * y + z * z), 2 * (x * y - w * z), 2 * (x * z + w * y),
        2 * (x * y + w * z), 1 - 2 * (x * x + z * z), 2 * (y * z - w * x),
        2 * (x * z - w * y), 2 * (y * z + w * x), 1 - 2 * (x * x + y * y)
    );

    return mul(rotation_matrix, p);
}

void ComputeSDFDecal_float(float SSR, float SD, float SDR, float2 softness, float2 dilate, float weight,
    float4 faceColor, out float4 rgba)
{
    float scale = 1 / SSR * SDR;
    scale /= 1 + (softness.x * scale);
    const float w = .5 - (weight + dilate.x) * .5;
    rgba = faceColor * saturate((SD - w) * scale + 0.5);

    float4 outlineColor = float4(0,1,1,1);
    float outlineWidth = dilate.y;

    // outlineColor = lerp(faceColor, outlineColor, sqrt(min(1.0, outlineWidth * scale * 2)));
    // rgba = lerp(outlineColor, faceColor, saturate((SD - w - outlineWidth) * scale + 0.5));
    // rgba *= saturate((SD - w + outlineWidth) * scale + 0.5);
}

void CalculateDecal_float(float3 position, float4 meshData, float4 uvData, float4 quaternion,
                          out float3 decalUV, out float3 decalClip)
{
    float3 local_pos = position - meshData.xyz;
    local_pos = rotate(local_pos, quaternion);
    const float width = meshData.w;
    const float height = meshData.w * uvData.w;
    local_pos.x += width / 2;
    local_pos.y += height / 2;

    decalClip = float3(
        step(0, local_pos.x) * (1 - step(width, local_pos.x)),
        step(0, local_pos.y) * (1 - step(height, local_pos.y)),
        1);

    // remap from (0..vertex_size) to (uv.start..uv.end)
    const float2 uvSize = float2(uvData.z, uvData.z * uvData.w);
    decalUV = float3(
        remap(0, width, uvData.x, uvData.x + uvSize.x, local_pos.x),
        remap(0, height, uvData.y, uvData.y + uvSize.y, local_pos.y),
        local_pos.z);
}

void ComputeSDFDecal_float(float4 faceColor, float SD, float scale, float3 decal_clip, out float4 rgba)
{
    rgba = faceColor * saturate((SD - 0.5) * scale + 0.5);
    rgba *= decal_clip.x * decal_clip.y * decal_clip.z;
}

void DecalClip_float(inout float4 color, float3 decal_clip)
{
    color *= decal_clip.x * decal_clip.y * decal_clip.z;
}

void DecalClip_float(in float4 color, float3 decal_clip, out float4 result)
{
    DecalClip_float(color, decal_clip);
    result = color;
}

void CalculateSSR_float(float2 uv, float textureHeight, bool filter, out float SSR)
{
    SSR = abs(ddx(uv.x)) + abs(ddy(uv.y));
    SSR *= textureHeight * 0.75;
}
