// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.

#ifndef __MONTE_CARLO__
#define __MONTE_CARLO__

float3 TangentToWorld(float3 vec, float3 tangentZ)
{
    float3 upVector = abs(tangentZ.z) < 0.999 ? float3(0, 0, 1) : float3(1, 0, 0);
    float3 tangentX = normalize(cross(upVector, tangentZ));
    float3 tangentY = cross(tangentZ, tangentX);
    return tangentX * vec.x + tangentY * vec.y + tangentZ * vec.z;
}

uint ReverseBits32(uint bits)
{
#if FEATURE_LEVEL >= FEATURE_LEVEL_SM5
    return reversebits(bits);
#else
	bits = ( bits << 16) | ( bits >> 16);
	bits = ( (bits & 0x00ff00ff) << 8 ) | ( (bits & 0xff00ff00) >> 8 );
	bits = ( (bits & 0x0f0f0f0f) << 4 ) | ( (bits & 0xf0f0f0f0) >> 4 );
	bits = ( (bits & 0x33333333) << 2 ) | ( (bits & 0xcccccccc) >> 2 );
	bits = ( (bits & 0x55555555) << 1 ) | ( (bits & 0xaaaaaaaa) >> 1 );
	return bits;
#endif
}

float2 Hammersley(uint index, uint numSamples, uint2 random)
{
    float e1 = frac((float)index / numSamples + float(random.x & 0xffff) / (1 << 16));
    float e2 = float(ReverseBits32(index) ^ random.y) * 2.3283064365386963e-10;
    return float2(e1, e2);
}

float4 UniformSampleSphere(float2 e)
{
    float phi = 2 * PI * e.x;
    float cosTheta = 1 - 2 * e.y;
    float sinTheta = sqrt(1 - cosTheta * cosTheta);

    float3 h;
    h.x = sinTheta * cos(phi);
    h.y = sinTheta * sin(phi);
    h.z = cosTheta;

    float pdf = 1.0 / (4 * PI);
    return float4(h, pdf);
}

float4 UniformSampleHemisphere(float2 e)
{
    float phi = 2 * PI * e.x;
    float cosTheta = e.y;
    float sinTheta = sqrt(1 - cosTheta * cosTheta);

    float3 h;
    h.x = sinTheta * cos(phi);
    h.y = sinTheta * sin(phi);
    h.z = cosTheta;

    float pdf = 1.0 / (2 * PI);
    return float4(h, pdf);
}

float4 CosineSampleHemisphere(float2 e)
{
    float phi = 2 * PI * e.x;
    float cosTheta = sqrt(e.y);
    float sinTheta = sqrt(1 - cosTheta * cosTheta);

    float3 h;
    h.x = sinTheta * cos(phi);
    h.y = sinTheta * sin(phi);
    h.z = cosTheta;

    float pdf = cosTheta / PI;
    return float4(h, pdf);
}

float4 UniformSampleCone(float2 e, float cosThetaMax)
{
    float phi = 2 * PI * e.x;
    float cosTheta = lerp(cosThetaMax, 1, e.y);
    float sinTheta = sqrt(1 - cosTheta * cosTheta);

    float3 l;
    l.x = sinTheta * cos(phi);
    l.y = sinTheta * sin(phi);
    l.z = cosTheta;

    float pdf = 1.0 / (2 * PI * (1 - cosThetaMax));
    return float4(l, pdf);
}

float4 ImportanceSampleBlinn(float2 e, float roughness)
{
    float m = roughness * roughness;
    float n = 2 / (m * m) - 2;

    float phi = 2 * PI * e.x;
    float cosTheta = ClampedPow(e.y, 1 / (n + 1));
    float sinTheta = sqrt(1 - cosTheta * cosTheta);

    float3 h;
    h.x = sinTheta * cos(phi);
    h.y = sinTheta * sin(phi);
    h.z = cosTheta;

    float d = (n + 2) / (2 * PI) * ClampedPow(cosTheta, n);
    float pdf = d * cosTheta;
    return float4(h, pdf);
}

float4 ImportanceSampleGGX(float2 e, float roughness)
{
    float m = roughness * roughness;
    float m2 = m * m;

    float phi = 2 * PI * e.x;
    float cosTheta = sqrt((1 - e.y) / (1 + (m2 - 1) * e.y));
    float sinTheta = sqrt(1 - cosTheta * cosTheta);

    float3 h;
    h.x = sinTheta * cos(phi);
    h.y = sinTheta * sin(phi);
    h.z = cosTheta;

    float r = (cosTheta * m2 - cosTheta) * cosTheta + 1;
    float d = m2 / (PI * r * r);
    float pdf = d * cosTheta;
    return float4(h, pdf);
}

// Multiple importance sampling power heuristic of two functions with a power of two. 
// [Veach 1997, "Robust Monte Carlo Methods for Light Transport Simulation"]
float MISWeight(uint number, float PDF, uint otherNumber, float otherPDF)
{
    float weight = number * PDF;
    float otherWeight = otherNumber * otherPDF;
    return weight * weight / (weight * weight + otherWeight * otherWeight);
}

#endif
