This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Tests/mathtest/obboxtest.cpp

1078 lines
33 KiB
C++

/*
** 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);
}