#ifndef GEO_UTILITIES_H
#define GEO_UTILITIES_H

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl"

struct CylinderBound
{
    float3 center;
    float3 axis;
    float halfHeight;
    float capRadius;
};

struct SphereBound
{
    float3 center;
    float radius;
};

float CompleteSinCos(float sinOrCos)
{
    return sqrt(max(1.0f - sinOrCos*sinOrCos, 0.0f));
}

float2 ProjectedHalfLengths(CylinderBound cylinder, float3 planeNormal)
{
    float absCosTheta = abs(dot(planeNormal, cylinder.axis));
    float sinTheta = CompleteSinCos(absCosTheta);

    float h = cylinder.halfHeight;
    float r = cylinder.capRadius;

    float halfLengthAlongNormal = absCosTheta * h + sinTheta * r;
    float halfLengthInPlane = max(sinTheta * h + absCosTheta * r, r); // ellipse, so use max of two axis lengths

    return float2(halfLengthInPlane, halfLengthAlongNormal);
}

struct BoundingObjectData
{
    float3 frontCenterPosRWS;
    float2 centerPosNDC;
    float2 radialPosNDC;
};

BoundingObjectData CalculateBoundingObjectData(SphereBound boundingSphere,
    float4x4 viewProjMatrix,
    float4 viewOriginWorldSpace,
    float4 radialDirWorldSpace,
    float4 facingDirWorldSpace)
{
    const float3 centerPosRWS = boundingSphere.center - viewOriginWorldSpace.xyz;

    const float3 radialVec = abs(boundingSphere.radius) * radialDirWorldSpace.xyz;
    const float3 facingVec = abs(boundingSphere.radius) * facingDirWorldSpace.xyz;

    BoundingObjectData data;
    data.centerPosNDC = ComputeNormalizedDeviceCoordinates(centerPosRWS, viewProjMatrix);
    data.radialPosNDC = ComputeNormalizedDeviceCoordinates(centerPosRWS + radialVec, viewProjMatrix);
    data.frontCenterPosRWS = centerPosRWS + facingVec;
    return data;
}

BoundingObjectData CalculateBoundingObjectData(CylinderBound cylinderBound,
    float4x4 viewProjMatrix,
    float4 viewOriginWorldSpace,
    float4 radialDirWorldSpace,
    float4 facingDirWorldSpace)
{
    const float3 centerPosRWS = cylinderBound.center - viewOriginWorldSpace.xyz;

    const float2 halfLengths = ProjectedHalfLengths(cylinderBound, facingDirWorldSpace.xyz);
    const float3 radialVec = halfLengths.x * radialDirWorldSpace.xyz;

    BoundingObjectData data;
    data.centerPosNDC = ComputeNormalizedDeviceCoordinates(centerPosRWS, viewProjMatrix);
    data.radialPosNDC = ComputeNormalizedDeviceCoordinates(centerPosRWS + radialVec, viewProjMatrix);
    data.frontCenterPosRWS = centerPosRWS + halfLengths.y * facingDirWorldSpace.xyz;
    return data;
}

#endif