//////////////////////////////////////////////////////////////////////
//
//                             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 "stochastic.h"
#include "renderer.h"
#include "memory.h"
#include "random.h"

static	int	numAllocatedFragments	=	0;

#define	newFragment(a)		if (freeFragments == NULL)	{									\
								int	j					=	8+numExtraSamples;				\
								T32	*newPage			=	new T32[j*fragmentStepSize+1];	\
								int	i;														\
								newPage->pointer		=	fragmentPages;					\
								fragmentPages			=	newPage;						\
																							\
								newPage++;													\
								for (i=fragmentStepSize;i>0;i--) {							\
									newPage->pointer	=	freeFragments;					\
									freeFragments		=	newPage;						\
									newPage				+=	j;								\
								}															\
																							\
								fragmentStepSize		*=	2;								\
							}																\
							numAllocatedFragments++;										\
							a				=	freeFragments;								\
							freeFragments	=	(T32 *) freeFragments[0].pointer;

#define	deleteFragment(a)	a[0].pointer	=	freeFragments;								\
							freeFragments	=	a;											\
							numAllocatedFragments--;


///////////////////////////////////////////////////////////////////////
// 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;

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

	// Allocate the framebuffer
	fb				=	new CPixel*[totalHeight];
	for (i=0;i<totalHeight;i++) {
		fb[i]		=	new CPixel[totalWidth];
	}

	if (aperture != 0)	focalBlur	=	TRUE;
	else				focalBlur	=	FALSE;

	freeFragments		=	NULL;
	fragmentPages		=	NULL;
	fragmentStepSize	=	1000;

	// 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;
	T32	*cPage;

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

	delete [] fb;

	// Ditch the allocated fragments
	while((cPage=fragmentPages) != NULL) {
		fragmentPages	=	(T32 *) cPage->pointer;
		delete [] cPage;
	}
}

///////////////////////////////////////////////////////////////////////
// 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;
	T32			*cSample;
	float		zoldStart;

	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;

	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++) {
			const	double	angle		=	2*DOUBLE_PI*urand();
			const	float	r			=	urand();

			pixel->jx					=	(float) (jitter*(urand()-0.5) + 0.5001011);
			pixel->jy					=	(float) (jitter*(urand()-0.5) + 0.5001017);
			pixel->jt					=	urand();
			pixel->jdx					=	(float) (cos(angle)*r);
			pixel->jdy					=	(float) (sin(angle)*r);
			pixel->xcent				=	(j+pixel->jx) + left;
			pixel->ycent				=	(i+pixel->jy) + top;

			newFragment(cSample);
			pixel->z					=	clipMax;
			pixel->zold					=	zoldStart;
			pixel->numSplats			=	0;
			pixel->sample				=	cSample;	// The surface samples will be inserted into this list
			pixel->node					=	getNode(j,i);
			pixel->node->zmax			=	clipMax;

			cSample[0].pointer			=	NULL;
			cSample[1].real				=	clipMax;

			cSample						+=	2;
			for (k=numExtraSamples+6;k>0;k--) {
				cSample->real			=	0;
				cSample++;
			}
		}
	}
	resetHierarchy();
}

///////////////////////////////////////////////////////////////////////
// Class				:	CStochastic
// Method				:	rasterDrawPrimitives
// Description			:	Draw bunch of primitives
// Return Value			:	-
// Comments				:
// Date last edited		:	7/31/2002
void		CStochastic::rasterDrawPrimitives(const TRasterPrimitive *primitives,int transparent) {
	const	int					xres				=	sampleWidth - 1;
	const	int					yres				=	sampleHeight - 1;
	const	TRasterPrimitive	*cPrimitive;
	vector						v0movTmp,v1movTmp,v2movTmp;
	vector						v0focTmp,v1focTmp,v2focTmp;
	T32							*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;

restart:;

	switch(depthFilter) {
	case DEPTH_MIN:
		#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;
	}
}















///////////////////////////////////////////////////////////////////////
// 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();
		return;
	}

	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];
		T32		*dSample;
		vector	ropacity;

		for (i=0;i<sampleWidth;i++,cPixel++,cFb+=pixelSize) {
			T32		*oSample	=	cPixel->sample;
			float	*C			=	&cFb[2];
			float	*O			=	&cFb[5];
 
			if (numExtraSamples > 0)
				memcpy(cFb+8,&oSample[8].real,numExtraSamples);

			movvv(C,&oSample[2].real);
			movvv(O,&oSample[5].real);
			ropacity[0]	=	1-O[0];
			ropacity[1]	=	1-O[1];
			ropacity[2]	=	1-O[2];

			for (dSample=oSample,oSample=(T32 *) oSample->pointer;oSample!=NULL;dSample=oSample,oSample=(T32 *) oSample->pointer) {
				const float	*color		= &oSample[2].real;
				const float *opacity	= &oSample[5].real;
				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];

				deleteFragment(dSample);
			}

			deleteFragment(dSample);

			cFb[0]			=	(float) ((O[0] + O[1] + O[2])*0.333333333);
		}
	}

	assert(numAllocatedFragments == 0);

	// 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=0;i<sampleWidth;i++,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	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=0;i<sampleWidth;i++,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	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=0;i<sampleWidth;i++,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	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=0;i<sampleWidth;i++,cPixel++,cFb+=pixelSize) {
				if (cPixel->z == clipMax)	cFb[1]	=	INFINITY;
				else {
					if (cPixel->zold == clipMax) {
						cFb[1]		=	cPixel->z;
					} else {
						cFb[1]		=	(cPixel->z + cPixel->zold) * (float) 0.5;
					}
				}
			}
		}

		break;
	}


	assert(numAllocatedFragments == 0);

	// 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		=	INFINITY;
	gSlopeMax		=	INFINITY;
	bSlopeMax		=	INFINITY;
	rSlopeMin		=	-INFINITY;
	gSlopeMin		=	-INFINITY;
	bSlopeMin		=	-INFINITY;

	// Output the first sample (at z=-INFINITY)
	origin[0]		=	-INFINITY;
	origin[1]		=	1;
	origin[2]		=	1;
	origin[3]		=	1;
	fwrite(origin,sizeof(float),4,deepShadowFile);
	lastZ			=	-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] == -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		=	INFINITY;
			gSlopeMax		=	INFINITY;
			bSlopeMax		=	INFINITY;
			rSlopeMin		=	-INFINITY;
			gSlopeMin		=	-INFINITY;
			bSlopeMin		=	-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]		=	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,T32 **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][1].real < samples[minSample][1].real) {
			minSample	=	i;
		}
	}

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

		if (cSample[5].real < 1) {
			if (cSample[6].real > 0) {
				int	i	=	1;
			}
		}

		outSample(cZ,opacity);

		newOpacity[0]				=	oldOpacity[1]*(1-cSample[5].real);
		newOpacity[1]				=	oldOpacity[2]*(1-cSample[6].real);
		newOpacity[2]				=	oldOpacity[3]*(1-cSample[7].real);

		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];

		//assert(opacity[0] <= 1);
		//assert(opacity[1] <= 1);
		//assert(opacity[2] <= 1);

		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]		=	(T32 *) samples[minSample]->pointer) == 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][1].real < samples[minSample][1].real) {
				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;
	T32			**samples;
	T32			**fSamples;
	float		*fWeights;
	T32			*memory;

	memBegin();

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

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

	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*totalWidth+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++) {
					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]	=	-INFINITY;
				dummy[1]	=	1;
				dummy[2]	=	1;
				dummy[3]	=	1;
				fwrite(dummy,sizeof(float),4,deepShadowFile);

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

	// Delete the samples
	for (i=0,y=0;y<sampleHeight;y++) {
		for (x=0;x<sampleWidth;x++,i++) {
			T32	*cSample	=	samples[y*sampleWidth+x];
			T32	*dSample;
			
			while((dSample=cSample) !=NULL) {
				cSample	=	(T32 *) dSample->pointer;

				deleteFragment(dSample);
			}
		}
	}

	assert(numAllocatedFragments == 0);

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

	memEnd();
}
