#include "BVHBottom.h"
#include "BVHTop.h"
#include "RayCaster.h"
#include "RendererUtil.h"

using namespace VRUT;

void RayCaster::CastRayPacket(RayStream & rayStream, const BVHBottom & bvh, unsigned int nodeIndex, unsigned int octant, RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & bottomBundle = rayStream.GetBottomBundle();

	// nodes
	const BVHBottom::BVHNode * nodes = bvh.GetNodes(); 
	const BVHBottom::BVHNode * node = &nodes[nodeIndex];

	// stack => push root
	SingleTraversalStack &stack = buffer.bottomPacketStack;
	stack.Clear();

	// init caches
#ifdef __AVX2__
	bottomBundle.InitTraversalCache();
#endif
	bottomBundle.InitIntersectionCache();

	// prefetch root
	_mm_prefetch((char*)node, _MM_HINT_T0);

	// traversal loop
	while (true)
	{
		// leaf
		if (node->IsLeaf())
		{
			// test triangles against ray packet
			bottomBundle.IntersectTriangles(bvh.GetTriangleCols().data(), node->offset, node->offset + node->t);
		}

		// interior
		else
		{
			unsigned int orderFlags = bottomBundle.IntersectAABB(nodes, node->t, octant);
			// any child intersected
			if (orderFlags > 0)
			{
				// both children intersected
				if (orderFlags >= 0x03)
				{
					unsigned char swp = (orderFlags >> 2) & 0x01;
					stack.Push(nodes + node->t + (swp ^ 1));
					node = nodes + node->t + swp;
					continue;
				}
				// one child intersected
				else
				{
					// right child intersected
					if (orderFlags >= 0x02)
					{
						node = nodes + node->t + 1;
						continue;
					}
					// left child intersected
					else
					{
						node = nodes + node->t;
						continue;
					}
				}
			}
		}

		// pop node
		if (stack.Empty())
			break;
		node = stack.Top();
		stack.Pop();
	}

	// write results
	bottomBundle.FlushIntersectionCache();
}

void RayCaster::CastRayPacketShadow(RayStream & rayStream, const BVHBottom & bvh, unsigned int nodeIndex, unsigned int octant, RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & bottomBundle = rayStream.GetBottomBundle();

	// nodes
	const BVHBottom::BVHNode * nodes = bvh.GetNodes(); 
	const BVHBottom::BVHNode * node = &nodes[nodeIndex];

	// stack => push root
	SingleTraversalStack &stack = buffer.bottomPacketStack;
	stack.Clear();

	// init caches
#ifdef __AVX2__
	bottomBundle.InitTraversalCache();
#endif
	bottomBundle.InitIntersectionCache();

	// prefetch root
	_mm_prefetch((char*)node, _MM_HINT_T0);

	// traversal loop
	while (bottomBundle.intersectionCache.mask != FULL_HIT)
	{
		// leaf
		if (node->IsLeaf())
		{
			// test triangles agains ray packet
			bottomBundle.IntersectTrianglesShadow(bvh.GetTriangleCols().data(), node->offset, node->offset + node->t);
		}

		// interior
		else
		{
			unsigned int orderFlags = bottomBundle.IntersectAABB(nodes, node->t, octant);
			// any child intersected
			if (orderFlags > 0)
			{
				// both children intersected
				if (orderFlags >= 0x03)
				{
					unsigned char swp = (orderFlags >> 2) & 0x01;
					stack.Push(nodes + node->t + (swp ^ 1));
					node = nodes + node->t + swp;
					continue;
				}
				// one child intersected
				else
				{
					// right child intersected
					if (orderFlags >= 0x02)
					{
						node = nodes + node->t + 1;
						continue;
					}
					// left child intersected
					else
					{
						node = nodes + node->t;
						continue;
					}
				}
			}
		}

		// pop node
		if (stack.Empty())
			break;
		node = stack.Top();
		stack.Pop();
	}

	// write results
	bottomBundle.FlushIntersectionCacheShadow();

}

void RayCaster::CastRayPacket(RayStream & rayStream, const BVHTop & bvh, unsigned int nodeIndex, unsigned int octant, RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & topBundle = rayStream.GetTopBundle();

	// nodes
	const BVHTop::BVHNode * nodes = bvh.GetNodes(); 
	const BVHTop::BVHNode * node = &nodes[nodeIndex];

	// infos and GAs
	auto & nodeInfos = bvh.GetNodeInfos();
	auto & geometryAccelerators = bvh.GetGeomAccelerators();

	// stack => push root
	SingleTraversalStack &stack = buffer.topPacketStack;
	stack.Clear();

#ifdef __AVX2__
	// init traversal cache
	topBundle.InitTraversalCache();
#endif

	// prefetch root
	_mm_prefetch((char*)node, _MM_HINT_T0);

	// traversal loop
	while (true)
	{
		// leaf
		if (node->IsLeaf())
		{
			// node is leaf - find intersect with acceleration structure
			NODE_ID nodeID = (NODE_ID)node->offset;
			GEOMETRY_ID gid = nodeInfos[nodeID].geometryID;
			BVHBottom * ga = geometryAccelerators[gid];
			if (ga) 
			{
				// transform packets
				rayStream.TransformDown(nodeInfos[nodeID], 1);
				// traverse to lower levels
				CastRayBundle(rayStream, *ga, buffer);
				// transform packets back
				rayStream.TransformUp(*ga, nodeID, nodeInfos[nodeID], 1);
			}
		}

		// interior
		else
		{
			unsigned int orderFlags = topBundle.IntersectAABB(nodes, node->t, octant);
			// any child intersected
			if (orderFlags > 0)
			{
				// both children intersected
				if (orderFlags >= 0x03)
				{
					unsigned char swp = (orderFlags >> 2) & 0x01;
					stack.Push(nodes + node->t + (swp ^ 1));
					node = nodes + node->t + swp;
					continue;
				}
				// one child intersected
				else
				{
					// right child intersected
					if (orderFlags >= 0x02)
					{
						node = nodes + node->t + 1;
						continue;
					}
					// left child intersected
					else
					{
						node = nodes + node->t;
						continue;
					}
				}
			}
		}

		// pop node
		if (stack.Empty())
			break;
		node = stack.Top();
		stack.Pop();
	}
}

void RayCaster::CastRayPacketShadow(RayStream & rayStream, const BVHTop & bvh, MaterialCache & materialCache, Vector4 lightColors[], float rayOffset, unsigned int nodeIndex, unsigned int octant,
		RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & topBundle = rayStream.GetTopBundle();

	// nodes
	const BVHTop::BVHNode * nodes = bvh.GetNodes(); 
	const BVHTop::BVHNode * node = &nodes[nodeIndex];

	// infos and GAs
	auto & nodeInfos = bvh.GetNodeInfos();
	auto & geometryAccelerators = bvh.GetGeomAccelerators();

	// stack => push root
	SingleTraversalStack &stack = buffer.topPacketStack;
	stack.Clear();

#ifdef __AVX2__
	// init traversal cache
	topBundle.InitTraversalCache();
#endif
	topBundle.InitIntersectionCache();

	// prefetch root
	_mm_prefetch((char*)node, _MM_HINT_T0);

	// traversal loop
	while (topBundle.intersectionCache.mask != FULL_HIT)
	{
		// leaf
		if (node->IsLeaf())
		{
			// node is leaf - find intersect with acceleration structure
			NODE_ID nodeID = (NODE_ID)node->offset;
			GEOMETRY_ID gid = nodeInfos[nodeID].geometryID;
			BVHBottom * ga = geometryAccelerators[gid];
			if (ga) 
			{
				// transform packets
				rayStream.TransformDown(nodeInfos[nodeID], 1);
				// traverse to lower levels
				if (nodeInfos[nodeID].material->flags & RTMaterial::ALPHA_TEST)
					CastRayBundle(rayStream, *ga, buffer);
				else 
					CastRayBundleShadow(rayStream, *ga, buffer);
				// transform packets back
				if (!rayStream.TransformUpShadow(*ga, nodeInfos[nodeID], materialCache, lightColors, rayOffset, 1))
				{
					unsigned int packetMask = 0;
					RayBundle::RayPacket & p = *topBundle.activePackets[0];
					for (int i = 0; i < MAX_RAYS_IN_PACKET; ++i)
					{
						if (lightColors[p.rayOffsets[i] / sizeof(Ray)].w == 0.0f)
							packetMask |= 1 << i;
					}
					topBundle.intersectionCache.mask = packetMask;
				}
			}
		}
		// interior
		else
		{
			unsigned int orderFlags = topBundle.IntersectAABB(nodes, node->t, octant);
			// any child intersected
			if (orderFlags > 0)
			{
				// both children intersected
				if (orderFlags >= 0x03)
				{
					unsigned char swp = (orderFlags >> 2) & 0x01;
					stack.Push(nodes + node->t + (swp ^ 1));
					node = nodes + node->t + swp;
					continue;
				}
				// one child interssected
				else
				{
					// right child intersected
					if (orderFlags >= 0x02)
					{
						node = nodes + node->t + 1;
						continue;
					}
					// left child intersected
					else
					{
						node = nodes + node->t;
						continue;
					}
				}
			}
		}

		// pop node
		if (stack.Empty())
			break;
		node = stack.Top();
		stack.Pop();
	}
}

void RayCaster::CastRayBundle(RayStream & rayStream, const BVHBottom & bvh, unsigned int octant, RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & bottomBundle = rayStream.GetBottomBundle();
	if (bottomBundle.GetNumberOfPackets(octant) == 0)
		return;

	// set active packets
	bottomBundle.SetActivePackets(octant);

	// nodes
	const BVHBottom::BVHNode * nodes = bvh.GetNodes(); 

	// stack => push root
	TraversalStack &stack = buffer.bottomBundleStack;
	stack.Clear();
	BVHTraversalInfo info = BVHTraversalInfo(0, bottomBundle.GetNumberOfPackets(octant) * MAX_RAYS_IN_PACKET, bottomBundle.GetNumberOfPackets(octant));

	// box swizzle offsets
	unsigned int xfirst = (octant & 1) ? 3 : 0;
	unsigned int yfirst = (octant & 2) ? 3 : 0;
    unsigned int zfirst = (octant & 4) ? 3 : 0;

	// box
	float fbox[6];

	// traversal loop
	while (true)
	{
		const BVHBottom::BVHNode * node = &nodes[info.nodeIndex];

		// swizzle box
		const float * fNodeBox = (const float*)&node->box;
		fbox[0] = fNodeBox[0 + xfirst];
		fbox[1] = fNodeBox[1 + yfirst];
		fbox[2] = fNodeBox[2 + zfirst];
		fbox[3] = fNodeBox[0 + (xfirst ^ 3)];
		fbox[4] = fNodeBox[1 + (yfirst ^ 3)];
		fbox[5] = fNodeBox[2 + (zfirst ^ 3)];

		// intersect node
		unsigned int hitPackets = info.numberOfPackets;
		unsigned int hitRays;
		if (bottomCulling)
			hitRays = bottomBundle.IntersectAABBCull(fbox, info.numberOfPackets);
		else
			hitRays = bottomBundle.IntersectAABB(fbox, info.numberOfPackets);

		// test clean miss
		if (hitRays)
		{
			_mm_prefetch((const char*)&nodes[node->t], _MM_HINT_T1);

			// skip ray reshuffling on a clean hit
			if(hitRays < info.numberOfRays)
			{
				hitPackets = bottomBundle.RemoveMissedPackets(info.numberOfPackets);
#if USE_REORDERING
				// re-sort incoherent packets if coherency is low enough
				if(hitPackets > 1 && (MAX_RAYS_IN_PACKET * hitPackets - hitRays) >= (MAX_RAYS_IN_PACKET >> 1) * hitPackets) // 50% utilization
				{           
					bottomBundle.ReorderRays(hitPackets);
					// packets are now fully coherent.  There is at most one underutilized packet.
					// reduce the number of active groups and append reordered packets onto end of group list
					hitPackets = IntDivCeilLog(hitRays, LOG_MAX_RAYS_IN_PACKET);
				}
#endif
			}

			// leaf
			if (node->IsLeaf())
			{
				// test triangles agains ray packets
				bottomBundle.IntersectTriangles(bvh.GetTriangleCols().data(), node->offset, node->offset + node->t, hitPackets);	
			}
			// single packet traversal
			else if (hitPackets <= 1)
			{
				assert(hitPackets > 0);
				CastRayPacket(rayStream, bvh, info.nodeIndex, octant, buffer);
			}
			// interior
			else
			{
				unsigned int axis = GetMaxExtentAxis(node->box);
				unsigned int swp = (octant >> axis) & 1;
				stack.Push(BVHTraversalInfo(node->t + (swp ^ 1), hitRays, hitPackets));
				info = BVHTraversalInfo(node->t + swp, hitRays, hitPackets);
				continue;
			}
		}

		// pop node
		if (stack.Empty())
			break;
		info = stack.Top();
		stack.Pop();
	}
}

void RayCaster::CastRayBundleShadow(RayStream & rayStream, const BVHBottom & bvh, unsigned int octant, RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & bottomBundle = rayStream.GetBottomBundle();
	if (bottomBundle.GetNumberOfPackets(octant) == 0)
		return;

	// set active packets
	bottomBundle.SetActivePackets(octant);

	// number of packets
	unsigned int numberOfAllPackets = bottomBundle.GetNumberOfPackets(octant);

	// nodes
	const BVHBottom::BVHNode * nodes = bvh.GetNodes(); 

	// stack => push root
	TraversalStack &stack = buffer.bottomBundleStack;
	stack.Clear();
	BVHTraversalInfo info = BVHTraversalInfo(0, bottomBundle.GetNumberOfPackets(octant) * MAX_RAYS_IN_PACKET, bottomBundle.GetNumberOfPackets(octant));

	// box swizzle offsets
	unsigned int xfirst = (octant & 1) ? 3 : 0;
	unsigned int yfirst = (octant & 2) ? 3 : 0;
	unsigned int zfirst = (octant & 4) ? 3 : 0;

	// box
	float fbox[6];

	// traversal loop
	while (true)
	{
		if (info.numberOfPackets != 0)
		{
			const BVHBottom::BVHNode * node = &nodes[info.nodeIndex];

			// swizzle box
			const float * fNodeBox = (const float*)&node->box;
			fbox[0] = fNodeBox[0 + xfirst];
			fbox[1] = fNodeBox[1 + yfirst];
			fbox[2] = fNodeBox[2 + zfirst];
			fbox[3] = fNodeBox[0 + (xfirst ^ 3)];
			fbox[4] = fNodeBox[1 + (yfirst ^ 3)];
			fbox[5] = fNodeBox[2 + (zfirst ^ 3)];

			// intersect node
			unsigned int hitPackets = info.numberOfPackets;
			unsigned int hitRays;
			if (bottomCulling)
				hitRays = bottomBundle.IntersectAABBCull(fbox, info.numberOfPackets);
			else
				hitRays = bottomBundle.IntersectAABB(fbox, info.numberOfPackets);

			// clean miss
			if (hitRays)
			{
				_mm_prefetch((const char*)&nodes[node->t], _MM_HINT_T1);

				// skip ray reshuffling on a clean hit
				if(hitRays < info.numberOfRays)
				{
					hitPackets = bottomBundle.RemoveMissedPackets(info.numberOfPackets);
#if USE_REORDERING
					// re-sort incoherent packets if coherency is low enough
					if(hitPackets > 1 && (MAX_RAYS_IN_PACKET * hitPackets - hitRays) >= (MAX_RAYS_IN_PACKET >> 1) * hitPackets) // 50% utilization
					{           
						bottomBundle.ReorderRays(hitPackets);
						// packets are now fully coherent.  There is at most one underutilized packet.
						// reduce the number of active groups and append reordered packets onto end of group list
						hitPackets = IntDivCeilLog(hitRays, LOG_MAX_RAYS_IN_PACKET);
					}
#endif
				}
				// leaf
				if (node->IsLeaf())
				{
					// test triangles agains ray packets
					unsigned int unoccludedPackets = bottomBundle.IntersectTrianglesShadow(bvh.GetTriangleCols().data(), node->offset, node->offset + node->t, hitPackets, numberOfAllPackets);	
					if (unoccludedPackets < hitPackets)
					{
						// some packets were completely occluded and dropped out.  Shift the rest of the active packets array backwards
						bottomBundle.ShiftPackets(hitPackets, numberOfAllPackets, unoccludedPackets);
						unsigned int droppedPackets = hitPackets - unoccludedPackets;
						// remove completely occluded packets
						numberOfAllPackets -= droppedPackets;
						// adjust stack entries to make it look like the occluded packets never existed
						for (size_t i = 0; i < stack.Size(); ++i)
						{
							//if (stack[i].numberOfRays - 8 * droppedPackets < 0) LOG("<RayCaster> Invalid number of rays.");
							stack[i].numberOfPackets -= droppedPackets;
							stack[i].numberOfRays -= 8 * droppedPackets;
						}
					}
				}
#if USE_BOTTOM_SHADOW_PACKET
				// single packet traversal
				else if (hitPackets <= 1)
				{
					assert(hitPackets > 0);
					CastRayPacketShadow(rayStream, bvh, info.nodeIndex, octant, buffer);
					if (bottomBundle.intersectionCache.mask == FULL_HIT)
					{
						// some packets were completely occluded and dropped out.  Shift the rest of the active packets array backwards
						bottomBundle.ShiftPackets(1, numberOfAllPackets, 0);
						// remove completely occluded packets
						numberOfAllPackets -= 1;
						// adjust stack entries to make it look like the occluded packets never existed
						for (size_t i = 0; i < stack.Size(); ++i)
						{
							//if (stack[i].numberOfRays - 8 < 0) LOG("<RayCaster> Invalid number of rays.");
							stack[i].numberOfPackets -= 1;
							stack[i].numberOfRays -= 8;
						}
					}
				}
#endif
				// interior
				else
				{
					stack.Push(BVHTraversalInfo(node->t + 1, hitRays, hitPackets));
					info = BVHTraversalInfo(node->t, hitRays, hitPackets);
					continue;
				}
			}
		}

		// pop node
		if (stack.Empty())
			break;
		info = stack.Top();
		stack.Pop();
	}
}

void RayCaster::CastRayBundle(RayStream & rayStream, const BVHBottom & bvh, RayCaster::Buffers &buffer)
{
	RayBundle & bottomBundle = rayStream.GetBottomBundle();
	bottomBundle.BuildPackets();
	bottomBundle.ClearInfos();
	for (unsigned int octant = 0; octant < 8; ++octant)
		CastRayBundle(rayStream, bvh, octant, buffer);
}

void RayCaster::CastRayBundleShadow(RayStream & rayStream, const BVHBottom & bvh, RayCaster::Buffers &buffer)
{
	RayBundle & bottomBundle = rayStream.GetBottomBundle();
	bottomBundle.BuildPackets();
	bottomBundle.ClearInfos();
	for (unsigned int octant = 0; octant < 8; ++octant)
		CastRayBundleShadow(rayStream, bvh, octant, buffer);
}

void RayCaster::CastRayBundle(RayStream & rayStream, const BVHTop & bvh, unsigned int octant, RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & topBundle = rayStream.GetTopBundle();
	if (topBundle.GetNumberOfPackets(octant) == 0)
		return;

	// set active packets
	topBundle.SetActivePackets(octant);

	// nodes
	const BVHTop::BVHNode * nodes = bvh.GetNodes(); 
	const BVHTop::BVHNode * node = &nodes[0];

	// infos and GAs
	auto & nodeInfos = bvh.GetNodeInfos();
	auto & geometryAccelerators = bvh.GetGeomAccelerators();

	// stack => push root
	TraversalStack &stack = buffer.topBundleStacks;
	stack.Clear();
	BVHTraversalInfo info = BVHTraversalInfo(0, topBundle.GetNumberOfPackets(octant) * MAX_RAYS_IN_PACKET, topBundle.GetNumberOfPackets(octant));

	// box swizzle offsets
	unsigned int xfirst = (octant & 1) ? 3 : 0;
	unsigned int yfirst = (octant & 2) ? 3 : 0;
	unsigned int zfirst = (octant & 4) ? 3 : 0;

	// box
	float fbox[6];

	// traversal loop
	while (true)
	{
		const BVHTop::BVHNode * node = &nodes[info.nodeIndex];
		// swizzle box
		const float * fNodeBox = (const float*)&node->box;
		fbox[0] = fNodeBox[0 + xfirst];
		fbox[1] = fNodeBox[1 + yfirst];
		fbox[2] = fNodeBox[2 + zfirst];
		fbox[3] = fNodeBox[0 + (xfirst ^ 3)];
		fbox[4] = fNodeBox[1 + (yfirst ^ 3)];
		fbox[5] = fNodeBox[2 + (zfirst ^ 3)];

		// intersect node
		unsigned int hitPackets = info.numberOfPackets;
		unsigned int hitRays;
		if (topCulling)
			hitRays = topBundle.IntersectAABBCull(fbox, info.numberOfPackets);
		else
			hitRays = topBundle.IntersectAABB(fbox, info.numberOfPackets);

		// clean miss
		if (hitRays)
		{
			_mm_prefetch((const char*)&nodes[node->t], _MM_HINT_T1);

			// skip ray reshuffling on a clean hit
			if(hitRays < info.numberOfRays)
			{
				hitPackets = topBundle.RemoveMissedPackets(info.numberOfPackets);
#if USE_REORDERING
				// re-sort incoherent packets if coherency is low enough
				if(node->IsLeaf() || (hitPackets > 1 && (MAX_RAYS_IN_PACKET * hitPackets - hitRays) >= (MAX_RAYS_IN_PACKET >> 1) * hitPackets)) // 50% utilization
				{           
					topBundle.ReorderRays(hitPackets);
					// packets are now fully coherent.  There is at most one underutilized packet.
					// reduce the number of active groups and append reordered packets onto end of group list
					hitPackets = IntDivCeilLog(hitRays, LOG_MAX_RAYS_IN_PACKET);
				}
#endif
			}
			// leaf
			if (node->IsLeaf())
			{
				// node is leaf - find intersect with acceleration structure
				NODE_ID nodeID = (NODE_ID)node->offset;
				GEOMETRY_ID gid = nodeInfos[nodeID].geometryID;
				BVHBottom * ga = geometryAccelerators[gid];
				if (ga) 
				{
					// transform packets
					rayStream.TransformDown(nodeInfos[nodeID], hitPackets);
					// traverse to lower levels
					CastRayBundle(rayStream, *ga, buffer);
					// transform packets back
					rayStream.TransformUp(*ga, nodeID, nodeInfos[nodeID], hitPackets);
				}
			}
			// single packet traversal
			else if (hitPackets <= 1)
			{
				assert(hitPackets > 0);
				CastRayPacket(rayStream, bvh, info.nodeIndex, octant, buffer);
			}
			// interior
			else
			{
				unsigned int axis = GetMaxExtentAxis(node->box);
				unsigned int swp = (octant >> axis) & 1;
				stack.Push(BVHTraversalInfo(node->t + (swp ^ 1), hitRays, hitPackets));
				info = BVHTraversalInfo(node->t + swp, hitRays, hitPackets);
				continue;
			}
		}

		// pop node
		if (stack.Empty())
			break;
		info = stack.Top();
		stack.Pop();
	}
}

void RayCaster::CastRayBundleShadow(RayStream & rayStream, const BVHTop & bvh, MaterialCache & materialCache, Vector4 lightColors[], float rayOffset, unsigned int octant, RayCaster::Buffers &buffer)
{
	// ray bundle
	RayBundle & topBundle = rayStream.GetTopBundle();
	if (topBundle.GetNumberOfPackets(octant) == 0)
		return;

	// set active packets
	topBundle.SetActivePackets(octant);

	// number of packets
	unsigned int numberOfAllPackets = topBundle.GetNumberOfPackets(octant);

	// nodes
	const BVHTop::BVHNode * nodes = bvh.GetNodes(); 
	const BVHTop::BVHNode * node = &nodes[0];

	// infos and GAs
	auto & nodeInfos = bvh.GetNodeInfos();
	auto & geometryAccelerators = bvh.GetGeomAccelerators();

	// stack => push root
	TraversalStack &stack = buffer.topBundleStacks;
	stack.Clear();
	BVHTraversalInfo info = BVHTraversalInfo(0, topBundle.GetNumberOfPackets(octant) * MAX_RAYS_IN_PACKET, topBundle.GetNumberOfPackets(octant));

	// box swizzle offsets
	unsigned int xfirst = (octant & 1) ? 3 : 0;
	unsigned int yfirst = (octant & 2) ? 3 : 0;
	unsigned int zfirst = (octant & 4) ? 3 : 0;

	// box
	float fbox[6];

	// traversal loop
	while (true)
	{
		// pop node
		const BVHTop::BVHNode * node = &nodes[info.nodeIndex];
		if (info.numberOfPackets != 0)
		{
			// swizzle box
			const float * fNodeBox = (const float*)&node->box;
			fbox[0] = fNodeBox[0 + xfirst];
			fbox[1] = fNodeBox[1 + yfirst];
			fbox[2] = fNodeBox[2 + zfirst];
			fbox[3] = fNodeBox[0 + (xfirst ^ 3)];
			fbox[4] = fNodeBox[1 + (yfirst ^ 3)];
			fbox[5] = fNodeBox[2 + (zfirst ^ 3)];

			// intersect node
			unsigned int hitPackets = info.numberOfPackets;
			unsigned int hitRays;
			if (topCulling)
				hitRays = topBundle.IntersectAABBCull(fbox, info.numberOfPackets);
			else
				hitRays = topBundle.IntersectAABB(fbox, info.numberOfPackets);

			// clean miss
			if (hitRays)
			{
				_mm_prefetch((const char*)&nodes[node->t], _MM_HINT_T1);

				// skip ray reshuffling on a clean hit
				if(hitRays < info.numberOfRays)
				{
					hitPackets = topBundle.RemoveMissedPackets(info.numberOfPackets);
#if USE_REORDERING
					// re-sort incoherent packets if coherency is low enough
					if(node->IsLeaf() || (hitPackets > 1 && (MAX_RAYS_IN_PACKET * hitPackets - hitRays) >= (MAX_RAYS_IN_PACKET >> 1) * hitPackets)) // 50% utilization
					{
						topBundle.ReorderRays(hitPackets);
						// packets are now fully coherent.  There is at most one underutilized packet.
						// reduce the number of active groups and append reordered packets onto end of group list
						hitPackets = IntDivCeilLog(hitRays, LOG_MAX_RAYS_IN_PACKET);
					}
#endif
				}

				// leaf
				if (node->IsLeaf())
				{
					// node is leaf - find intersect with acceleration structure
					NODE_ID nodeID = (NODE_ID)node->offset;
					GEOMETRY_ID gid = nodeInfos[nodeID].geometryID;
					BVHBottom * ga = geometryAccelerators[gid];
					if (ga) 
					{
						// transform packets
						rayStream.TransformDown(nodeInfos[nodeID], hitPackets);
						// traverse to lower levels
						if (nodeInfos[nodeID].material->flags & RTMaterial::ALPHA_TEST)
							CastRayBundle(rayStream, *ga, buffer);
						else 
							CastRayBundleShadow(rayStream, *ga, buffer);
						// transform packets back
						unsigned int unoccludedPackets = rayStream.TransformUpShadow(*ga, nodeInfos[nodeID], materialCache, lightColors, rayOffset, hitPackets);
						// skip complete miss
						if (unoccludedPackets < hitPackets)
						{
							// some packets were completely occluded and dropped out.  Shift the rest of the active packets array backwards
							topBundle.ShiftPackets(hitPackets, numberOfAllPackets, unoccludedPackets);
							unsigned int droppedPackets = hitPackets - unoccludedPackets;
							// remove completely occluded packets
							numberOfAllPackets -= droppedPackets;
							// adjust stack entries to make it look like the occluded packets never existed
							for (size_t i = 0; i < stack.Size(); ++i)
							{
								//if (stack[i].numberOfRays - 8 * droppedPackets < 0) LOG("<RayCaster> Invalid number of rays.");
								stack[i].numberOfPackets -= droppedPackets;
								stack[i].numberOfRays -= 8 * droppedPackets;
							}
						}
					}
				}

				// single packet traversal
#if USE_TOP_SHADOW_PACKET
				else if (hitPackets <= 1)
				{
					assert(hitPackets > 0);
					CastRayPacketShadow(rayStream, bvh, materialCache, lightColors, rayOffset, info.nodeIndex, octant, buffer);
					if (topBundle.intersectionCache.mask == FULL_HIT)
					{
						// some packets were completely occluded and dropped out.  Shift the rest of the active packets array backwards
						topBundle.ShiftPackets(1, numberOfAllPackets, 0);
						// remove completely occluded packets
						numberOfAllPackets -= 1;
						// adjust stack entries to make it look like the occluded packets never existed
						for (size_t i = 0; i < stack.Size(); ++i)
						{
							//if (stack[i].numberOfRays - 8 < 0) LOG("<RayCaster> Invalid number of rays.");
							stack[i].numberOfPackets -= 1;
							stack[i].numberOfRays -= 8;
						}
					}
				}
#endif

				// interior
				else
				{
					unsigned int axis = GetMaxExtentAxis(node->box);
					unsigned int swp = (octant >> axis) & 1;
					stack.Push(BVHTraversalInfo(node->t + (swp ^ 1), hitRays, hitPackets));
					info = BVHTraversalInfo(node->t + swp, hitRays, hitPackets);
					continue;
				}
			}
		}

		// pop node
		if (stack.Empty())
			break;
		info = stack.Top();
		stack.Pop();
	}
}

RayCaster::RayCaster() : topCulling(true), bottomCulling(true)
{
}

RayCaster::~RayCaster() 
{
}

void RayCaster::CastRayBundle(RayStream & rayStream, const BVHTop & bvh, RayCaster::Buffers &buffer)
{
	RayBundle & topBundle = rayStream.GetTopBundle();
	topBundle.ClearInfos();
	if (!bvh.IsEmpty())
	{
		topBundle.BuildPackets();
		for (unsigned int octant = 0; octant < 8; ++octant)
			CastRayBundle(rayStream, bvh, octant, buffer);
	}
}

void RayCaster::CastRayBundleShadow(RayStream & rayStream, const BVHTop & bvh, MaterialCache & materialCache, Vector4 lightColors[], float rayOffset, RayCaster::Buffers &buffer)
{
	if (!bvh.IsEmpty())
	{
		RayBundle & topBundle = rayStream.GetTopBundle();
		topBundle.BuildPackets();
		topBundle.ClearInfos();
		for (unsigned int octant = 0; octant < 8; ++octant)
			CastRayBundleShadow(rayStream, bvh, materialCache, lightColors, rayOffset, octant, buffer);
	}
}

void RayCaster::SetTopCulling(bool topCulling)
{
	this->topCulling = topCulling;
}

bool RayCaster::GetTopCulling()
{
	return topCulling;
}

void RayCaster::SetBottomCulling(bool bottomCulling)
{
	this->bottomCulling = bottomCulling;
}

bool RayCaster::GetBottomCulling()
{
	return bottomCulling;
}
