/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program 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 3 of the License, or
**	(at your option) any later version.
**
**	This program 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 program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : WWMath                                                       *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Tests/mathtest/obboxtest.cpp                 $*
 *                                                                                             *
 *                       Author:: Greg Hjelstrom                                               *
 *                                                                                             *
 *                     $Modtime:: 12/06/00 9:33a                                              $*
 *                                                                                             *
 *                    $Revision:: 8                                                           $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 *   test_obb_tri -- performs some "hard-coded" obb-tri collision tests                        *
 *   brute_force_cast_obb_tri -- binary-search method of finding the collision time for obb-tr *
 *   brute_force_obb_tri_test -- collide random obb's into random tri's                        *
 *   test_obb_obb -- performs some "hard-coded" obb-obb collision tests                        *
 *   brute_force_cast_obb_obb -- brute force function to verify collision of two obb's         *
 *   brute_force_obb_obb_test -- collide random obb's together                                 *
 *   Test_OBBoxes -- run all of the obb-obb and obb-tri tests                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "obboxtest.h"
#include "output.h"
#include "vector3.h"
#include "tri.h"
#include "obbox.h"
#include "wwmath.h"
#include "colmath.h"
#include "p_timer.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


//#define MANUAL_DEBUGGING


static Matrix3 _RotateZ45
(
	(float)WWMATH_SQRT2/2.0f,	-(float)WWMATH_SQRT2/2.0f,	0.0f,
	(float)WWMATH_SQRT2/2.0f,	 (float)WWMATH_SQRT2/2.0f,	0.0f,
							  0.0f,								0.0f,	1.0f
);

static Matrix3 _RotateZ90
(
	0.0f, -1.0f, 0.0f,
	1.0f,  0.0f, 0.0f,
	0.0f,  0.0f, 1.0f
);


/*********************************************************************
**
** OBBoxTriTestClass
** Test Data for OBBox->Triangle collision
**
*********************************************************************/
class OBBoxTriTestClass 
{
public:
	OBBoxClass		Box;
	Vector3			BoxMove;
	Vector3			V0;		
	Vector3			V1;
	Vector3			V2;
	Vector3			N;
	TriClass			Tri;
	float				Fraction;
	bool				StartBad;
	
	OBBoxTriTestClass
	(
		const Vector3 &c,					// center of box
		const Vector3 &e,					// extent of box
		const Matrix3 &b,					// basis of box
		const Vector3 &m,					// move for the box
		const Vector3 &v0,				// v0 of triangle
		const Vector3 &v1,				// v1 of triangle
		const Vector3 &v2,				// v2 of triangle
		float frac,							// expected fraction
		bool sol								// expected start solid 
	)
	{
		BoxMove = m;
		V0 = v0;
		V1 = v1;
		V2 = v2;
		Fraction = frac;
		StartBad = sol;

		/*
		** Initialize the triangle
		*/
		Tri.V[0] = &V0;
		Tri.V[1] = &V1;
		Tri.V[2] = &V2;
		Tri.N = & N;
		Tri.Compute_Normal();

		/*
		** Initialize the box
		*/
		Box.Center = c;
		Box.Extent = e;
		Box.Basis = b;
	}
};

OBBoxTriTestClass Test0
(
	Vector3(0,0,0),			// box starting at origin
	Vector3(2,1,1),			// extent is 2 units along x, 1 y, 1 z
	Matrix3(1),
	Vector3(1,0,0),			// moving 5 along x axis
	Vector3(6,-3,-1),			// triangle crossing x and y extent but not colliding
	Vector3(8,-1,2),
	Vector3(9,0,-1),
	1.0f,
	false
);

OBBoxTriTestClass Test1
(
	Vector3(3,0,0),			
	Vector3(1,2,1),
	Matrix3(1),
	Vector3(0,-2,0),
	Vector3(1,-3,0),
	Vector3(2,-3,5),
	Vector3(6,-3,1),
	0.5f,
	false
);

OBBoxTriTestClass Test2
(
	Vector3(-3.5,-1.5,0),	// sweeping a 3x3 box along pos x and neg y
	Vector3(1.5,1.5,1.5),
	Matrix3(1),
	Vector3(4,-4,0),
	Vector3(-4,-4,-1),		// into a polygon in y-z plane
	Vector3(-2,-4,5),
	Vector3(0,-4,1),
	0.25f,						// should only move 25%
	false
);

OBBoxTriTestClass Test3
(
	Vector3(-3.5,-1.5,0) + 0.25f * Vector3(4,-4,0),
	Vector3(1.5,1.5,1.5),	// starting at end of test2's move, should be 0.0 but not StartBad!
	Matrix3(1),
	Vector3(4,-4,0),
	Vector3(-4,-4,-1),		// into a polygon in y-z plane
	Vector3(-2,-4,5),
	Vector3(0,-4,1),
	0.0f,						
	false
);

OBBoxTriTestClass Test4
(
	Vector3(-3.5f,-1.5f,0),	// Same as test 2
	Vector3(1.5f,1.5f,1.5f),
	Matrix3(1),
	Vector3(4,-4,0),
	Vector3(-9,-4,-1),		// into a polygon in y-z plane *but* just barely not in the way
	Vector3(-8,-4,5),
	Vector3(-4.9f,-4,0),
	1.0f,							// should move 100%
	false
);

OBBoxTriTestClass Test5
(
	Vector3(-3.5,-1.5,0),	// Same as test 3 with box brushing polygon vertex.
	Vector3(1.5,1.5,1.5),
	Matrix3(1),
	Vector3(4,-4,0),
	Vector3(-9,-4,-1),		// into a polygon in y-z plane just touching path of box
	Vector3(-8,-4,5),
	Vector3(-4,-4,0),
	1.0f,							// should move 100%
	false
);

OBBoxTriTestClass Test6
(
	Vector3(-3.5f,-1.5f,0),	// Same as test 3 with box brushing polygon vertex.
	Vector3(1.5f,1.5f,1.5f),
	Matrix3(1),
	Vector3(4,-4,0),
	
	Vector3(-9,-4,-1),		// into a polygon in y-z plane just barely hitting it
	Vector3(-8,-4,5),
	Vector3(-3.999f,-4,0),	// (-4,-4,0) would just "touch" (see test5)
	
	0.25f,						// should move 25%
	false
);

OBBoxTriTestClass Test7
(
	Vector3(0,0,0),			// This is a case where the box starts out intersecting
	Vector3(5,5,5),	
	Matrix3(1),
	Vector3(4,4,0),
	Vector3(1,4,-1),
	Vector3(2,4,5),
	Vector3(5,4,0),
	0.0f,					
	true
);

OBBoxTriTestClass Test8
(
	Vector3(-2.5,2,0),		// center
	Vector3(1.5,1,1),			// extent
	Matrix3(1),					// basis
	Vector3(3,0,0),			// move
	
	Vector3(1,2,0),			// v0
	Vector3(3,4,5),			// v1
	Vector3(4,5,-1),			// v2
	0.66666667f,					
	false
);

OBBoxTriTestClass Test9
(
	Vector3(0,0,0),		// Box with diagonal y-z length of 1, rotated 45 about z 
	Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
	_RotateZ45,
	Vector3(4,0,0),

	Vector3(3,2,-1),		// triangle blocking the move at x=3 (hitting back side)
	Vector3(3,0,1),		
	Vector3(3,-2,-1),

	0.5f,						// hitting another box edge-to-face halfway through the move
	false
);

OBBoxTriTestClass * OBBoxTriTestCases[] = 
{
	&Test0,
	&Test1,
	&Test2,
	&Test3,
	&Test4,
	&Test5,
	&Test6,
	&Test7,
	&Test8,
	&Test9
};


/***********************************************************************************************
 * test_obb_tri -- performs some "hard-coded" obb-tri collision tests                          *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   4/8/99     GTH : Created.                                                                 *
 *=============================================================================================*/
void test_obb_tri(void)
{
	Print_Title("Testing OBBox->Triangle collision.");

	/*
	** Test the sweep function with a bunch of boxes and triangles
	*/
	CastResultStruct result;
	result.ComputeContactPoint = true;
	int numtests = sizeof(OBBoxTriTestCases)/sizeof(OBBoxTriTestClass *);
	unsigned cycles;
	unsigned totalcycles = 0;

	// prime the cache
	OBBoxTriTestClass * testcase = OBBoxTriTestCases[9];
	cycles = Get_CPU_Clock();

	CollisionMath::Collide(	testcase->Box,
									testcase->BoxMove,
									testcase->Tri,
									Vector3(0,0,0),
									&result);

	cycles = Get_CPU_Clock() - cycles;


	// now time and test the routine
	for (int i=0; i<numtests; i++) {
		
		testcase = OBBoxTriTestCases[i];

		result.Fraction = 1.0;
		result.StartBad = false;
		result.Normal.Set(0,0,0);

		cycles = Get_CPU_Clock();

		CollisionMath::Collide(	testcase->Box,
										testcase->BoxMove,
										testcase->Tri,
										Vector3(0,0,0),
										&result);
	
		cycles = Get_CPU_Clock() - cycles;
		totalcycles += cycles;
		if ((WWMath::Fabs(testcase->Fraction - result.Fraction) > WWMATH_EPSILON) ||
			 (testcase->StartBad != result.StartBad))
		{
			printf("test: %3d  fraction: %8.6f   cycles: %12d \tfailed!\n",i,result.Fraction,cycles);
		} else {
			printf("test: %3d  fraction: %8.6f   cycles: %12d \tpassed...\n",i,result.Fraction,cycles);
		}

	}
	printf("average cycles: %d\n",totalcycles / numtests);
	printf("\n");


	/*
	** Test a box moving down the z-axis to a polygon, then moving along
	** the x-axis along the surface of the polygon.
	*/
	OBBoxClass testbox;
	Vector3 move;
	Vector3 v0,v1,v2,n;
	TriClass testtri;

	v0.Set(0,1,0);
	v1.Set(-1,-1,0);
	v2.Set(1,1,0);
	
	testtri.V[0] = &v0;
	testtri.V[1] = &v1;
	testtri.V[2] = &v2;
	testtri.N = &n;
	testtri.Compute_Normal();

	testbox.Center.Set(0,0,2.363f);
	testbox.Extent.Set(1,1,1);
	move.Set(0,0,-5.0f);

	CastResultStruct cres;
	cres.StartBad = 0;
	cres.Fraction = 1.0f;
	CollisionMath::Collide(testbox,move,testtri,Vector3(0,0,0),&cres);

	printf("fraction = %f\n",cres.Fraction);

	testbox.Center += cres.Fraction * move;

	move.Set(1.0f,1.0f,0.0f);

	cres.StartBad = 0;
	cres.Fraction = 1.0f;
	CollisionMath::Collide(testbox,move,testtri,Vector3(0,0,0),&cres);

	printf("fraction = %f\n",cres.Fraction);

	testbox.Center += cres.Fraction * move;

	/*
	** Try to get a timing of Oriented_Box_Intersects_Tri versus
	** CollisionMath::Intersect
	*/
	const int REPEAT = 50;
	cycles = Get_CPU_Clock();
	for (int j=0;j<REPEAT;j++) {
		testcase = OBBoxTriTestCases[7];
		CollisionMath::Intersection_Test(testcase->Box,testcase->Tri);
	}
	cycles = Get_CPU_Clock() - cycles;
	printf("CollisionMath::Intersect - average cycles: %d\n",cycles / REPEAT);
	printf("\n");

	cycles = Get_CPU_Clock();
	for (j=0;j<REPEAT;j++) {
		testcase = OBBoxTriTestCases[7];
		Oriented_Box_Intersects_Tri(testcase->Box,testcase->Tri);
	}
	cycles = Get_CPU_Clock() - cycles;
	printf("Oriented_Box_Intersects_Tri - average cycles: %d\n",cycles / REPEAT);
	printf("\n");

}



/***********************************************************************************************
 * brute_force_cast_obb_tri -- binary-search method of finding the collision time for obb-tri  *
 *                                                                                             *
 * This function doesn't really work in the general case.  Only when the endpoint of the move  *
 * is guaranteed to be inside the triangle                                                     *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   4/8/99     GTH : Created.                                                                 *
 *=============================================================================================*/
float brute_force_cast_obb_tri
(
	const OBBoxClass & box,
	const Vector3 & move,
	const TriClass & tri
)
{
	float istart = 0.0f;
	float iend = 1.0f;
	
	while (iend - istart > WWMATH_EPSILON/2.0f) {
		float icenter = (iend + istart) / 2.0f;
		OBBoxClass testbox = box;
		testbox.Center = box.Center + icenter*move;

		if (Oriented_Box_Intersects_Tri(testbox,tri)) {
			iend = icenter;
		} else {
			istart = icenter;
		}
	}
	return (iend + istart) / 2.0f;
}


/***********************************************************************************************
 * brute_force_obb_tri_test -- collide random obb's into random tri's                          *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   4/8/99     GTH : Created.                                                                 *
 *=============================================================================================*/
void brute_force_obb_tri_test(int test_count)
{
	Print_Title("Brute Force Testing OBBox->Tri collision.");
	
	Vector3 v[3];
	Vector3 n;
	OBBoxClass box;
	TriClass tri;
	Vector3 move;

	tri.V[0] = &v[0];
	tri.V[1] = &v[1];
	tri.V[2] = &v[2];
	tri.N = &n;
	
	int fail_count = 0;
	int startbad_count = 0;
	int startbad_fail_count = 0;
	int bad_points = 0;

	float min_fraction = 1.0f;
	float max_fraction = 0.0f;
	float avg_fraction = 0.0f;
	float avg_fraction_error = 0.0f;
	int fraction_error_count = 0;
	float max_error = 0.0f;

	for (int i=0; i<test_count; i++) {

		// v0 is always in positive quadrant
		v[0].X = WWMath::Random_Float(0,5);
		v[0].Y = WWMath::Random_Float(0,5);
		v[0].Z = WWMath::Random_Float(0,5);

		// v1 is always +x,+y,-z
		v[1].X = WWMath::Random_Float(0,5);
		v[1].Y = WWMath::Random_Float(0,5);
		v[1].Z = -WWMath::Random_Float(0,5);

		// v2 is always -x,-y,-z
		v[2].X = -WWMath::Random_Float(0,5);
		v[2].Y = -WWMath::Random_Float(0,5);
		v[2].Z = -WWMath::Random_Float(0,5);

		tri.Compute_Normal();

		// make a random box
		box.Init_Random(0.25f,3.0f);

		// put it in a random position in a 20x20x20 cube
		box.Center.X = WWMath::Random_Float(-10.0f,10.0f);
		box.Center.Y = WWMath::Random_Float(-10.0f,10.0f);
		box.Center.Z = WWMath::Random_Float(-10.0f,10.0f);

		// make a move vector that will move the box's center to the centroid of the tri
		Vector3 new_center;
		new_center.X = (v[0].X + v[1].X + v[2].X) / 3.0f;
		new_center.Y = (v[0].Y + v[1].Y + v[2].Y) / 3.0f;
		new_center.Z = (v[0].Z + v[1].Z + v[2].Z) / 3.0f;
		move = new_center - box.Center;
		
		// sweep box into tri!
		CastResultStruct result;
		result.ComputeContactPoint = true;
		CollisionMath::Collide(box,move,tri,Vector3(0,0,0),&result);

		// if they started out intersecting, this test doesn't count
		if (result.StartBad) {
			startbad_count++;
			if (!Oriented_Box_Intersects_Tri(box,tri)) {
				startbad_fail_count++;
				printf("False startbad!");
			}
		}

		// if they were intersecting, never mind
		if (!result.StartBad) {

			printf(".");
			bool success = true;

			// add the fraction into the total so we get an idea
			// how far our tests are going (hopefully a good mix)
			avg_fraction += result.Fraction;
			if (result.Fraction < min_fraction) min_fraction = result.Fraction;
			if (result.Fraction > max_fraction) max_fraction = result.Fraction;

			// verify that the fraction is correct
			float realfrac = brute_force_cast_obb_tri(box,move,tri);
			if (fabs(realfrac - result.Fraction) > WWMATH_EPSILON) {
				success = false;
				float error = fabs(realfrac - result.Fraction);
				avg_fraction_error += error;
				fraction_error_count++;
				if (error > max_error) max_error = error;
			}

			// verify that they are not intersecting at the end of the move
			// if the allowed move is smaller than epsilon, we skip this and don't move
			if (result.Fraction > WWMATH_EPSILON) {
				OBBoxClass box2 = box;
				CastResultStruct second_result;
				box2.Center += /*0.9999f **/ result.Fraction * move;
				CollisionMath::Collide(box2,move,tri,Vector3(0,0,0),&second_result);

				if (second_result.StartBad) success = false;
				if ((result.Fraction < 1.0f) && (second_result.Fraction > 0.01f)) success = false;
			}

			// if something failed, do the test again to let the programmer step through...
			if (!success) {
				fail_count++;
				CastResultStruct redo_result;
				redo_result.ComputeContactPoint = true;
				CollisionMath::Collide(box,move,tri,Vector3(0,0,0),&redo_result);
			} 
		}			
	}
	printf("\n");
	int passes = test_count - (startbad_fail_count + fail_count);
	printf("Passed %d tests out of %d tests.\n",passes,test_count);
	printf("StartBad tests: %d  failures: %d\n",startbad_count,startbad_fail_count);

	if (fraction_error_count > 0) {
		avg_fraction_error /= (float)fraction_error_count;
	}
	avg_fraction /= (float)(test_count - startbad_count);
	printf("Largest Fraction:     %f\n",max_fraction);
	printf("Smallest Fraction:    %f\n",min_fraction);
	printf("Average Fraction:     %f\n",avg_fraction);
	printf("Average Error:        %f\n",avg_fraction_error);
	printf("Biggest Error:        %f\n",max_error);
}




/*********************************************************************
**
** OBBoxTestClass
** Data for testing OBBox->OBBox collision detection
**
*********************************************************************/
class OBBoxTestClass
{
public:

	OBBoxClass		Box0;
	Vector3			Move0;
	OBBoxClass		Box1;
	Vector3			Move1;
	float				Fraction;
	bool				StartBad;
	
	OBBoxTestClass
	(
		const Vector3 & c0,		// center of box0
		const Vector3 & e0,		// extent of box0
		const Matrix3 & b0,		// basis of box0
		const Vector3 & m0,		// move for box0

		const Vector3 & c1,		// center of box1
		const Vector3 & e1,		// extent of box1
		const Matrix3 & b1,		// basis of box1
		const Vector3 & m1,		// move for box1

		float frac,					// expected fraction
		bool sol						// expected start solid 
	) :
		Box0(c0,e0,b0),
		Move0(m0),
		Box1(c1,e1,b1),
		Move1(m1),
		Fraction(frac),
		StartBad(sol)
	{
	}
};


OBBoxTestClass BTest0
(
	Vector3(0,0,0),		// center
	Vector3(4,0,0),		// extent
	Matrix3(1),				// basis
	Vector3(4,0,0),		// move
	Vector3(6,0,0),		// center
	Vector3(1,1,1),		// extent
	Matrix3(1),				// basis
	Vector3(0,0,0),		// move
	0.25f,					
	false
);

OBBoxTestClass BTest1
(
	Vector3(-3.5f,-1.5f,0.0f),
	Vector3(1.5f,1.5f,1.5f),
	Matrix3(1),
	Vector3(4,-4,0),

	Vector3(-5.1,-5,0),		
	Vector3(1,1,1),		
	Matrix3(1),				
	Vector3(0,0,0),		

	1.0f,						// should just barely go by (touches)
	false
);

OBBoxTestClass BTest2
(
	Vector3(3,0,0),
	Vector3(7,1,1),
	Matrix3(1),
	Vector3(4,-4,0),

	Vector3(9.5,0,0),		
	Vector3(1,6,1),		
	Matrix3(1),				
	Vector3(0,0,0),		

	0.0f,						// startbad
	true
);

OBBoxTestClass BTest3	
(
	Vector3(0,0,0),		// Box with diagonal y-z length of 1, rotated 45 about z 
	Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
	_RotateZ45,
	Vector3(4,0,0),

	Vector3(4,0,0),		// axis-aligned box blocking the move along the x-axis
	Vector3(1,3,1),		
	Matrix3(1),				
	Vector3(0,0,0),		

	0.5f,						// hitting another box edge-to-face halfway through the move
	false
);

OBBoxTestClass BTest4
(
	Vector3(0,0,0),		// Box with diagonal y-z length of 1, rotated 45 about z 
	Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
	_RotateZ45,
	Vector3(0,4,0),

	Vector3(0,4,0),		// axis-aligned box blocking the move along the x-axis
	Vector3(3,1,1),		
	Matrix3(1),				
	Vector3(0,0,0),		

	0.5f,						// hitting another box edge-to-face halfway through the move
	false
);

OBBoxTestClass BTest5
(
	Vector3(0,0,0),		// Box with diagonal y-z length of 1, rotated 45 about z 
	Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
	_RotateZ45,
	Vector3(0,-4,0),

	Vector3(0,-4,0),		// axis-aligned box blocking the move along the x-axis
	Vector3(3,1,1),		
	Matrix3(1),				
	Vector3(0,0,0),		

	0.5f,						// hitting another box edge-to-face halfway through the move
	false
);

OBBoxTestClass * OBBoxTestCases[] = 
{
	&BTest0,
	&BTest1,
	&BTest2,
	&BTest3,
	&BTest4,
	&BTest5
};


/***********************************************************************************************
 * test_obb_obb -- performs some "hard-coded" obb-obb collision tests                          *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   4/8/99     GTH : Created.                                                                 *
 *=============================================================================================*/
void test_obb_obb(void)
{
	Print_Title("Testing OBBox->OBBox collision.");

	CastResultStruct result;
	result.ComputeContactPoint = true;
	int numtests = sizeof(OBBoxTestCases)/sizeof(OBBoxTestClass *);
	unsigned cycles;
	unsigned totalcycles = 0;

	// prime the cache
	OBBoxTestClass * testcase = OBBoxTestCases[4];
	cycles = Get_CPU_Clock();

	CollisionMath::Collide(	testcase->Box0,
									testcase->Move0,
									testcase->Box1,
									testcase->Move1,
									&result);

	cycles = Get_CPU_Clock() - cycles;


	// now time and test the routine
	for (int i=0; i<numtests; i++) {
		
		testcase = OBBoxTestCases[i];

		result.Fraction = 1.0;
		result.StartBad = false;
		result.Normal.Set(0,0,0);

		cycles = Get_CPU_Clock();

		CollisionMath::Collide(	testcase->Box0,
										testcase->Move0,
										testcase->Box1,
										testcase->Move1,
										&result);
	
		cycles = Get_CPU_Clock() - cycles;
		totalcycles += cycles;
	
		if ((WWMath::Fabs(testcase->Fraction - result.Fraction) > WWMATH_EPSILON) ||
			 (testcase->StartBad != result.StartBad))
		{
			printf("test: %5d\tcycles: %12d \t\tfailed!\n",i,cycles);
		} else {
			printf("test: %5d\tcycles: %12d \t\tpassed...\n",i,cycles);
		}
	}
	printf("average cycles: %d\n",totalcycles / numtests);
	printf("\n");

	/*
	** Try to get an accurate timing, run the same (startbad) test
	** 100 times...
	*/
	const int REPEAT=50;
	cycles = Get_CPU_Clock();
	for (int j=0;j<REPEAT;j++) {
		testcase = OBBoxTestCases[0];
		result.Fraction = 1.0;
		result.StartBad = false;
		result.Normal.Set(0,0,0);
		CollisionMath::Collide(	testcase->Box0,
										testcase->Move0,
										testcase->Box1,
										testcase->Move1,
										&result);
	}
	cycles = Get_CPU_Clock() - cycles;
	printf("average cycles: %d\n",cycles / REPEAT);
	printf("\n");

	/*
	** Now, comparing Oriented_Boxes_Intersect with CollisionMath::Intersect
	*/
	cycles = Get_CPU_Clock();
	for (j=0;j<REPEAT;j++) {
		testcase = OBBoxTestCases[2];
		CollisionMath::Intersection_Test(testcase->Box0,testcase->Box1);
	}
	cycles = Get_CPU_Clock() - cycles;
	printf("CollisionMath::Intersect - average cycles: %d\n",cycles / REPEAT);
	printf("\n");

	cycles = Get_CPU_Clock();
	for (j=0;j<REPEAT;j++) {
		testcase = OBBoxTestCases[2];
		Oriented_Boxes_Intersect(testcase->Box0,testcase->Box1);
	}
	cycles = Get_CPU_Clock() - cycles;
	printf("Oriented_Boxes_Intersect - average cycles: %d\n",cycles / REPEAT);
	printf("\n");
}


/***********************************************************************************************
 * brute_force_cast_obb_obb -- brute force function to verify collision of two obb's           *
 *                                                                                             *
 * This function doesn't really work in the general case.  Only when the endpoint of the move  *
 * is guaranteed to be inside the other box...                                                 *
 *                                                                                             *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   4/8/99     GTH : Created.                                                                 *
 *=============================================================================================*/
float brute_force_cast_obb_obb
(
	const OBBoxClass & box0,
	const Vector3 & move0,
	const OBBoxClass & box1
)
{
	float istart = 0.0f;
	float iend = 1.0f;
	
	while (iend - istart > WWMATH_EPSILON) {
		float icenter = (iend + istart) / 2.0f;
		OBBoxClass testbox = box0;
		testbox.Center = box0.Center + icenter*move0;

		if (Oriented_Boxes_Intersect(testbox,box1)) {
			iend = icenter;
		} else {
			istart = icenter;
		}
	}
	return (iend + istart) / 2.0f;
}


/***********************************************************************************************
 * brute_force_obb_obb_test -- collide random obb's together                                   *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   4/8/99     GTH : Created.                                                                 *
 *=============================================================================================*/
void brute_force_obb_obb_test(int test_count)
{
	Print_Title("Brute Force Testing OBBox->OBBox collision.");
	
	OBBoxClass box0;
	OBBoxClass box1;
	Vector3 move0;

	int fail_count = 0;
	int startbad_count = 0;
	int startbad_fail_count = 0;

	float avg_fraction = 0.0f;
	float avg_fraction_error = 0.0f;
	int fraction_error_count = 0;
	float max_error = 0.0f;
	float min_fraction = 1.0f;
	float max_fraction = 0.0f;

	for (int i=0; i<test_count; i++) {

		box0.Init_Random(0.25f,3.0f);
		box1.Init_Random(0.25f,3.0f);
		
		box0.Center.X = WWMath::Random_Float(-10.0f,10.0f);
		box0.Center.Y = WWMath::Random_Float(-10.0f,10.0f);
		box0.Center.Z = WWMath::Random_Float(-10.0f,10.0f);
		box1.Center.Set(0,0,0);

		Vector3 newcenter;
		newcenter.X = WWMath::Random_Float(-0.12f,0.12f);	// new center must be inside other box
		newcenter.Y = WWMath::Random_Float(-0.12f,0.12f);
		newcenter.Z = WWMath::Random_Float(-0.12f,0.12f);
		move0 = newcenter - box0.Center;

		// sweep box0 into box1
		CastResultStruct result;
		result.ComputeContactPoint = true;
		CollisionMath::Collide(box0,move0,box1,Vector3(0,0,0),&result);

		// if they were intersecting, verify that
		if (result.StartBad) {
			startbad_count++;
			if (!Oriented_Boxes_Intersect(box0,box1)) {
				startbad_fail_count++;
				printf("False startbad!");
			}
		}

		// if they weren't intersecting, verify the allowed move
		if (!result.StartBad) {

			bool success = true;

			// add the fraction into the total so we get an idea
			// how far our tests are going (hopefully a good mix)
			avg_fraction += result.Fraction;
			if (result.Fraction < min_fraction) min_fraction = result.Fraction;
			if (result.Fraction > max_fraction) max_fraction = result.Fraction;

			// verify that the fraction is correct
			float realfrac = brute_force_cast_obb_obb(box0,move0,box1);
			if (fabs(realfrac - result.Fraction) > WWMATH_EPSILON) {
				success = false;
				float error = fabs(realfrac - result.Fraction);
				avg_fraction_error += error;
				fraction_error_count++;
				if (error > max_error) max_error = error;
			}
			
			// verify that they are not intersecting now
			// if the allowed move is smaller than epsilon, we skip this and don't move
			if (result.Fraction > WWMATH_EPSILON) {

				OBBoxClass box2 = box0;
				box2.Center += (1.0f - WWMATH_EPSILON) * result.Fraction * move0;

				CastResultStruct second_result;
				CollisionMath::Collide(box2,move0,box1,Vector3(0,0,0),&second_result);

				if (second_result.StartBad) {
					success = false;
#ifdef MANUAL_DEBUGGING
					_asm int 0x03;
					while (!success) {
						CastResultStruct move_result;
						CollisionMath::Collide(box0,move0,box1,Vector3(0,0,0),&move_result);
						second_result.Reset();
						CollisionMath::Collide(box2,move0,box1,Vector3(0,0,0),&second_result);
					}
#endif
				}
				if ((result.Fraction < 1.0f) && (second_result.Fraction > 0.01f)) success = false;
			}

			// if something failed, do the test again to let the programmer step through...
			if (!success) {
#ifdef MANUAL_DEBUGGING
				_asm int 0x03;
				while (!success) {
					fail_count++;
					CastResultStruct redo_result;
					redo_result.ComputePoint = true;
					CollisionMath::Collide(box0,move0,box1,Vector3(0,0,0),&redo_result);
				}			
#endif
				printf("x");
			} else {
				printf(".");
			}
		}			
	}
	printf("\n");

	int passes = test_count - (startbad_fail_count + fail_count);
	printf("Passed %d tests out of %d tests.\n",passes,test_count);
	printf("StartBad tests: %d  failures: %d\n",startbad_count,startbad_fail_count);

	if (fraction_error_count) {
		avg_fraction_error /= (float)fraction_error_count;
	}
	avg_fraction /= (float)(test_count - startbad_count);

	printf("Largest Fraction:   %f\n",max_fraction);
	printf("Smallest Fraction:  %f\n",min_fraction);
	printf("Average Fraction:   %f\n",avg_fraction);
	printf("Average Error:      %f\n",avg_fraction_error);
	printf("Biggest Error:      %f\n",max_error);
}



/***********************************************************************************************
 * Test_OBBoxes -- run all of the obb-obb and obb-tri tests                                    *
 *                                                                                             *
 * INPUT:                                                                                      *
 *                                                                                             *
 * OUTPUT:                                                                                     *
 *                                                                                             *
 * WARNINGS:                                                                                   *
 *                                                                                             *
 * HISTORY:                                                                                    *
 *   4/8/99     GTH : Created.                                                                 *
 *=============================================================================================*/
void Test_OBBoxes(void)
{
	const int TESTCOUNT = 100;
	srand(time(NULL));

	test_obb_tri();
	test_obb_obb();
	brute_force_obb_tri_test(TESTCOUNT);
	brute_force_obb_obb_test(TESTCOUNT);
}



