/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "bih/bih.h"

		using namespace GS;
		using namespace GS::BIH;


//------------------------------------------------------------------------------
void				Tree::Raytrace(Trace &trace, const Vector4 &s, const Vector4 &d, float l, void *parm)
{
	trace.has_i = false;
	trace.i_t = -1;
	trace.node_visited = 0;
	trace.stack_pos = 0;

	// Intersect BIH bounding volume.
	float tmin, tmax;
	if	(!minmax.IntersectRay(s, d, tmin, tmax))
		return;

	// Reject if intersection is too far away.
	if	((l > 0) && (tmin >= l))
		return;

	// Initialize trace.
	trace.s = s;
	trace.d = d;

	tmax = ((l > 0) && (tmax > l)) ? l : tmax;

	// Iterative trace.
	float i_t[2];

	for	(Node *node = root; node; )
	{
		if	(!trace.has_i || ((tmin < trace.i_t) && trace.want_closest))	// Only bother about rays that could lead to a closer hit.
		{
			while (node->axis != 3)
			{
				if	(d[node->axis] == 0)			// Axis aligned.
				{
					if	(node->split[0] > s[node->axis])
					{
						if	(s[node->axis] > node->split[1])
						{
							trace.stack[trace.stack_pos].node = &((Node *)node->p)[1];
							trace.stack[trace.stack_pos].tmin = tmin;
							trace.stack[trace.stack_pos++].tmax = tmax;
						}
						node = &((Node *)node->p)[0];
					}
					else	if	(s[node->axis] > node->split[1])
							node = &((Node *)node->p)[1];
					else	break;	// Empty space.
				}
				else
				{
					float idn = 1.f / d[node->axis];
					i_t[0] = (node->split[0] - s[node->axis]) * idn;
					i_t[1] = (node->split[1] - s[node->axis]) * idn;

					int min = d[node->axis] > 0 ? 0 : 1, max = 1 - min;

					if	(i_t[min] > tmin)
					{
						if	(tmax > i_t[max])
						{
							trace.stack[trace.stack_pos].node = &((Node *)node->p)[max];
							trace.stack[trace.stack_pos].tmin = (i_t[max] > tmin) ? i_t[max] : tmin;
							trace.stack[trace.stack_pos++].tmax = tmax;
						}
						node = &((Node *)node->p)[min];
						tmax = (i_t[min] < tmax) ? i_t[min] : tmax;
					}
					else	if	(tmax > i_t[max])
					{
						node = &((Node *)node->p)[max];
						tmin = (i_t[max] > tmin) ? i_t[max] : tmin;
					}
					else	break;	// Empty space.
				}
				trace.node_visited++;
			}
			if	(node->axis == 3)
				TraceLeaf(node, tmin, tmax, trace, parm);
		}

		if	(!trace.stack_pos)
			break;

		node = trace.stack[--trace.stack_pos].node;
		tmin = trace.stack[trace.stack_pos].tmin;
		tmax = trace.stack[trace.stack_pos].tmax;
	}
}
//------------------------------------------------------------------------------
