//////////////////////////////////////////////////////////////////////
//
//                             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				:	trace.cpp
//  Classes				:	CShadingContext
//  Description			:	This file implements raytracing functionality of the shading context
//
////////////////////////////////////////////////////////////////////////
#include "hierarchy.h"
#include "renderer.h"
#include "shading.h"
#include "stats.h"
#include "memory.h"
#include "output.h"
#include "points.h"
#include "volume.h"


















///////////////////////////////////////////////////////////////////////
// Class				:	CShadingContext
// Method				:	trace
// Description			:	Trace/Shade bunch of rays
// Return Value			:	-
// Comments				:	Assume the following fields of CRay have been set:
//							1. from
//							2. dir (must be unit length)
//							3. t
//							4. tmin
//							5. flags
// Date last edited		:	8/30/2002
void	CShadingContext::trace(CRayBundle *bundle) {
	int								i,j;
	const	COptions::CClipPlane	*cPlane;
	float							t;
	float							tt;
	CRay							**rays;
	CRay							**raysStart;
	int								numRays;
	CSurface						*object;
	float							**varying;
	int								numShadingRays;
	int								*stack;


	// Compute some of the ray junk
	rays			=	bundle->rays;
	numRays			=	bundle->numRays;
	stack			=	(int *) alloca(numRays*2*sizeof(int));

	assert(numRays != 0);

	for (i=0;i<numRays;i++) {
		CRay	*ray	=	rays[i];
		
		t				=	ray->t;

		addvv(ray->to,ray->from,ray->dir);
		
		// Check the ray against the clipping planes
		for (cPlane=clipPlanes;cPlane!=NULL;cPlane=cPlane->next) {
			tt	=	-(dotvv(cPlane->normal,ray->from)+cPlane->d) / dotvv(cPlane->normal,ray->dir);

			if ((tt > 0) && (tt < t))	t	=	tt;
		}

		ray->invDir[COMP_X]		=	1/ray->dir[COMP_X];
		ray->invDir[COMP_Y]		=	1/ray->dir[COMP_Y];
		ray->invDir[COMP_Z]		=	1/ray->dir[COMP_Z];
		ray->t					=	t;
		ray->lastXform			=	NULL;
		ray->object				=	NULL;
		hierarchy->intersect(ray);
	}

	// Increment the traced ray counter
	stats.numTracedRays	+=	numRays;

	// The transparency pass loop
	while(TRUE) {

		// Check if we should shade these rays
		if (bundle->postTraceAction() == FALSE) {
			return;
		} else {
			// Sort the shading points with respect to objects
			if (numRays > 1) {
				int				last;
				int				start,end;
				CRay			*tmp;
				int				*stackBase	=	stack;

				*stack++				=	0;
				*stack++				=	numRays-1;

				while(stack > stackBase) {
					end				=	*(--stack);
					start			=	*(--stack);

					last			=	start;
					for (i=start+1;i<=end;i++) {
						if ((unsigned int) (rays[i]->object) > (unsigned int) (rays[start]->object)) {
							last++;
							tmp				=	rays[last];
							rays[last]		=	rays[i];
							rays[i]			=	tmp;
						}
					}

					tmp			=	rays[last];
					rays[last]	=	rays[start];
					rays[start]	=	tmp;

					if (start < (last-1)) {
						*stack++	=	start;
						*stack++	=	last-1;
					}

					if ((last+1) < end)	{
						*stack++	=	last+1;
						*stack++	=	end;
					}
				}
			}

			if ((object=rays[0]->object) == NULL) {
				bundle->postShade(numRays,rays);
				return;
			}

			varying			=	currentShadingState->varying;
			raysStart		=	rays;

			// Shade the objects one by one
			for (i=numRays,j=maxGridSize;i>0;i--,j--,rays++) {
				CRay	*cRay	=	*rays;

				if ((object != cRay->object) || (j == 0)) {
					numShadingRays	=	maxGridSize - j;

					// Shade the sucker
					object->shade(this,numShadingRays,raysStart);
					bundle->postShade(numShadingRays,raysStart,varying);

					if ((object=cRay->object) == NULL) {
						bundle->postShade(i,rays);
						break;
					}

					raysStart		=	rays;
					j				=	maxGridSize;
				}
			}

			// Finish shading
			if (object != NULL) {
				numShadingRays	=	maxGridSize - j;
				assert(object == raysStart[0]->object);
				object->shade(this,numShadingRays,raysStart);
				bundle->postShade(numShadingRays,raysStart,varying);
			}

			bundle->post();

			if ((numRays=bundle->numRays) == 0) {
				break;
			}

			for (i=numRays,rays=bundle->rays;i>0;i--) {
				CRay	*cRay	=	*rays++;

				assert(cRay->object != NULL);

				cRay->tmin		=	cRay->t + cRay->object->attributes->shadowBias;
				cRay->object	=	NULL;
				cRay->t			=	INFINITY;
				hierarchy->intersect(cRay);
			}

			// Increment the traced ray counter
			stats.numTracedRays	+=	numRays;

			rays				=	bundle->rays;
		}
	}
}


///////////////////////////////////////////////////////////////////////
// Class				:	CShadingContext
// Method				:	traceEx
// Description			:	Trace/Shade bunch of rays
// Return Value			:	-
// Comments				:
// Date last edited		:	8/30/2002
void	CShadingContext::traceEx(CRayBundle *bundle) {
	if (currentRayDepth < maxRayDepth) {
		CShadingState	*savedState		=	currentShadingState;
		char			*savedLabel		=	currentRayLabel;

		currentRayDepth++;
		currentRayLabel	=	bundle->label;

		if (freeStates == NULL)			freeStates	=	newState();

		currentShadingState				=	freeStates;
		freeStates						=	currentShadingState->next;
		currentShadingState->postShader	=	bundle->postShader;

		trace(bundle);

		currentRayDepth--;
		currentShadingState->next		=	freeStates;
		freeStates						=	currentShadingState;

		currentRayLabel					=	savedLabel;
		currentShadingState				=	savedState;

	} else {
		bundle->postShade(bundle->numRays,bundle->rays);
	}
}




