#include "RayTypes.h"

StructuredBuffer<Sphere> Spheres : register(t0, space0);
StructuredBuffer<SphereRay> Rays : register(t1, space0);
RWStructuredBuffer<SphereHit> Hits : register(u0, space1);

cbuffer UBO : register(b0, space2)
{
    int4 ubo_size : packoffset(c0);
    int spheresCount : packoffset(c1.x);
};

float RayIntersectSphere(SphereRay ray, Sphere sphere)
{
    float3 oc = ray.Position.xyz - sphere.PositionAndRadius.xyz;
    if (length(oc) <= sphere.PositionAndRadius.w)
        return 0.0;
        float b = 2.0 * dot(ray.Heading.xyz, oc);
    float c = dot(oc, oc) - sphere.PositionAndRadius.w * sphere.PositionAndRadius.w;
    float discriminant = b * b - 4.0 * c;
    if (discriminant < 0.0)
        return 0.0;
    float sqrtD = sqrt(discriminant);
    float t1 = (-b - sqrtD) / 2.0;
    float t2 = (-b + sqrtD) / 2.0;
    if (t1 > 0.001 && t2 > 0.001)
        return min(t1, t2);
    if (t1 > 0.001)
        return t1;
    if (t2 > 0.001)
        return t2;
    return 0.0;
}

#define MaxSpheresCount 64

[numthreads(64, 1, 1)]
void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
{
    uint n = GlobalInvocationID.x;

    SphereRay ray = Rays[n];

    int bestSphere = spheresCount;
    float bestHit = 99999;
    int hitCount = 0;
    [unroll]
    for (int i = 0; i < MaxSpheresCount; ++i)
    {
        if (i < spheresCount)
        {
            Sphere sphere = Spheres[i];
            float t = RayIntersectSphere(ray, sphere);
            if (t > 0)
                hitCount++;
            if ((t > 0) && (t < bestHit))
            {
                bestSphere = i;
                bestHit = t;
            }
        }
    }
    SphereHit sh;

    sh.RayDistance = bestHit;
    sh.SphereId = bestSphere;
    sh.HitCount = hitCount;
    Hits[n] = sh;
}
