//================================================================
// EQUINOX-3D plugin
//
// Universal importance sampling
//
// AUTHOR:  Gabor Nagy
// DATE:    2025-Feb-05 02:54:46
//
// Copyright (C) 1996-2026 By Gabor Nagy. All rights reserved.
//================================================================


//
// Stratified importance sampling
//

#define _E3dShader_CDFCmp(pIdx)  (lSamplesInCDF[pIdx].CDFPosition < lN0)

//========================================================================================
// CDF-based importance-sampling for shaders with a non-invertible PDF - single-sample
//========================================================================================
#define E3dShader_StratIS1(pISBinsSqrt, pSampleFunc) do\
{\
/* Super-sample a shader lobe and build CDF */\
\
	const E3dCoordinate  lISBinsNSqrtInv = 1.0 / (E3dCoordinate)pISBinsSqrt;\
	const EIndex         lNBins = pISBinsSqrt * pISBinsSqrt;\
	const E3dCoordinate  lISBinsNInv = 1.0 / (E3dCoordinate)lNBins;\
	E3dShaderLight       lBins[lNBins];\
\
	E3dShaderLight*  lOutSample = PSamples + LCount;\
/* The weight is the BRDF divided by the probability distribution function (PDF). */\
/* The latter is the CDF probability divided by the total accumulated value (lCDFPosition) */\
	int  lUI = 0, LICount = 0;E3dCoordinate  lU = 0.0;E3dShaderLight*  LISample = lBins;float  lCDFPosition = 0.0;for(;lUI < pISBinsSqrt;lUI++, lU += 1.0)\
	{\
		int  lVI = 0;E3dCoordinate  lV = 0.0;for(;lVI < pISBinsSqrt;lVI++, lV += 1.0)\
		{\
			E3dCoord3  LPointToLight;E3d_SampleSphere((lU + E_RandomDN(PRandomState)) * lISBinsNSqrtInv, (lV + E_RandomDN(PRandomState)) * lISBinsNSqrtInv,  &LPointToLight);\
\
			pSampleFunc;\
		}\
	}\
\
/* Now, pick a sample from the CDF.  */\
	if(LICount > 0)\
	{\
		if(LICount > 1)\
		{\
			const E3dShaderLight*  lSamplesInCDF = lBins;\
			const E3dCoordinate    lN0 = (E3dCoordinate)E_RandomDN(PRandomState) * lCDFPosition;\
\
			EIndex  lIndex;E_BinarySearch(LICount, _E3dShader_CDFCmp, lIndex);\
			*lOutSample = lSamplesInCDF[lIndex];\
		}\
		else *lOutSample = lBins[0];\
\
		lOutSample->PointToLightNew.W = -1.0;\
		lOutSample->Weight = 4.0 * lISBinsNInv / lOutSample->Weight * lCDFPosition;   /* We need to normalize the weight and invert it, as pSampleFunc did not know the total lCDFPosition and it stores the unnormalized probability, so we can save the divide for unused samples. */\
		lOutSample->SolidAngle = 0.0;         /* E3dFIXME compute this */\
		LCount++;\
	}\
} while(0)


//========================================================================================
// CDF-based importance-sampling for shaders with a non-invertible PDF - multi-sample
//========================================================================================
#define E3dShader_StratIS(pNOutSamplesSqrt, pISBinsSqrt, pSampleFunc) do\
{\
/* Super-sample a shader lobe for CDF-based importance-sampling */\
\
	const EIndex         lNOutSamples = pNOutSamplesSqrt * pNOutSamplesSqrt;\
	const E3dCoordinate  lISBinsNSqrtInv = 1.0 / (E3dCoordinate)pISBinsSqrt;\
	const EIndex         lNBins = pISBinsSqrt * pISBinsSqrt;\
	const E3dCoordinate  lISBinsNInv = 1.0 / (E3dCoordinate)lNBins;\
	E3dShaderLight       lBins[lNBins];\
\
	EIndex  lI = 0;E3dShaderLight*  lOutSample = PSamples + LCount;for(;lI < lNOutSamples;lI++)\
	{\
/* The weight is the BRDF divided by the probability distribution function (PDF). */\
/* The latter is the CDF probability divided by the total accumulated value (lCDFPosition) */\
		int  lUI = 0, LICount = 0;E3dCoordinate  lU = 0.0;E3dShaderLight*  LISample = lBins;float  lCDFPosition = 0.0;for(;lUI < pISBinsSqrt;lUI++, lU += 1.0)\
		{\
			int  lVI = 0;E3dCoordinate  lV = 0.0;for(;lVI < pISBinsSqrt;lVI++, lV += 1.0)\
			{\
				E3dCoord3  LPointToLight;E3d_SampleSphere((lU + E_RandomDN(PRandomState)) * lISBinsNSqrtInv, (lV + E_RandomDN(PRandomState)) * lISBinsNSqrtInv,  &LPointToLight);\
\
				pSampleFunc;\
			}\
		}\
\
/* Now, pick a sample from the CDF.  */\
		if(LICount > 0)\
		{\
			if(LICount > 1)\
			{\
				const E3dShaderLight*  lSamplesInCDF = lBins;\
				const E3dCoordinate    lN0 = (E3dCoordinate)E_RandomDN(PRandomState) * lCDFPosition;\
\
				EIndex  lIndex;E_BinarySearch(LICount, _E3dShader_CDFCmp, lIndex);\
				*lOutSample = lSamplesInCDF[lIndex];\
			}\
			else *lOutSample = lBins[0];\
\
			lOutSample->PointToLightNew.W = -1.0;\
			lOutSample->Weight = 4.0 * lISBinsNInv / lOutSample->Weight * lCDFPosition;   /* We need to normalize the weight and invert it, as pSampleFunc did not know the total lCDFPosition and it stores the unnormalized probability, so we can save the divide for unused samples. */\
			lOutSample->SolidAngle = 0.0;         /* E3dFIXME compute this */\
			lOutSample++;\
			LCount++;\
		}\
	}\
} while(0)
