/*********************************************************************

Name:		Raytracer
Author:		Kamil Szatkowski "Netrix" (C)
Website:	http://www.netrix.org.pl/

*********************************************************************/
#include "TriBoxIntersection.hpp"
using namespace NIne;

#define FINDMINMAX(x0, x1, x2, fMin, fMax) \
  fMin = fMax = x0; \
  if(x1 < fMin) fMin = x1; \
  if(x1 > fMax) fMax = x1; \
  if(x2 < fMin) fMin = x2; \
  if(x2 > fMax) fMax = x2;
// X-tests
#define AXISTEST_X01( a, b, fa, fb )											\
	p0 = a * v0.v[1] - b * v0.v[2], p2 = a * v2.v[1] - b * v2.v[2]; \
    if (p0 < p2) { fMin = p0; fMax = p2;} else { fMin = p2; fMax = p0; }			\
	rad = fa * vBoxHalfSize.v[1] + fb * vBoxHalfSize.v[2];				\
	if (fMin > rad || fMax < -rad) return 0;
#define AXISTEST_X2( a, b, fa, fb )												\
	p0 = a * v0.v[1] - b * v0.v[2], p1 = a * v1.v[1] - b * v1.v[2];	\
    if (p0 < p1) { fMin = p0; fMax = p1; } else { fMin = p1; fMax = p0;}			\
	rad = fa * vBoxHalfSize.v[1] + fb * vBoxHalfSize.v[2];				\
	if(fMin>rad || fMax<-rad) return 0;
// Y-tests
#define AXISTEST_Y02( a, b, fa, fb )											\
	p0 = -a * v0.v[0] + b * v0.v[2], p2 = -a * v2.v[0] + b * v2.v[2]; \
    if(p0 < p2) { fMin = p0; fMax = p2; } else { fMin = p2; fMax = p0; }			\
	rad = fa * vBoxHalfSize.v[0] + fb * vBoxHalfSize.v[2];				\
	if (fMin > rad || fMax < -rad) return 0;
#define AXISTEST_Y1( a, b, fa, fb )												\
	p0 = -a * v0.v[0] + b * v0.v[2], p1 = -a * v1.v[0] + b * v1.v[2]; \
    if (p0 < p1) { fMin = p0; fMax = p1; } else { fMin = p1; fMax = p0; }			\
	rad = fa * vBoxHalfSize.v[0] + fb * vBoxHalfSize.v[2];				\
	if (fMin > rad || fMax < -rad) return 0;
// Z-tests
#define AXISTEST_Z12( a, b, fa, fb )											\
	p1 = a * v1.v[0] - b * v1.v[1], p2 = a * v2.v[0] - b * v2.v[1]; \
    if(p2 < p1) { fMin = p2; fMax = p1; } else { fMin = p1; fMax = p2; }			\
	rad = fa * vBoxHalfSize.v[0] + fb * vBoxHalfSize.v[1];				\
	if (fMin > rad || fMax < -rad) return 0;
#define AXISTEST_Z0( a, b, fa, fb )												\
	p0 = a * v0.v[0] - b * v0.v[1], p1 = a * v1.v[0] - b * v1.v[1];	\
    if(p0 < p1) { fMin = p0; fMax = p1; } else { fMin = p1; fMax = p0; }			\
	rad = fa * vBoxHalfSize.v[0] + fb * vBoxHalfSize.v[1];				\
	if (fMin > rad || fMax < -rad) return 0;


bool PlaneBoxOverlap(const NMVector3f& vNormal, const NMVector3f& vVert, const NMVector3f& vMaxBox)
{
	NMVector3f vResult = NMVector3fGreater(vNormal, NMVector3fLoadZeros());
	NMVector3f vMin = vMaxBox - NMVector3fAnd(vResult, 2 * vMaxBox) - vVert,
			 vMax = vMaxBox - NMVector3fAndNot(vResult, 2 * vMaxBox) - vVert;

	if(NMVector3fDot(vNormal, vMin) > 0.0f)		{ return false; }
	if(NMVector3fDot(vNormal, vMax) >= 0.0f)	{ return true; }
	return false;
}

bool TriBoxIntersection(const NMVector3f& vBoxCentre, 
						const NMVector3f& vBoxHalfSize,
						const NMVector3f& vp0, 
						const NIne::NMVector3f& vp1, 
						const NIne::NMVector3f& vp2)
{
	NMVector3f e0, e1, e2;
	float fMin, fMax, p0, p1, p2, rad, fex, fey, fez;

	NMVector3f v0 = vp0 - vBoxCentre;
	NMVector3f v1 = vp1 - vBoxCentre;
	NMVector3f v2 = vp2 - vBoxCentre;

	e0 = v1 - v0;
	e1 = v2 - v1;
	e2 = v0 - v2;

	fex = fabsf( e0.v[0] );
	fey = fabsf( e0.v[1] );
	fez = fabsf( e0.v[2] );
	AXISTEST_X01( e0.v[2], e0.v[1], fez, fey );
	AXISTEST_Y02( e0.v[2], e0.v[0], fez, fex );
	AXISTEST_Z12( e0.v[1], e0.v[0], fey, fex );
	fex = fabsf( e1.v[0] );
	fey = fabsf( e1.v[1] );
	fez = fabsf( e1.v[2] );
	AXISTEST_X01( e1.v[2], e1.v[1], fez, fey );
	AXISTEST_Y02( e1.v[2], e1.v[0], fez, fex );
	AXISTEST_Z0 ( e1.v[1], e1.v[0], fey, fex );
	fex = fabsf( e2.v[0] );
	fey = fabsf( e2.v[1] );
	fez = fabsf( e2.v[2] );
	AXISTEST_X2 ( e2.v[2], e2.v[1], fez, fey );
	AXISTEST_Y1 ( e2.v[2], e2.v[0], fez, fex );
	AXISTEST_Z12( e2.v[1], e2.v[0], fey, fex );
	FINDMINMAX( v0.v[0], v1.v[0], v2.v[0], fMin, fMax );

	if (fMin > vBoxHalfSize.v[0] || fMax < -vBoxHalfSize.v[0]) return false;
	FINDMINMAX( v0.v[1], v1.v[1], v2.v[1], fMin, fMax );
	if (fMin > vBoxHalfSize.v[1] || fMax < -vBoxHalfSize.v[1]) return false;
	FINDMINMAX( v0.v[2], v1.v[2], v2.v[2], fMin, fMax );
	if (fMin > vBoxHalfSize.v[2] || fMax < -vBoxHalfSize.v[2]) return false;

	if (!PlaneBoxOverlap(NMVector3fCross(e0, e1), v0, vBoxHalfSize)) return false;
	return true;
}