//////////////////////////////////////////////////////////////////////
//
//                             Pixie
//
// Copyright © 1999 - 2003, Okan Arikan
//
// Contact: okan@cs.berkeley.edu
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
//  File				:	stochastic.cpp
//  Classes				:	CStochastic
//  Description			:	implements the stochastic hider
//
////////////////////////////////////////////////////////////////////////
#include <math.h>

#include "stochastic.h"
#include "renderer.h"
#include "memory.h"
#include "random.h"

static	int	numFragments	=	0;

// This macro is used to allocate fragments
#define	newFragment(__a)	if (freeFragments == NULL)	{						\
								__a					=	new CFragment;			\
							} else {											\
								__a					=	freeFragments;			\
								freeFragments		=	freeFragments->next;	\
							}													\
							numFragments++;

// And deallocate macro
#define	deleteFragment(__a)	__a->next				=	freeFragments;			\
							freeFragments			=	__a;					\
							numFragments--;


///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	CStochastic
// Description			:	Ctor
// Return Value			:	-
// Comments				:
// Date last edited		:	7/31/2002
CStochastic::CStochastic(COptions *o,CXform *x,SOCKET s) : CReyes(o,x,s,0), COcclusionCuller() {
	int		i,j;
	float	*cExtraSample;
	CPixel	*cPixel;

	// The maximum width/height we should handle
	totalWidth		=	pixelXsamples*bucketWidth + 2*xSampleOffset;
	totalHeight		=	pixelYsamples*bucketHeight + 2*ySampleOffset;

	// Allocate the framebuffer
	if (numExtraSamples > 0)	extraSampleMemory	=	new float[totalWidth*totalHeight*numExtraSamples];
	else						extraSampleMemory	=	NULL;

	cExtraSample	=	extraSampleMemory;
	fb				=	new CPixel*[totalHeight];
	for (i=0;i<totalHeight;i++) {
		cPixel		=	fb[i]		=	new CPixel[totalWidth];

		for (j=totalHeight;j>0;j--,cPixel++,cExtraSample+=numExtraSamples)	cPixel->extraSamples	=	cExtraSample;
	}

	// Init the fragment buffer
	freeFragments	=	NULL;

	// Initialize the occlusion culler
	initCuller(max(totalWidth,totalHeight), &maxDepth);

}

///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	~CStochastic
// Description			:	Dtor
// Return Value			:	-
// Comments				:
// Date last edited		:	7/31/2002
CStochastic::~CStochastic() {
	int			i;
	CFragment	*cFragment;

	// Ditch the memory we allocated for extra samples
	if (extraSampleMemory != NULL) delete [] extraSampleMemory;

	// Ditch the pixels
	for (i=0;i<totalHeight;i++) {
		delete [] fb[i];
	}
	delete [] fb;

	// Ditch the extra fragments
	while((cFragment = freeFragments) != NULL) {
		freeFragments	=	cFragment->next;
		delete cFragment;
	}
}

///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	rasterBegin
// Description			:	Begin drawing an image
// Return Value			:	-
// Comments				:
// Date last edited		:	7/31/2002
void		CStochastic::rasterBegin(int w,int h,int l,int t) {
	int			i,j,k;
	float		zoldStart;
	CFragment	*cFragment;
	CSobol<1>	timeGenerator;
	CSobol<2>	apertureGenerator;


	assert(numFragments == 0);


	switch(depthFilter) {
	case DEPTH_MIN:
		zoldStart	=	clipMax;
		break;
	case DEPTH_MAX:
		zoldStart	=	-clipMax;
		break;
	case DEPTH_AVG:
		zoldStart	=	0;
		break;
	case DEPTH_MID:
		zoldStart	=	clipMax;
		break;
	}

	// Set the digits
	width				=	w;
	height				=	h;
	left				=	l;
	top					=	t;
	sampleWidth			=	width*pixelXsamples + 2*xSampleOffset;
	sampleHeight		=	height*pixelYsamples + 2*ySampleOffset;
	right				=	left + sampleWidth;
	bottom				=	top + sampleHeight;

	assert(sampleWidth <= totalWidth);
	assert(sampleHeight <= totalHeight);

	// Init the occlusion culler to zero
	initToZero();
	for (i=0;i<sampleHeight;i++) {
		CPixel	*pixel	=	fb[i];

		for (j=0;j<sampleWidth;j++,pixel++) {
			float	aperture[2];

			// The stratified sample
			pixel->jx					=	(float) (jitter*(urand()-0.5) + 0.5001011);
			pixel->jy					=	(float) (jitter*(urand()-0.5) + 0.5001017);

			// Time of the sample for motion blur
			timeGenerator.get(&(pixel->jt));

			// Aperture sample for depth of field
			apertureGenerator.get(aperture);
			const	double	angle		=	2*DOUBLE_PI*aperture[0];
			const	float	r			=	aperture[1];
			pixel->jdx					=	(float) (cos(angle)*r);
			pixel->jdy					=	(float) (sin(angle)*r);

			// Center location of the sample
			pixel->xcent				=	(j+pixel->jx) + left;
			pixel->ycent				=	(i+pixel->jy) + top;

			pixel->z					=	clipMax;
			pixel->zold					=	zoldStart;
			pixel->numSplats			=	0;
			pixel->node					=	getNode(j,i);
			pixel->node->zmax			=	clipMax;


			cFragment					=	&pixel->last;
			cFragment->z				=	clipMax;
			initv(cFragment->color,0);
			initv(cFragment->opacity,0);
			cFragment->next				=	NULL;
			cFragment->prev				=	&pixel->first;

			cFragment					=	&pixel->first;
			cFragment->z				=	-C_INFINITY;
			initv(cFragment->color,0);
			initv(cFragment->opacity,0);
			cFragment->next				=	&pixel->last;
			cFragment->prev				=	NULL;
			
			for (k=0;k<numExtraSamples;k++) {
				pixel->extraSamples[k]	=	0;
			}

			pixel->update				=	&pixel->first;
		}
	}
	resetHierarchy();
}

///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	rasterDrawPrimitives
// Description			:	Draw bunch of primitives
// Return Value			:	-
// Comments				:
// Date last edited		:	7/31/2002
void		CStochastic::rasterDrawPrimitives(CRasterGrid *grid) {
	const	int					xres				=	sampleWidth - 1;
	const	int					yres				=	sampleHeight - 1;
	const	TRasterPrimitive	*cPrimitive;
	vector						v0movTmp,v1movTmp,v2movTmp;
	vector						v0focTmp,v1focTmp,v2focTmp;
	CFragment					*cSample,*lSample,*nSample;
	const	int					sampleDisplacement	=	numExtraSamples + 10;
	const	float				*v0;
	const	float				*v0c;
	const	float				*v0o;
	const	float				*v1;
	const	float				*v1c;
	const	float				*v1o;
	const	float				*v2;
	const	float				*v2c;
	const	float				*v2o;
	const	int					*xbound;
	const	int					*ybound;
	int							xmin,xmax,ymin,ymax,x,y;
	float						a;
	const	float				*dv0,*dv1,*dv2;
	int							numPrimitives;

// This macro is used to insert a fragment into the linked list for a pixel
#define	findSample(__dest,__z) 																		\
	lSample	=	pixel->update;																		\
	if (__z >= lSample->z)	{																		\
		for (cSample=lSample->next;__z >= cSample->z;lSample=cSample,cSample=cSample->next);		\
		assert(__z >= lSample->z);																	\
		assert(__z <= cSample->z);																	\
		newFragment(__dest);																		\
		__dest->next	=	cSample;																\
		__dest->prev	=	lSample;																\
		cSample->prev	=	__dest;																	\
		lSample->next	=	__dest;																	\
	} else {																						\
		for (cSample=lSample->prev;__z < cSample->z;lSample=cSample,cSample=cSample->prev);			\
		assert(__z >= cSample->z);																	\
		assert(__z <= lSample->z);																	\
		newFragment(__dest);																		\
		__dest->next	=	lSample;																\
		__dest->prev	=	cSample;																\
		cSample->next	=	__dest;																	\
		lSample->prev	=	__dest;																	\
	}																								\
	pixel->update	=	__dest;


	// This macro is called when an opaque fragment is inserted
#define updateOpaque()																				\
	cSample=pixel->last.prev;																		\
	while(cSample->z > z) {																			\
		nSample				=	cSample->prev;														\
		nSample->next		=	&pixel->last;														\
		pixel->last.prev	=	nSample;															\
		assert(cSample != &pixel->first);															\
		deleteFragment(cSample);																	\
		cSample				=	nSample;															\
	}																								\
	pixel->update			=	cSample;


restart:;

	switch(depthFilter) {
	case DEPTH_MIN:
		#define	depthFilterIf()
		#define	depthFilterElse()

		#include "stochasticPrimitives.h"

		#undef	depthFilterIf
		#undef	depthFilterElse
		break;
	case DEPTH_MAX:
		#define	depthFilterIf()		pixel->zold	=	max(pixel->zold,z);
		#define	depthFilterElse()	else {	pixel->zold	=	max(pixel->zold,z);  }

		#include "stochasticPrimitives.h"

		#undef	depthFilterIf
		#undef	depthFilterElse
		break;
	case DEPTH_AVG:
		#define	depthFilterIf()		pixel->zold	+=	z; pixel->numSplats++;
		#define	depthFilterElse()	else {	pixel->zold	+=	z; pixel->numSplats++; }

		#include "stochasticPrimitives.h"

		#undef	depthFilterIf
		#undef	depthFilterElse
		break;
	case DEPTH_MID:
		#define	depthFilterIf()		pixel->zold	=	pixel->z;
		#define	depthFilterElse()	else {	pixel->zold	=	min(pixel->zold,z);  }

		#include "stochasticPrimitives.h"

		#undef	depthFilterIf
		#undef	depthFilterElse
		break;
	}

#undef findSample
#undef updateOpaque
}















///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	rasterEnd
// Description			:	Get the image from the screen
// Return Value			:	-
// Comments				:
// Date last edited		:	7/31/2002
void		CStochastic::rasterEnd(float *fb2) {
	int				i,y;
	int				sx,sy;
	const int		xres					=	width;
	const int		yres					=	height;
	const int		filterWidth				=	pixelXsamples + 2*xSampleOffset;
	const int		filterHeight			=	pixelYsamples + 2*ySampleOffset;
	const float		halfFilterWidth			=	(float) filterWidth / (float) 2;
	const float		halfFilterHeight		=	(float) filterHeight / (float) 2;
	const float		invPixelXsamples		=	1 / (float) pixelXsamples;
	const float		invPixelYsamples		=	1 / (float) pixelYsamples;
	float			*fbs;
	float			*contribution;
	const int		sampleSize				=	5	+ numExtraSamples;	// rgbaz + extra samples
	const int		pixelSize				=	8	+ numExtraSamples;	// alpha + depth + color + opacity + extra samples
	float			*tmp;
	const int		sampleLineDisplacement	=	pixelXsamples*pixelSize;

	// Deep shadow map computation
	if (flags & OPTIONS_FLAGS_DEEP_SHADOW_RENDERING)	deepShadowCompute();

	memBegin();

	contribution	=	(float *) ralloc((xres*yres + totalWidth*totalHeight*pixelSize)*sizeof(float));
	fbs				=	contribution + xres*yres;

	// Collapse the samples (transparency composite)

	// 0	=	alpha
	// 1	=	z;
	// 2-4	=	color
	// 5-7	=	opacity
	for (y=0;y<sampleHeight;y++) {
		CPixel	*cPixel		=	fb[y];
		float	*cFb		=	&fbs[y*totalWidth*pixelSize];
		vector	ropacity;

		for (i=sampleWidth;i>0;i--,cPixel++,cFb+=pixelSize) {
			CFragment	*cSample	=	cPixel->first.next;
			CFragment	*oSample;
			float		*C			=	&cFb[2];
			float		*O			=	&cFb[5];
 
			assert(cPixel->first.z == -C_INFINITY);

			if (numExtraSamples > 0)
				memcpy(cFb+8,cPixel->extraSamples,numExtraSamples*sizeof(float));

			// Get the base color and opacity
			movvv(C,cSample->color);
			movvv(O,cSample->opacity);
			ropacity[0]	=	1-O[0];
			ropacity[1]	=	1-O[1];
			ropacity[2]	=	1-O[2];

			oSample		=	cSample;
			cSample		=	cSample->next;
			for (;cSample!=NULL;) {
				deleteFragment(oSample);
				const float	*color		= cSample->color;
				const float *opacity	= cSample->opacity;

				// Composite
				C[0]		+=	ropacity[0]*color[0];
				C[1]		+=	ropacity[1]*color[1];
				C[2]		+=	ropacity[2]*color[2];
				O[0]		+=	ropacity[0]*opacity[0];
				O[1]		+=	ropacity[1]*opacity[1];
				O[2]		+=	ropacity[2]*opacity[2];
				ropacity[0]	*=	1-opacity[0];
				ropacity[1]	*=	1-opacity[1];
				ropacity[2]	*=	1-opacity[2];

				oSample		=	cSample;
				cSample		=	cSample->next;
			}

			// Alpha is the average opacity
			// I know this is wrong but this is more useful
			cFb[0]			=	(float) ((O[0] + O[1] + O[2])*0.333333333);
		}
	}

	// Find the depth component
	switch(depthFilter) {
	case DEPTH_MIN:
		for (y=0;y<sampleHeight;y++) {
			CPixel	*cPixel		=	fb[y];
			float	*cFb		=	&fbs[y*totalWidth*pixelSize];

			for (i=sampleWidth;i>0;i--,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	C_INFINITY;
				else						cFb[1]	=	cPixel->z;
			}
		}

		break;
	case DEPTH_MAX:
		for (y=0;y<sampleHeight;y++) {
			CPixel	*cPixel		=	fb[y];
			float	*cFb		=	&fbs[y*totalWidth*pixelSize];

			for (i=sampleWidth;i>0;i--,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	C_INFINITY;
				else						cFb[1]	=	cPixel->zold;
			}
		}

		break;
	case DEPTH_AVG:
		for (y=0;y<sampleHeight;y++) {
			CPixel	*cPixel		=	fb[y];
			float	*cFb		=	&fbs[y*totalWidth*pixelSize];

			for (i=sampleWidth;i>0;i--,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	C_INFINITY;
				else						cFb[1]	=	cPixel->zold / (float) cPixel->numSplats;
			}
		}

		break;
	case DEPTH_MID:
		for (y=0;y<sampleHeight;y++) {
			CPixel	*cPixel		=	fb[y];
			float	*cFb		=	&fbs[y*totalWidth*pixelSize];

			for (i=sampleWidth;i>0;i--,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	C_INFINITY;
				else {
					if (cPixel->zold == clipMax) {
						cFb[1]		=	cPixel->z;
					} else {
						cFb[1]		=	(cPixel->z + cPixel->zold) * (float) 0.5;
					}
				}
			}
		}

		break;
	}

	// Clear the memory first
	for (tmp=fb2,i=xres*yres*numSamples;i>0;i--)	*tmp++	=	0;
	for (tmp=contribution,i=xres*yres;i>0;i--)		*tmp++	=	0;

	// Filter the samples
	for (y=0;y<yres;y++) {
		for (sy=0;sy<filterHeight;sy++) {
			for (sx=0;sx<filterWidth;sx++) {
				const CPixel	*pixels			=	&fb[y*pixelYsamples+sy][sx];
				float			*pixelLine		=	&fb2[y*xres*numSamples];
				const float		*sampleLine		=	&fbs[((y*pixelYsamples+sy)*totalWidth + sx)*pixelSize];
				float			*cLine			=	&contribution[y*xres];
				const float		xOffset			=	sx - halfFilterWidth;
				const float		yOffset			=	sy - halfFilterHeight;

				for (i=0;i<xres;i++) {
					const float	cx				=	(pixels->jx + xOffset)*invPixelXsamples;
					const float	cy				=	(pixels->jy + yOffset)*invPixelYsamples;
					float		filterResponse	=	pixelFilter(cx,cy,pixelFilterWidth,pixelFilterHeight);
					int			es;

					if (fabs(cx) > marginX)	filterResponse *=marginXcoverage;
					if (fabs(cy) > marginY)	filterResponse *=marginYcoverage;

					cLine[0]				+=	filterResponse;
					pixelLine[0]			+=	filterResponse*sampleLine[2];
					pixelLine[1]			+=	filterResponse*sampleLine[3];
					pixelLine[2]			+=	filterResponse*sampleLine[4];
					pixelLine[3]			+=	filterResponse*sampleLine[0];
					pixelLine[4]			+=	filterResponse*sampleLine[1];
					// Filter the extra samples here

					for (es=0;es<numExtraSamples;es++) {
						pixelLine[5+es]		+=	filterResponse*sampleLine[8+es];
					}

					pixelLine				+=	numSamples;
					sampleLine				+=	sampleLineDisplacement;
					cLine					+=	1;
					pixels					+=	pixelXsamples;
				}
			}
		}
	}

	// Normalize the filter
	for (i=xres*yres;i>0;i--) {
		const float	invContribution	=	1/(*contribution++);
		int			k;

		for (k=numSamples;k>0;k--) {
			*fb2++ *= invContribution;
		}
	}

	memEnd();
}


// The origin, the last depth sample we output
static	float	origin[4];
static	float	lastZ;
static	float	rSlopeMin;
static	float	gSlopeMin;
static	float	bSlopeMin;
static	float	rSlopeMax;
static	float	gSlopeMax;
static	float	bSlopeMax;
static	FILE	*deepShadowFile;
static	float	tsmThreshold;

///////////////////////////////////////////////////////////////////////
// Function				:	outSample
// Description			:	This function is used to output a depth sample
// Return Value			:	-
// Comments				:
// Date last edited		:	12/26/2003
inline	void	startSample(FILE *outFile,float threshold) {
	deepShadowFile	=	outFile;
	tsmThreshold	=	threshold;

	rSlopeMax		=	C_INFINITY;
	gSlopeMax		=	C_INFINITY;
	bSlopeMax		=	C_INFINITY;
	rSlopeMin		=	-C_INFINITY;
	gSlopeMin		=	-C_INFINITY;
	bSlopeMin		=	-C_INFINITY;

	// Output the first sample (at z=-C_INFINITY)
	origin[0]		=	-C_INFINITY;
	origin[1]		=	1;
	origin[2]		=	1;
	origin[3]		=	1;
	fwrite(origin,sizeof(float),4,deepShadowFile);
	lastZ			=	-C_INFINITY;
}

///////////////////////////////////////////////////////////////////////
// Function				:	outSample
// Description			:	This function is used to output a depth sample
// Return Value			:	-
// Comments				:
// Date last edited		:	12/26/2003
inline	void	outSample(float cZ,const float *opacity) {
	// Always output the closest sample
	if (origin[0] == -C_INFINITY) {
		origin[0]	=	cZ;
		origin[1]	=	opacity[0];
		origin[2]	=	opacity[1];
		origin[3]	=	opacity[2];

		fwrite(origin,sizeof(float),4,deepShadowFile);
	} else if (cZ == origin[0]) {	// Do we have a step ?
		const float	dr	=	(float) fabs(origin[1] - opacity[0]);							
		const float	dg	=	(float) fabs(origin[2] - opacity[1]);
		const float	db	=	(float) fabs(origin[3] - opacity[2]);

		// Is the step small enough ?
		if ((dr >= tsmThreshold) || (dg >= tsmThreshold) || (db >= tsmThreshold)) {

			// No, output the step
			origin[1]	=	opacity[0];
			origin[2]	=	opacity[1];
			origin[3]	=	opacity[2];
			fwrite(origin,sizeof(float),4,deepShadowFile);
		}
	} else {
		// Check for the window of validity
		const float	denom		=	1 / (cZ - origin[0]);
		float		crSlopeMax	=	(opacity[0] - origin[1] + tsmThreshold) * denom;
		float		cgSlopeMax	=	(opacity[1] - origin[2] + tsmThreshold) * denom;
		float		cbSlopeMax	=	(opacity[2] - origin[3] + tsmThreshold) * denom;
		float		crSlopeMin	=	(opacity[0] - origin[1] - tsmThreshold) * denom;
		float		cgSlopeMin	=	(opacity[1] - origin[2] - tsmThreshold) * denom;
		float		cbSlopeMin	=	(opacity[2] - origin[3] - tsmThreshold) * denom;

		crSlopeMax				=	min(crSlopeMax,rSlopeMax);
		cgSlopeMax				=	min(cgSlopeMax,gSlopeMax);
		cbSlopeMax				=	min(cbSlopeMax,bSlopeMax);

		crSlopeMin				=	max(crSlopeMin,rSlopeMin);
		cgSlopeMin				=	max(cgSlopeMin,gSlopeMin);
		cbSlopeMin				=	max(cbSlopeMin,bSlopeMin);

		if ((crSlopeMin < crSlopeMax) && (cgSlopeMin < cgSlopeMax) && (cbSlopeMin < cbSlopeMax)) {			
			// We're in range
			rSlopeMax		=	crSlopeMax;
			gSlopeMax		=	cgSlopeMax;
			bSlopeMax		=	cbSlopeMax;

			rSlopeMin		=	crSlopeMin;
			gSlopeMin		=	cgSlopeMin;
			bSlopeMin		=	cbSlopeMin;
		} else {
			origin[1]		+=	(rSlopeMin + rSlopeMax)*(lastZ - origin[0])*(float) 0.5;
			origin[2]		+=	(gSlopeMin + gSlopeMax)*(lastZ - origin[0])*(float) 0.5;
			origin[3]		+=	(bSlopeMin + bSlopeMax)*(lastZ - origin[0])*(float) 0.5;
			origin[0]		=	lastZ;
			fwrite(origin,sizeof(float),4,deepShadowFile);

			rSlopeMax		=	C_INFINITY;
			gSlopeMax		=	C_INFINITY;
			bSlopeMax		=	C_INFINITY;
			rSlopeMin		=	-C_INFINITY;
			gSlopeMin		=	-C_INFINITY;
			bSlopeMin		=	-C_INFINITY;

			// Do we have a step ?
			if (cZ == origin[0]) {
				const float	dr	=	(float) fabs(origin[1] - opacity[0]);							
				const float	dg	=	(float) fabs(origin[2] - opacity[1]);
				const float	db	=	(float) fabs(origin[3] - opacity[2]);

				// Is the step small enough ?
				if ((dr >= tsmThreshold) || (dg >= tsmThreshold) || (db >= tsmThreshold)) {

					// No, output the step
					origin[1]	=	opacity[0];
					origin[2]	=	opacity[1];
					origin[3]	=	opacity[2];
					fwrite(origin,sizeof(float),4,deepShadowFile);
				}
			} else {
				const float	denom		=	1 / (cZ - origin[0]);
				rSlopeMax	=	(opacity[0] - origin[1] + tsmThreshold) * denom;
				gSlopeMax	=	(opacity[1] - origin[2] + tsmThreshold) * denom;
				bSlopeMax	=	(opacity[2] - origin[3] + tsmThreshold) * denom;
				rSlopeMin	=	(opacity[0] - origin[1] - tsmThreshold) * denom;
				gSlopeMin	=	(opacity[1] - origin[2] - tsmThreshold) * denom;
				bSlopeMin	=	(opacity[2] - origin[3] - tsmThreshold) * denom;
			}
		}
	}

	lastZ	=	cZ;
}

///////////////////////////////////////////////////////////////////////
// Function				:	finishSample
// Description			:	This function is used to output the last sample
// Return Value			:	-
// Comments				:
// Date last edited		:	12/26/2003
inline	void	finishSample(float cZ,const float *opacity) {
	if (origin[0] < cZ) {
		origin[1]		+=	(rSlopeMin + rSlopeMax)*(lastZ - origin[0])*(float) 0.5;	
		origin[2]		+=	(gSlopeMin + gSlopeMax)*(lastZ - origin[0])*(float) 0.5;
		origin[3]		+=	(bSlopeMin + bSlopeMax)*(lastZ - origin[0])*(float) 0.5;
		origin[0]		=	lastZ;
		fwrite(origin,sizeof(float),4,deepShadowFile);
	}

	origin[0]		=	cZ;
	origin[1]		=	opacity[0];
	origin[2]		=	opacity[1];
	origin[3]		=	opacity[2];
	fwrite(origin,sizeof(float),4,deepShadowFile);

	origin[0]		=	C_INFINITY;
	fwrite(origin,sizeof(float),4,deepShadowFile);
}

///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	filterSamples
// Description			:	Filter / output the pixel
// Return Value			:	-
// Comments				:
// Date last edited		:	3/1/2003
void			CStochastic::filterSamples(int numSamples,CFragment **samples,float *weights) {
	int		minSample		=	0;
	int		i;
	vector	opacity;

	initv(opacity,1,1,1);		// The current opacity

	startSample(deepShadowFile,tsmThreshold);				// The beginning of a pixel

	// Find the closest sample
	for (i=1;i<numSamples;i++) {
		if (samples[i]->z < samples[minSample]->z) {
			minSample	=	i;
		}
	}

	// Filter / output pixels incrementally
	while(TRUE) {
		int				stop		=	FALSE;							// TRUE when the opacity drops below 0
		const CFragment	*cSample	=	samples[minSample];
		const float		cZ			=	cSample->z;						// The current Z coordinate
		float			*oldOpacity	=	weights + (minSample<<2);
		vector			newOpacity;

		outSample(cZ,opacity);

		newOpacity[0]				=	oldOpacity[1]*(1-cSample->opacity[0]);
		newOpacity[1]				=	oldOpacity[2]*(1-cSample->opacity[1]);
		newOpacity[2]				=	oldOpacity[3]*(1-cSample->opacity[2]);

		opacity[0]					+=	(newOpacity[0] - oldOpacity[1])*oldOpacity[0];
		opacity[1]					+=	(newOpacity[1] - oldOpacity[2])*oldOpacity[0];
		opacity[2]					+=	(newOpacity[2] - oldOpacity[3])*oldOpacity[0];

		oldOpacity[1]				=	newOpacity[0];
		oldOpacity[2]				=	newOpacity[1];
		oldOpacity[3]				=	newOpacity[2];

		if (opacity[0] <= 0) {
			opacity[0]	=	0;
			stop++;
		}

		if (opacity[1] <= 0) {
			opacity[1]	=	0;
			stop++;
		}

		if (opacity[2] <= 0) {
			opacity[2]	=	0;
			stop++;
		}

		// Advance the minSample
		if ((samples[minSample]		=	samples[minSample]->next) == NULL) {
			int	nindex				=	minSample << 2;
			int	oindex				=	(numSamples-1) << 2;
			samples[minSample]		=	samples[numSamples-1];
			weights[nindex + 0]		=	weights[oindex + 0];
			weights[nindex + 1]		=	weights[oindex + 1];
			weights[nindex + 2]		=	weights[oindex + 2];
			weights[nindex + 3]		=	weights[oindex + 3];
			numSamples--;
			if (numSamples == 0) {
				stop	=	3;
			}
		}

		// Decide whether we should stop or keep going
		if (stop == 3) {
			finishSample(cZ,opacity);
			break;
		} else {
			outSample(cZ,opacity);
		}

		for (minSample=0,i=1;i<numSamples;i++) {
			if (samples[i]->z < samples[minSample]->z) {
				minSample	=	i;
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	deepShadowCompute
// Description			:	Compute/write deep shadow map data
// Return Value			:	-
// Comments				:
// Date last edited		:	7/31/2002
void		CStochastic::deepShadowCompute() {
	int			i;
	const int	xres				=	width;
	const int	yres				=	height;
	const int	filterWidth			=	pixelXsamples + 2*xSampleOffset;
	const int	filterHeight		=	pixelYsamples + 2*ySampleOffset;
	const float	invPixelXsamples	=	1 / (float) pixelXsamples;
	const float	invPixelYsamples	=	1 / (float) pixelYsamples;
	int			numSamples;
	int			x,y;
	CFragment	**samples;
	CFragment	**fSamples;
	float		*fWeights;

	memBegin();

	// Allocate the memory for misc junk
	samples			=	(CFragment **)	ralloc(totalHeight*totalWidth*sizeof(CFragment*));
	fSamples		=	(CFragment **)	ralloc(filterWidth*filterHeight*sizeof(CFragment*));
	fWeights		=	(float *)		ralloc(filterWidth*filterHeight*sizeof(float)*4);

	// Init the samples
	for (i=0;i<totalWidth*totalHeight;i++)	samples[i]	=	NULL;

	// Collect the samples first
	for (i=0,y=0;y<sampleHeight;y++) {
		for (x=0;x<sampleWidth;x++,i++) {
			samples[i]	=	fb[y][x].first.next;
		}
	}

	assert(bucketWidth	>= xres);
	assert(bucketHeight	>= yres);

	// Compute the visibility function for each pixel
	for (y=0;y<bucketHeight;y++) {
		for (x=0;x<bucketWidth;x++) {

			if ((x < xres) && (y < yres)) {
				// Gather the samples for this pixel and the filter response
				int		sx,sy;
				float	filterSum		=	0;

				for (i=0,sy=0;sy<filterHeight;sy++) {
					for (sx=0;sx<filterWidth;sx++,i++) {
						const int		xsample	=	x*pixelXsamples + sx;
						const int		ysample	=	y*pixelYsamples + sy;
						const CPixel	*pixels	=	&fb[ysample][xsample];
						const float		cx		=	(sx + pixels->jx - (filterWidth/(float) 2))*invPixelXsamples;
						const float		cy		=	(sy + pixels->jy - (filterHeight/(float) 2))*invPixelYsamples;
						fSamples[i]				=	samples[ysample*sampleWidth+xsample];
						fWeights[i<<2]			=	pixelFilter(cx,cy,pixelFilterWidth,pixelFilterHeight);
						filterSum				+=	fWeights[i<<2];
					}
				}

				// Normalize the pixel filter responses
				numSamples	=	i;
				for (i=0;i<numSamples;i++) {
					const int	index	=	i << 2;
					fWeights[index+0]			/=	filterSum;
					fWeights[index+1]			=	1;
					fWeights[index+2]			=	1;
					fWeights[index+3]			=	1;
				}

				// Filter/write the pixels
				filterSamples(numSamples,fSamples,fWeights);
			} else {
				// Output a dummy pixel
				float	dummy[4];

				dummy[0]	=	-C_INFINITY;
				dummy[1]	=	1;
				dummy[2]	=	1;
				dummy[3]	=	1;
				fwrite(dummy,sizeof(float),4,deepShadowFile);

				dummy[0]	=	C_INFINITY;
				dummy[1]	=	1;
				dummy[2]	=	1;
				dummy[3]	=	1;
				fwrite(dummy,sizeof(float),4,deepShadowFile);
			}
		}
	}

	// Record the index in the file
	deepShadowIndex[currentYBucket*xBuckets + currentXBucket]	=	ftell(deepShadowFile);

	memEnd();
}
