obboxtest.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** 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 ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWMath *
  23. * *
  24. * $Archive:: /Commando/Code/Tests/mathtest/obboxtest.cpp $*
  25. * *
  26. * Author:: Greg Hjelstrom *
  27. * *
  28. * $Modtime:: 12/06/00 9:33a $*
  29. * *
  30. * $Revision:: 8 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * test_obb_tri -- performs some "hard-coded" obb-tri collision tests *
  35. * brute_force_cast_obb_tri -- binary-search method of finding the collision time for obb-tr *
  36. * brute_force_obb_tri_test -- collide random obb's into random tri's *
  37. * test_obb_obb -- performs some "hard-coded" obb-obb collision tests *
  38. * brute_force_cast_obb_obb -- brute force function to verify collision of two obb's *
  39. * brute_force_obb_obb_test -- collide random obb's together *
  40. * Test_OBBoxes -- run all of the obb-obb and obb-tri tests *
  41. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  42. #include "obboxtest.h"
  43. #include "output.h"
  44. #include "vector3.h"
  45. #include "tri.h"
  46. #include "obbox.h"
  47. #include "wwmath.h"
  48. #include "colmath.h"
  49. #include "p_timer.h"
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <time.h>
  53. //#define MANUAL_DEBUGGING
  54. static Matrix3 _RotateZ45
  55. (
  56. (float)WWMATH_SQRT2/2.0f, -(float)WWMATH_SQRT2/2.0f, 0.0f,
  57. (float)WWMATH_SQRT2/2.0f, (float)WWMATH_SQRT2/2.0f, 0.0f,
  58. 0.0f, 0.0f, 1.0f
  59. );
  60. static Matrix3 _RotateZ90
  61. (
  62. 0.0f, -1.0f, 0.0f,
  63. 1.0f, 0.0f, 0.0f,
  64. 0.0f, 0.0f, 1.0f
  65. );
  66. /*********************************************************************
  67. **
  68. ** OBBoxTriTestClass
  69. ** Test Data for OBBox->Triangle collision
  70. **
  71. *********************************************************************/
  72. class OBBoxTriTestClass
  73. {
  74. public:
  75. OBBoxClass Box;
  76. Vector3 BoxMove;
  77. Vector3 V0;
  78. Vector3 V1;
  79. Vector3 V2;
  80. Vector3 N;
  81. TriClass Tri;
  82. float Fraction;
  83. bool StartBad;
  84. OBBoxTriTestClass
  85. (
  86. const Vector3 &c, // center of box
  87. const Vector3 &e, // extent of box
  88. const Matrix3 &b, // basis of box
  89. const Vector3 &m, // move for the box
  90. const Vector3 &v0, // v0 of triangle
  91. const Vector3 &v1, // v1 of triangle
  92. const Vector3 &v2, // v2 of triangle
  93. float frac, // expected fraction
  94. bool sol // expected start solid
  95. )
  96. {
  97. BoxMove = m;
  98. V0 = v0;
  99. V1 = v1;
  100. V2 = v2;
  101. Fraction = frac;
  102. StartBad = sol;
  103. /*
  104. ** Initialize the triangle
  105. */
  106. Tri.V[0] = &V0;
  107. Tri.V[1] = &V1;
  108. Tri.V[2] = &V2;
  109. Tri.N = & N;
  110. Tri.Compute_Normal();
  111. /*
  112. ** Initialize the box
  113. */
  114. Box.Center = c;
  115. Box.Extent = e;
  116. Box.Basis = b;
  117. }
  118. };
  119. OBBoxTriTestClass Test0
  120. (
  121. Vector3(0,0,0), // box starting at origin
  122. Vector3(2,1,1), // extent is 2 units along x, 1 y, 1 z
  123. Matrix3(1),
  124. Vector3(1,0,0), // moving 5 along x axis
  125. Vector3(6,-3,-1), // triangle crossing x and y extent but not colliding
  126. Vector3(8,-1,2),
  127. Vector3(9,0,-1),
  128. 1.0f,
  129. false
  130. );
  131. OBBoxTriTestClass Test1
  132. (
  133. Vector3(3,0,0),
  134. Vector3(1,2,1),
  135. Matrix3(1),
  136. Vector3(0,-2,0),
  137. Vector3(1,-3,0),
  138. Vector3(2,-3,5),
  139. Vector3(6,-3,1),
  140. 0.5f,
  141. false
  142. );
  143. OBBoxTriTestClass Test2
  144. (
  145. Vector3(-3.5,-1.5,0), // sweeping a 3x3 box along pos x and neg y
  146. Vector3(1.5,1.5,1.5),
  147. Matrix3(1),
  148. Vector3(4,-4,0),
  149. Vector3(-4,-4,-1), // into a polygon in y-z plane
  150. Vector3(-2,-4,5),
  151. Vector3(0,-4,1),
  152. 0.25f, // should only move 25%
  153. false
  154. );
  155. OBBoxTriTestClass Test3
  156. (
  157. Vector3(-3.5,-1.5,0) + 0.25f * Vector3(4,-4,0),
  158. Vector3(1.5,1.5,1.5), // starting at end of test2's move, should be 0.0 but not StartBad!
  159. Matrix3(1),
  160. Vector3(4,-4,0),
  161. Vector3(-4,-4,-1), // into a polygon in y-z plane
  162. Vector3(-2,-4,5),
  163. Vector3(0,-4,1),
  164. 0.0f,
  165. false
  166. );
  167. OBBoxTriTestClass Test4
  168. (
  169. Vector3(-3.5f,-1.5f,0), // Same as test 2
  170. Vector3(1.5f,1.5f,1.5f),
  171. Matrix3(1),
  172. Vector3(4,-4,0),
  173. Vector3(-9,-4,-1), // into a polygon in y-z plane *but* just barely not in the way
  174. Vector3(-8,-4,5),
  175. Vector3(-4.9f,-4,0),
  176. 1.0f, // should move 100%
  177. false
  178. );
  179. OBBoxTriTestClass Test5
  180. (
  181. Vector3(-3.5,-1.5,0), // Same as test 3 with box brushing polygon vertex.
  182. Vector3(1.5,1.5,1.5),
  183. Matrix3(1),
  184. Vector3(4,-4,0),
  185. Vector3(-9,-4,-1), // into a polygon in y-z plane just touching path of box
  186. Vector3(-8,-4,5),
  187. Vector3(-4,-4,0),
  188. 1.0f, // should move 100%
  189. false
  190. );
  191. OBBoxTriTestClass Test6
  192. (
  193. Vector3(-3.5f,-1.5f,0), // Same as test 3 with box brushing polygon vertex.
  194. Vector3(1.5f,1.5f,1.5f),
  195. Matrix3(1),
  196. Vector3(4,-4,0),
  197. Vector3(-9,-4,-1), // into a polygon in y-z plane just barely hitting it
  198. Vector3(-8,-4,5),
  199. Vector3(-3.999f,-4,0), // (-4,-4,0) would just "touch" (see test5)
  200. 0.25f, // should move 25%
  201. false
  202. );
  203. OBBoxTriTestClass Test7
  204. (
  205. Vector3(0,0,0), // This is a case where the box starts out intersecting
  206. Vector3(5,5,5),
  207. Matrix3(1),
  208. Vector3(4,4,0),
  209. Vector3(1,4,-1),
  210. Vector3(2,4,5),
  211. Vector3(5,4,0),
  212. 0.0f,
  213. true
  214. );
  215. OBBoxTriTestClass Test8
  216. (
  217. Vector3(-2.5,2,0), // center
  218. Vector3(1.5,1,1), // extent
  219. Matrix3(1), // basis
  220. Vector3(3,0,0), // move
  221. Vector3(1,2,0), // v0
  222. Vector3(3,4,5), // v1
  223. Vector3(4,5,-1), // v2
  224. 0.66666667f,
  225. false
  226. );
  227. OBBoxTriTestClass Test9
  228. (
  229. Vector3(0,0,0), // Box with diagonal y-z length of 1, rotated 45 about z
  230. Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
  231. _RotateZ45,
  232. Vector3(4,0,0),
  233. Vector3(3,2,-1), // triangle blocking the move at x=3 (hitting back side)
  234. Vector3(3,0,1),
  235. Vector3(3,-2,-1),
  236. 0.5f, // hitting another box edge-to-face halfway through the move
  237. false
  238. );
  239. OBBoxTriTestClass * OBBoxTriTestCases[] =
  240. {
  241. &Test0,
  242. &Test1,
  243. &Test2,
  244. &Test3,
  245. &Test4,
  246. &Test5,
  247. &Test6,
  248. &Test7,
  249. &Test8,
  250. &Test9
  251. };
  252. /***********************************************************************************************
  253. * test_obb_tri -- performs some "hard-coded" obb-tri collision tests *
  254. * *
  255. * INPUT: *
  256. * *
  257. * OUTPUT: *
  258. * *
  259. * WARNINGS: *
  260. * *
  261. * HISTORY: *
  262. * 4/8/99 GTH : Created. *
  263. *=============================================================================================*/
  264. void test_obb_tri(void)
  265. {
  266. Print_Title("Testing OBBox->Triangle collision.");
  267. /*
  268. ** Test the sweep function with a bunch of boxes and triangles
  269. */
  270. CastResultStruct result;
  271. result.ComputeContactPoint = true;
  272. int numtests = sizeof(OBBoxTriTestCases)/sizeof(OBBoxTriTestClass *);
  273. unsigned cycles;
  274. unsigned totalcycles = 0;
  275. // prime the cache
  276. OBBoxTriTestClass * testcase = OBBoxTriTestCases[9];
  277. cycles = Get_CPU_Clock();
  278. CollisionMath::Collide( testcase->Box,
  279. testcase->BoxMove,
  280. testcase->Tri,
  281. Vector3(0,0,0),
  282. &result);
  283. cycles = Get_CPU_Clock() - cycles;
  284. // now time and test the routine
  285. for (int i=0; i<numtests; i++) {
  286. testcase = OBBoxTriTestCases[i];
  287. result.Fraction = 1.0;
  288. result.StartBad = false;
  289. result.Normal.Set(0,0,0);
  290. cycles = Get_CPU_Clock();
  291. CollisionMath::Collide( testcase->Box,
  292. testcase->BoxMove,
  293. testcase->Tri,
  294. Vector3(0,0,0),
  295. &result);
  296. cycles = Get_CPU_Clock() - cycles;
  297. totalcycles += cycles;
  298. if ((WWMath::Fabs(testcase->Fraction - result.Fraction) > WWMATH_EPSILON) ||
  299. (testcase->StartBad != result.StartBad))
  300. {
  301. printf("test: %3d fraction: %8.6f cycles: %12d \tfailed!\n",i,result.Fraction,cycles);
  302. } else {
  303. printf("test: %3d fraction: %8.6f cycles: %12d \tpassed...\n",i,result.Fraction,cycles);
  304. }
  305. }
  306. printf("average cycles: %d\n",totalcycles / numtests);
  307. printf("\n");
  308. /*
  309. ** Test a box moving down the z-axis to a polygon, then moving along
  310. ** the x-axis along the surface of the polygon.
  311. */
  312. OBBoxClass testbox;
  313. Vector3 move;
  314. Vector3 v0,v1,v2,n;
  315. TriClass testtri;
  316. v0.Set(0,1,0);
  317. v1.Set(-1,-1,0);
  318. v2.Set(1,1,0);
  319. testtri.V[0] = &v0;
  320. testtri.V[1] = &v1;
  321. testtri.V[2] = &v2;
  322. testtri.N = &n;
  323. testtri.Compute_Normal();
  324. testbox.Center.Set(0,0,2.363f);
  325. testbox.Extent.Set(1,1,1);
  326. move.Set(0,0,-5.0f);
  327. CastResultStruct cres;
  328. cres.StartBad = 0;
  329. cres.Fraction = 1.0f;
  330. CollisionMath::Collide(testbox,move,testtri,Vector3(0,0,0),&cres);
  331. printf("fraction = %f\n",cres.Fraction);
  332. testbox.Center += cres.Fraction * move;
  333. move.Set(1.0f,1.0f,0.0f);
  334. cres.StartBad = 0;
  335. cres.Fraction = 1.0f;
  336. CollisionMath::Collide(testbox,move,testtri,Vector3(0,0,0),&cres);
  337. printf("fraction = %f\n",cres.Fraction);
  338. testbox.Center += cres.Fraction * move;
  339. /*
  340. ** Try to get a timing of Oriented_Box_Intersects_Tri versus
  341. ** CollisionMath::Intersect
  342. */
  343. const int REPEAT = 50;
  344. cycles = Get_CPU_Clock();
  345. for (int j=0;j<REPEAT;j++) {
  346. testcase = OBBoxTriTestCases[7];
  347. CollisionMath::Intersection_Test(testcase->Box,testcase->Tri);
  348. }
  349. cycles = Get_CPU_Clock() - cycles;
  350. printf("CollisionMath::Intersect - average cycles: %d\n",cycles / REPEAT);
  351. printf("\n");
  352. cycles = Get_CPU_Clock();
  353. for (j=0;j<REPEAT;j++) {
  354. testcase = OBBoxTriTestCases[7];
  355. Oriented_Box_Intersects_Tri(testcase->Box,testcase->Tri);
  356. }
  357. cycles = Get_CPU_Clock() - cycles;
  358. printf("Oriented_Box_Intersects_Tri - average cycles: %d\n",cycles / REPEAT);
  359. printf("\n");
  360. }
  361. /***********************************************************************************************
  362. * brute_force_cast_obb_tri -- binary-search method of finding the collision time for obb-tri *
  363. * *
  364. * This function doesn't really work in the general case. Only when the endpoint of the move *
  365. * is guaranteed to be inside the triangle *
  366. * *
  367. * INPUT: *
  368. * *
  369. * OUTPUT: *
  370. * *
  371. * WARNINGS: *
  372. * *
  373. * HISTORY: *
  374. * 4/8/99 GTH : Created. *
  375. *=============================================================================================*/
  376. float brute_force_cast_obb_tri
  377. (
  378. const OBBoxClass & box,
  379. const Vector3 & move,
  380. const TriClass & tri
  381. )
  382. {
  383. float istart = 0.0f;
  384. float iend = 1.0f;
  385. while (iend - istart > WWMATH_EPSILON/2.0f) {
  386. float icenter = (iend + istart) / 2.0f;
  387. OBBoxClass testbox = box;
  388. testbox.Center = box.Center + icenter*move;
  389. if (Oriented_Box_Intersects_Tri(testbox,tri)) {
  390. iend = icenter;
  391. } else {
  392. istart = icenter;
  393. }
  394. }
  395. return (iend + istart) / 2.0f;
  396. }
  397. /***********************************************************************************************
  398. * brute_force_obb_tri_test -- collide random obb's into random tri's *
  399. * *
  400. * INPUT: *
  401. * *
  402. * OUTPUT: *
  403. * *
  404. * WARNINGS: *
  405. * *
  406. * HISTORY: *
  407. * 4/8/99 GTH : Created. *
  408. *=============================================================================================*/
  409. void brute_force_obb_tri_test(int test_count)
  410. {
  411. Print_Title("Brute Force Testing OBBox->Tri collision.");
  412. Vector3 v[3];
  413. Vector3 n;
  414. OBBoxClass box;
  415. TriClass tri;
  416. Vector3 move;
  417. tri.V[0] = &v[0];
  418. tri.V[1] = &v[1];
  419. tri.V[2] = &v[2];
  420. tri.N = &n;
  421. int fail_count = 0;
  422. int startbad_count = 0;
  423. int startbad_fail_count = 0;
  424. int bad_points = 0;
  425. float min_fraction = 1.0f;
  426. float max_fraction = 0.0f;
  427. float avg_fraction = 0.0f;
  428. float avg_fraction_error = 0.0f;
  429. int fraction_error_count = 0;
  430. float max_error = 0.0f;
  431. for (int i=0; i<test_count; i++) {
  432. // v0 is always in positive quadrant
  433. v[0].X = WWMath::Random_Float(0,5);
  434. v[0].Y = WWMath::Random_Float(0,5);
  435. v[0].Z = WWMath::Random_Float(0,5);
  436. // v1 is always +x,+y,-z
  437. v[1].X = WWMath::Random_Float(0,5);
  438. v[1].Y = WWMath::Random_Float(0,5);
  439. v[1].Z = -WWMath::Random_Float(0,5);
  440. // v2 is always -x,-y,-z
  441. v[2].X = -WWMath::Random_Float(0,5);
  442. v[2].Y = -WWMath::Random_Float(0,5);
  443. v[2].Z = -WWMath::Random_Float(0,5);
  444. tri.Compute_Normal();
  445. // make a random box
  446. box.Init_Random(0.25f,3.0f);
  447. // put it in a random position in a 20x20x20 cube
  448. box.Center.X = WWMath::Random_Float(-10.0f,10.0f);
  449. box.Center.Y = WWMath::Random_Float(-10.0f,10.0f);
  450. box.Center.Z = WWMath::Random_Float(-10.0f,10.0f);
  451. // make a move vector that will move the box's center to the centroid of the tri
  452. Vector3 new_center;
  453. new_center.X = (v[0].X + v[1].X + v[2].X) / 3.0f;
  454. new_center.Y = (v[0].Y + v[1].Y + v[2].Y) / 3.0f;
  455. new_center.Z = (v[0].Z + v[1].Z + v[2].Z) / 3.0f;
  456. move = new_center - box.Center;
  457. // sweep box into tri!
  458. CastResultStruct result;
  459. result.ComputeContactPoint = true;
  460. CollisionMath::Collide(box,move,tri,Vector3(0,0,0),&result);
  461. // if they started out intersecting, this test doesn't count
  462. if (result.StartBad) {
  463. startbad_count++;
  464. if (!Oriented_Box_Intersects_Tri(box,tri)) {
  465. startbad_fail_count++;
  466. printf("False startbad!");
  467. }
  468. }
  469. // if they were intersecting, never mind
  470. if (!result.StartBad) {
  471. printf(".");
  472. bool success = true;
  473. // add the fraction into the total so we get an idea
  474. // how far our tests are going (hopefully a good mix)
  475. avg_fraction += result.Fraction;
  476. if (result.Fraction < min_fraction) min_fraction = result.Fraction;
  477. if (result.Fraction > max_fraction) max_fraction = result.Fraction;
  478. // verify that the fraction is correct
  479. float realfrac = brute_force_cast_obb_tri(box,move,tri);
  480. if (fabs(realfrac - result.Fraction) > WWMATH_EPSILON) {
  481. success = false;
  482. float error = fabs(realfrac - result.Fraction);
  483. avg_fraction_error += error;
  484. fraction_error_count++;
  485. if (error > max_error) max_error = error;
  486. }
  487. // verify that they are not intersecting at the end of the move
  488. // if the allowed move is smaller than epsilon, we skip this and don't move
  489. if (result.Fraction > WWMATH_EPSILON) {
  490. OBBoxClass box2 = box;
  491. CastResultStruct second_result;
  492. box2.Center += /*0.9999f **/ result.Fraction * move;
  493. CollisionMath::Collide(box2,move,tri,Vector3(0,0,0),&second_result);
  494. if (second_result.StartBad) success = false;
  495. if ((result.Fraction < 1.0f) && (second_result.Fraction > 0.01f)) success = false;
  496. }
  497. // if something failed, do the test again to let the programmer step through...
  498. if (!success) {
  499. fail_count++;
  500. CastResultStruct redo_result;
  501. redo_result.ComputeContactPoint = true;
  502. CollisionMath::Collide(box,move,tri,Vector3(0,0,0),&redo_result);
  503. }
  504. }
  505. }
  506. printf("\n");
  507. int passes = test_count - (startbad_fail_count + fail_count);
  508. printf("Passed %d tests out of %d tests.\n",passes,test_count);
  509. printf("StartBad tests: %d failures: %d\n",startbad_count,startbad_fail_count);
  510. if (fraction_error_count > 0) {
  511. avg_fraction_error /= (float)fraction_error_count;
  512. }
  513. avg_fraction /= (float)(test_count - startbad_count);
  514. printf("Largest Fraction: %f\n",max_fraction);
  515. printf("Smallest Fraction: %f\n",min_fraction);
  516. printf("Average Fraction: %f\n",avg_fraction);
  517. printf("Average Error: %f\n",avg_fraction_error);
  518. printf("Biggest Error: %f\n",max_error);
  519. }
  520. /*********************************************************************
  521. **
  522. ** OBBoxTestClass
  523. ** Data for testing OBBox->OBBox collision detection
  524. **
  525. *********************************************************************/
  526. class OBBoxTestClass
  527. {
  528. public:
  529. OBBoxClass Box0;
  530. Vector3 Move0;
  531. OBBoxClass Box1;
  532. Vector3 Move1;
  533. float Fraction;
  534. bool StartBad;
  535. OBBoxTestClass
  536. (
  537. const Vector3 & c0, // center of box0
  538. const Vector3 & e0, // extent of box0
  539. const Matrix3 & b0, // basis of box0
  540. const Vector3 & m0, // move for box0
  541. const Vector3 & c1, // center of box1
  542. const Vector3 & e1, // extent of box1
  543. const Matrix3 & b1, // basis of box1
  544. const Vector3 & m1, // move for box1
  545. float frac, // expected fraction
  546. bool sol // expected start solid
  547. ) :
  548. Box0(c0,e0,b0),
  549. Move0(m0),
  550. Box1(c1,e1,b1),
  551. Move1(m1),
  552. Fraction(frac),
  553. StartBad(sol)
  554. {
  555. }
  556. };
  557. OBBoxTestClass BTest0
  558. (
  559. Vector3(0,0,0), // center
  560. Vector3(4,0,0), // extent
  561. Matrix3(1), // basis
  562. Vector3(4,0,0), // move
  563. Vector3(6,0,0), // center
  564. Vector3(1,1,1), // extent
  565. Matrix3(1), // basis
  566. Vector3(0,0,0), // move
  567. 0.25f,
  568. false
  569. );
  570. OBBoxTestClass BTest1
  571. (
  572. Vector3(-3.5f,-1.5f,0.0f),
  573. Vector3(1.5f,1.5f,1.5f),
  574. Matrix3(1),
  575. Vector3(4,-4,0),
  576. Vector3(-5.1,-5,0),
  577. Vector3(1,1,1),
  578. Matrix3(1),
  579. Vector3(0,0,0),
  580. 1.0f, // should just barely go by (touches)
  581. false
  582. );
  583. OBBoxTestClass BTest2
  584. (
  585. Vector3(3,0,0),
  586. Vector3(7,1,1),
  587. Matrix3(1),
  588. Vector3(4,-4,0),
  589. Vector3(9.5,0,0),
  590. Vector3(1,6,1),
  591. Matrix3(1),
  592. Vector3(0,0,0),
  593. 0.0f, // startbad
  594. true
  595. );
  596. OBBoxTestClass BTest3
  597. (
  598. Vector3(0,0,0), // Box with diagonal y-z length of 1, rotated 45 about z
  599. Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
  600. _RotateZ45,
  601. Vector3(4,0,0),
  602. Vector3(4,0,0), // axis-aligned box blocking the move along the x-axis
  603. Vector3(1,3,1),
  604. Matrix3(1),
  605. Vector3(0,0,0),
  606. 0.5f, // hitting another box edge-to-face halfway through the move
  607. false
  608. );
  609. OBBoxTestClass BTest4
  610. (
  611. Vector3(0,0,0), // Box with diagonal y-z length of 1, rotated 45 about z
  612. Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
  613. _RotateZ45,
  614. Vector3(0,4,0),
  615. Vector3(0,4,0), // axis-aligned box blocking the move along the x-axis
  616. Vector3(3,1,1),
  617. Matrix3(1),
  618. Vector3(0,0,0),
  619. 0.5f, // hitting another box edge-to-face halfway through the move
  620. false
  621. );
  622. OBBoxTestClass BTest5
  623. (
  624. Vector3(0,0,0), // Box with diagonal y-z length of 1, rotated 45 about z
  625. Vector3(WWMATH_SQRT2/2.0,WWMATH_SQRT2/2.0,1),
  626. _RotateZ45,
  627. Vector3(0,-4,0),
  628. Vector3(0,-4,0), // axis-aligned box blocking the move along the x-axis
  629. Vector3(3,1,1),
  630. Matrix3(1),
  631. Vector3(0,0,0),
  632. 0.5f, // hitting another box edge-to-face halfway through the move
  633. false
  634. );
  635. OBBoxTestClass * OBBoxTestCases[] =
  636. {
  637. &BTest0,
  638. &BTest1,
  639. &BTest2,
  640. &BTest3,
  641. &BTest4,
  642. &BTest5
  643. };
  644. /***********************************************************************************************
  645. * test_obb_obb -- performs some "hard-coded" obb-obb collision tests *
  646. * *
  647. * INPUT: *
  648. * *
  649. * OUTPUT: *
  650. * *
  651. * WARNINGS: *
  652. * *
  653. * HISTORY: *
  654. * 4/8/99 GTH : Created. *
  655. *=============================================================================================*/
  656. void test_obb_obb(void)
  657. {
  658. Print_Title("Testing OBBox->OBBox collision.");
  659. CastResultStruct result;
  660. result.ComputeContactPoint = true;
  661. int numtests = sizeof(OBBoxTestCases)/sizeof(OBBoxTestClass *);
  662. unsigned cycles;
  663. unsigned totalcycles = 0;
  664. // prime the cache
  665. OBBoxTestClass * testcase = OBBoxTestCases[4];
  666. cycles = Get_CPU_Clock();
  667. CollisionMath::Collide( testcase->Box0,
  668. testcase->Move0,
  669. testcase->Box1,
  670. testcase->Move1,
  671. &result);
  672. cycles = Get_CPU_Clock() - cycles;
  673. // now time and test the routine
  674. for (int i=0; i<numtests; i++) {
  675. testcase = OBBoxTestCases[i];
  676. result.Fraction = 1.0;
  677. result.StartBad = false;
  678. result.Normal.Set(0,0,0);
  679. cycles = Get_CPU_Clock();
  680. CollisionMath::Collide( testcase->Box0,
  681. testcase->Move0,
  682. testcase->Box1,
  683. testcase->Move1,
  684. &result);
  685. cycles = Get_CPU_Clock() - cycles;
  686. totalcycles += cycles;
  687. if ((WWMath::Fabs(testcase->Fraction - result.Fraction) > WWMATH_EPSILON) ||
  688. (testcase->StartBad != result.StartBad))
  689. {
  690. printf("test: %5d\tcycles: %12d \t\tfailed!\n",i,cycles);
  691. } else {
  692. printf("test: %5d\tcycles: %12d \t\tpassed...\n",i,cycles);
  693. }
  694. }
  695. printf("average cycles: %d\n",totalcycles / numtests);
  696. printf("\n");
  697. /*
  698. ** Try to get an accurate timing, run the same (startbad) test
  699. ** 100 times...
  700. */
  701. const int REPEAT=50;
  702. cycles = Get_CPU_Clock();
  703. for (int j=0;j<REPEAT;j++) {
  704. testcase = OBBoxTestCases[0];
  705. result.Fraction = 1.0;
  706. result.StartBad = false;
  707. result.Normal.Set(0,0,0);
  708. CollisionMath::Collide( testcase->Box0,
  709. testcase->Move0,
  710. testcase->Box1,
  711. testcase->Move1,
  712. &result);
  713. }
  714. cycles = Get_CPU_Clock() - cycles;
  715. printf("average cycles: %d\n",cycles / REPEAT);
  716. printf("\n");
  717. /*
  718. ** Now, comparing Oriented_Boxes_Intersect with CollisionMath::Intersect
  719. */
  720. cycles = Get_CPU_Clock();
  721. for (j=0;j<REPEAT;j++) {
  722. testcase = OBBoxTestCases[2];
  723. CollisionMath::Intersection_Test(testcase->Box0,testcase->Box1);
  724. }
  725. cycles = Get_CPU_Clock() - cycles;
  726. printf("CollisionMath::Intersect - average cycles: %d\n",cycles / REPEAT);
  727. printf("\n");
  728. cycles = Get_CPU_Clock();
  729. for (j=0;j<REPEAT;j++) {
  730. testcase = OBBoxTestCases[2];
  731. Oriented_Boxes_Intersect(testcase->Box0,testcase->Box1);
  732. }
  733. cycles = Get_CPU_Clock() - cycles;
  734. printf("Oriented_Boxes_Intersect - average cycles: %d\n",cycles / REPEAT);
  735. printf("\n");
  736. }
  737. /***********************************************************************************************
  738. * brute_force_cast_obb_obb -- brute force function to verify collision of two obb's *
  739. * *
  740. * This function doesn't really work in the general case. Only when the endpoint of the move *
  741. * is guaranteed to be inside the other box... *
  742. * *
  743. * *
  744. * INPUT: *
  745. * *
  746. * OUTPUT: *
  747. * *
  748. * WARNINGS: *
  749. * *
  750. * HISTORY: *
  751. * 4/8/99 GTH : Created. *
  752. *=============================================================================================*/
  753. float brute_force_cast_obb_obb
  754. (
  755. const OBBoxClass & box0,
  756. const Vector3 & move0,
  757. const OBBoxClass & box1
  758. )
  759. {
  760. float istart = 0.0f;
  761. float iend = 1.0f;
  762. while (iend - istart > WWMATH_EPSILON) {
  763. float icenter = (iend + istart) / 2.0f;
  764. OBBoxClass testbox = box0;
  765. testbox.Center = box0.Center + icenter*move0;
  766. if (Oriented_Boxes_Intersect(testbox,box1)) {
  767. iend = icenter;
  768. } else {
  769. istart = icenter;
  770. }
  771. }
  772. return (iend + istart) / 2.0f;
  773. }
  774. /***********************************************************************************************
  775. * brute_force_obb_obb_test -- collide random obb's together *
  776. * *
  777. * INPUT: *
  778. * *
  779. * OUTPUT: *
  780. * *
  781. * WARNINGS: *
  782. * *
  783. * HISTORY: *
  784. * 4/8/99 GTH : Created. *
  785. *=============================================================================================*/
  786. void brute_force_obb_obb_test(int test_count)
  787. {
  788. Print_Title("Brute Force Testing OBBox->OBBox collision.");
  789. OBBoxClass box0;
  790. OBBoxClass box1;
  791. Vector3 move0;
  792. int fail_count = 0;
  793. int startbad_count = 0;
  794. int startbad_fail_count = 0;
  795. float avg_fraction = 0.0f;
  796. float avg_fraction_error = 0.0f;
  797. int fraction_error_count = 0;
  798. float max_error = 0.0f;
  799. float min_fraction = 1.0f;
  800. float max_fraction = 0.0f;
  801. for (int i=0; i<test_count; i++) {
  802. box0.Init_Random(0.25f,3.0f);
  803. box1.Init_Random(0.25f,3.0f);
  804. box0.Center.X = WWMath::Random_Float(-10.0f,10.0f);
  805. box0.Center.Y = WWMath::Random_Float(-10.0f,10.0f);
  806. box0.Center.Z = WWMath::Random_Float(-10.0f,10.0f);
  807. box1.Center.Set(0,0,0);
  808. Vector3 newcenter;
  809. newcenter.X = WWMath::Random_Float(-0.12f,0.12f); // new center must be inside other box
  810. newcenter.Y = WWMath::Random_Float(-0.12f,0.12f);
  811. newcenter.Z = WWMath::Random_Float(-0.12f,0.12f);
  812. move0 = newcenter - box0.Center;
  813. // sweep box0 into box1
  814. CastResultStruct result;
  815. result.ComputeContactPoint = true;
  816. CollisionMath::Collide(box0,move0,box1,Vector3(0,0,0),&result);
  817. // if they were intersecting, verify that
  818. if (result.StartBad) {
  819. startbad_count++;
  820. if (!Oriented_Boxes_Intersect(box0,box1)) {
  821. startbad_fail_count++;
  822. printf("False startbad!");
  823. }
  824. }
  825. // if they weren't intersecting, verify the allowed move
  826. if (!result.StartBad) {
  827. bool success = true;
  828. // add the fraction into the total so we get an idea
  829. // how far our tests are going (hopefully a good mix)
  830. avg_fraction += result.Fraction;
  831. if (result.Fraction < min_fraction) min_fraction = result.Fraction;
  832. if (result.Fraction > max_fraction) max_fraction = result.Fraction;
  833. // verify that the fraction is correct
  834. float realfrac = brute_force_cast_obb_obb(box0,move0,box1);
  835. if (fabs(realfrac - result.Fraction) > WWMATH_EPSILON) {
  836. success = false;
  837. float error = fabs(realfrac - result.Fraction);
  838. avg_fraction_error += error;
  839. fraction_error_count++;
  840. if (error > max_error) max_error = error;
  841. }
  842. // verify that they are not intersecting now
  843. // if the allowed move is smaller than epsilon, we skip this and don't move
  844. if (result.Fraction > WWMATH_EPSILON) {
  845. OBBoxClass box2 = box0;
  846. box2.Center += (1.0f - WWMATH_EPSILON) * result.Fraction * move0;
  847. CastResultStruct second_result;
  848. CollisionMath::Collide(box2,move0,box1,Vector3(0,0,0),&second_result);
  849. if (second_result.StartBad) {
  850. success = false;
  851. #ifdef MANUAL_DEBUGGING
  852. _asm int 0x03;
  853. while (!success) {
  854. CastResultStruct move_result;
  855. CollisionMath::Collide(box0,move0,box1,Vector3(0,0,0),&move_result);
  856. second_result.Reset();
  857. CollisionMath::Collide(box2,move0,box1,Vector3(0,0,0),&second_result);
  858. }
  859. #endif
  860. }
  861. if ((result.Fraction < 1.0f) && (second_result.Fraction > 0.01f)) success = false;
  862. }
  863. // if something failed, do the test again to let the programmer step through...
  864. if (!success) {
  865. #ifdef MANUAL_DEBUGGING
  866. _asm int 0x03;
  867. while (!success) {
  868. fail_count++;
  869. CastResultStruct redo_result;
  870. redo_result.ComputePoint = true;
  871. CollisionMath::Collide(box0,move0,box1,Vector3(0,0,0),&redo_result);
  872. }
  873. #endif
  874. printf("x");
  875. } else {
  876. printf(".");
  877. }
  878. }
  879. }
  880. printf("\n");
  881. int passes = test_count - (startbad_fail_count + fail_count);
  882. printf("Passed %d tests out of %d tests.\n",passes,test_count);
  883. printf("StartBad tests: %d failures: %d\n",startbad_count,startbad_fail_count);
  884. if (fraction_error_count) {
  885. avg_fraction_error /= (float)fraction_error_count;
  886. }
  887. avg_fraction /= (float)(test_count - startbad_count);
  888. printf("Largest Fraction: %f\n",max_fraction);
  889. printf("Smallest Fraction: %f\n",min_fraction);
  890. printf("Average Fraction: %f\n",avg_fraction);
  891. printf("Average Error: %f\n",avg_fraction_error);
  892. printf("Biggest Error: %f\n",max_error);
  893. }
  894. /***********************************************************************************************
  895. * Test_OBBoxes -- run all of the obb-obb and obb-tri tests *
  896. * *
  897. * INPUT: *
  898. * *
  899. * OUTPUT: *
  900. * *
  901. * WARNINGS: *
  902. * *
  903. * HISTORY: *
  904. * 4/8/99 GTH : Created. *
  905. *=============================================================================================*/
  906. void Test_OBBoxes(void)
  907. {
  908. const int TESTCOUNT = 100;
  909. srand(time(NULL));
  910. test_obb_tri();
  911. test_obb_obb();
  912. brute_force_obb_tri_test(TESTCOUNT);
  913. brute_force_obb_obb_test(TESTCOUNT);
  914. }