raytest.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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 : MathTest *
  23. * *
  24. * $Archive:: /Commando/Code/Tests/mathtest/raytest.cpp $*
  25. * *
  26. * $Author:: Greg_h $*
  27. * *
  28. * $Modtime:: 3/17/00 9:34a $*
  29. * *
  30. * $Revision:: 10 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "raytest.h"
  36. #include "output.h"
  37. #include "wwmath.h"
  38. #include "tri.h"
  39. #include "vector3.h"
  40. #include "lineseg.h"
  41. #include "aabox.h"
  42. #include "obbox.h"
  43. #include "colmath.h"
  44. #include "p_timer.h"
  45. #include <stdio.h>
  46. void line_box_test(const LineSegClass & line,const OBBoxClass & box,CastResultStruct * res);
  47. /************************************************************************************************
  48. **
  49. ** Ray - Triangle Test Cases
  50. **
  51. ************************************************************************************************/
  52. class RayTriTestClass
  53. {
  54. public:
  55. Vector3 P0;
  56. Vector3 P1;
  57. Vector3 V0;
  58. Vector3 V1;
  59. Vector3 V2;
  60. Vector3 N;
  61. float Fraction;
  62. bool StartBad;
  63. LineSegClass LineSeg;
  64. TriClass Tri;
  65. RayTriTestClass
  66. (
  67. const Vector3 p0, // p0 of ray
  68. const Vector3 p1, // p1 of ray
  69. const Vector3 v0, // v0 of triangle
  70. const Vector3 v1, // v1 of triangle
  71. const Vector3 v2, // v2 of triangle
  72. float frac, // expected fraction
  73. bool sol // expected start solid
  74. ) :
  75. P0(p0),P1(p1),V0(v0),V1(v1),V2(v2),Fraction(frac),StartBad(sol),LineSeg(P0,P1)
  76. {
  77. Tri.V[0] = &V0;
  78. Tri.V[1] = &V1;
  79. Tri.V[2] = &V2;
  80. Tri.N = & N;
  81. Tri.Compute_Normal();
  82. }
  83. };
  84. RayTriTestClass RayTriTestCases[] =
  85. {
  86. RayTriTestClass
  87. (
  88. Vector3(0,0,0),
  89. Vector3(1,0,0),
  90. Vector3(1,1,1),
  91. Vector3(1,-1,1),
  92. Vector3(1,0,-1),
  93. 1.0,
  94. false
  95. ),
  96. RayTriTestClass // ray going down +x, hitting a triangle at x=0.75
  97. (
  98. Vector3(0,0,0),
  99. Vector3(1,0,0),
  100. Vector3(0.5,1,1),
  101. Vector3(1,-1,1),
  102. Vector3(0.75,0,-1),
  103. 0.75,
  104. false
  105. ),
  106. RayTriTestClass // ray going down the -z axis hitting an x-y triangle
  107. (
  108. Vector3(0,0,5), // ray start
  109. Vector3(0,0,-5), // ray end
  110. Vector3(0,1,0), // p0
  111. Vector3(-1,-1,0), // p1
  112. Vector3(1,-1,0), // p2
  113. 0.5,
  114. false
  115. ),
  116. RayTriTestClass // ray going down the -z axis, hitting back of an x-y triangle
  117. (
  118. Vector3(0,0,5), // ray start
  119. Vector3(0,0,-5), // ray end
  120. Vector3(0,1,0), // p0
  121. Vector3(1,-1,0), // p1
  122. Vector3(-1,-1,0), // p2
  123. 0.5,
  124. false
  125. ),
  126. RayTriTestClass // ray going down the +x axis, hitting a y-z triangle
  127. (
  128. Vector3(0,0,0), // ray start
  129. Vector3(5,0,0), // ray end
  130. Vector3(2,0,1), // p0
  131. Vector3(2,1,-1), // p1
  132. Vector3(2,-1,-1), // p2
  133. 2.0f / 5.0f,
  134. false
  135. ),
  136. RayTriTestClass // ray going down the -z axis, hitting vertex 2 of the triangle
  137. (
  138. Vector3(0,0,5), // ray start
  139. Vector3(0,0,-5), // ray end
  140. Vector3(0,1,0), // p0
  141. Vector3(0,0,0), // p1
  142. Vector3(1,0,0), // p2
  143. 0.5f,
  144. false
  145. ),
  146. RayTriTestClass // ray going down the -z axis, hitting center of edge between p0 and p2
  147. (
  148. Vector3(0,0,5), // ray start
  149. Vector3(0,0,-5), // ray end
  150. Vector3(-1,1,0), // p0
  151. Vector3(-1,-1,0), // p1
  152. Vector3(1,-1,0), // p2
  153. 0.5f,
  154. false
  155. ),
  156. };
  157. /************************************************************************************************
  158. **
  159. ** Ray - AABox Test Cases
  160. **
  161. ************************************************************************************************/
  162. class RayAABoxTestClass
  163. {
  164. public:
  165. Vector3 P0;
  166. Vector3 P1;
  167. AABoxClass Box;
  168. float Fraction;
  169. bool StartBad;
  170. LineSegClass LineSeg;
  171. TriClass Tri;
  172. RayAABoxTestClass
  173. (
  174. const Vector3 p0, // p0 of ray
  175. const Vector3 p1, // p1 of ray
  176. const Vector3 center, // center of the box
  177. const Vector3 extent, // extent of the box
  178. float frac, // expected fraction
  179. bool sol // expected start solid
  180. ) :
  181. P0(p0),P1(p1),Box(center,extent),Fraction(frac),StartBad(sol),LineSeg(P0,P1)
  182. {
  183. }
  184. };
  185. RayAABoxTestClass RayAABoxTestCases[] =
  186. {
  187. RayAABoxTestClass
  188. (
  189. Vector3(5,0,0), // p0 of ray
  190. Vector3(0,0,0), // p1 of ray
  191. Vector3(0,0,0), // center of the box
  192. Vector3(1,1,1), // extent of the box
  193. 4.0f / 5.0f, // expected fraction
  194. false
  195. ),
  196. RayAABoxTestClass
  197. (
  198. Vector3(-2,-5,0), // p0 of ray
  199. Vector3(0,0,0), // p1 of ray
  200. Vector3(0,0,0), // center of the box
  201. Vector3(2,2,2), // extent of the box
  202. 3.0f / 5.0f, // expected fraction
  203. false
  204. ),
  205. RayAABoxTestClass
  206. (
  207. Vector3(-2,5,0), // p0 of ray
  208. Vector3(0,0,0), // p1 of ray
  209. Vector3(0,0,0), // center of the box
  210. Vector3(10,1,1), // extent of the box
  211. 4.0f / 5.0f, // expected fraction
  212. false
  213. ),
  214. RayAABoxTestClass // ray just clips the corner of the box (in x-y plane)
  215. (
  216. Vector3(2,0,0), // p0 of ray
  217. Vector3(0,2,0), // p1 of ray
  218. Vector3(0,0,0), // center of the box
  219. Vector3(1,1,1), // extent of the box
  220. 0.5f, // expected fraction
  221. false
  222. ),
  223. RayAABoxTestClass // ray misses box
  224. (
  225. Vector3(1.01f,-3,0), // p0 of ray
  226. Vector3(1.01f,3,0), // p1 of ray
  227. Vector3(0,0,0), // center of the box
  228. Vector3(1,1,1), // extent of the box
  229. 1.0f, // expected fraction
  230. false
  231. ),
  232. };
  233. void Test_Rays(void)
  234. {
  235. CastResultStruct result;
  236. CastResultStruct result_check;
  237. volatile unsigned cycles;
  238. int numtests;
  239. int i;
  240. /*
  241. ** Test the Cast_Ray function with a bunch of ray-triangle pairs.
  242. */
  243. Print_Title("Testing ray-triangle intersection.");
  244. numtests = sizeof(RayTriTestCases)/sizeof(RayTriTestClass);
  245. for (i=0; i<numtests; i++) {
  246. RayTriTestClass * testcase = &(RayTriTestCases[i]);
  247. result.Fraction = 1.0;
  248. result.StartBad = false;
  249. result.Normal.Set(0,0,0);
  250. cycles = Get_CPU_Clock();
  251. CollisionMath::Collide(testcase->LineSeg,testcase->Tri,&result);
  252. cycles = Get_CPU_Clock() - cycles;
  253. printf("Ray -> Tri cycles: %d\n",cycles);
  254. CHECK(i,(fabs(testcase->Fraction - result.Fraction) < 0.001f));
  255. }
  256. /*
  257. ** Test the Cast_Ray function with a bunch of ray-aabox pairs.
  258. */
  259. Print_Title("Testing ray-aabox intersection.");
  260. numtests = sizeof(RayAABoxTestCases)/sizeof(RayAABoxTestClass);
  261. for (i=0; i<numtests; i++) {
  262. RayAABoxTestClass * testcase = &(RayAABoxTestCases[i]);
  263. result.Fraction = 1.0;
  264. result.StartBad = false;
  265. result.Normal.Set(0,0,0);
  266. cycles = Get_CPU_Clock();
  267. CollisionMath::Collide(testcase->LineSeg,testcase->Box,&result);
  268. cycles = Get_CPU_Clock() - cycles;
  269. printf("Ray -> AABox cycles: %d\n",cycles);
  270. CHECK(i,(fabs(testcase->Fraction - result.Fraction) < 0.001f));
  271. }
  272. /*
  273. ** Test the Cast_Ray function on some random oriented boxes
  274. */
  275. Print_Title("Testing ray-obbox intersection.");
  276. for (i=0; i<30; i++) {
  277. result.Fraction = 1.0;
  278. result.StartBad = false;
  279. result.Normal.Set(0,0,0);
  280. // create a random box
  281. OBBoxClass box;
  282. box.Init_Random(1.0f,2.75f);
  283. // create a random line
  284. LineSegClass line;
  285. line.Set_Random(Vector3(-3,-3,-3),Vector3(3,3,3));
  286. // use the ray-box test
  287. cycles = Get_CPU_Clock();
  288. CollisionMath::Collide(line,box,&result);
  289. cycles = Get_CPU_Clock() - cycles;
  290. // double-check the result
  291. result_check.Fraction = 1.0f;
  292. result_check.StartBad = false;
  293. result_check.Normal.Set(0,0,0);
  294. line_box_test(line,box,&result_check);
  295. // print what happened
  296. bool passed = false;
  297. if (fabs(result_check.Fraction - result.Fraction) < 0.001f) {
  298. passed = true;
  299. }
  300. printf("test %3d cycles: %6d\tfraction: %1.8f\t",i,cycles,result.Fraction);
  301. if (passed) {
  302. printf("passed\n");
  303. } else if (result.StartBad) {
  304. printf("(start inside)\n");
  305. } else {
  306. printf("<<failed!>>\n");
  307. }
  308. }
  309. /*
  310. ** Test the Overlap_Test function on some random AABoxes
  311. */
  312. int total_tests = 0;
  313. int total_cycles = 0;
  314. int outside_tests = 0;
  315. int outside_cycles = 0;
  316. int overlap_tests = 0;
  317. int overlap_cycles = 0;
  318. CollisionMath::OverlapType overlap;
  319. Print_Title("Testing ray-aabox overlap.");
  320. for (i=0; i<10000; i++) {
  321. // create a random box
  322. AABoxClass box;
  323. box.Init_Random(-1.0f,1.0f,1.0f,2.0f);
  324. // create a random line
  325. const float DIMENSION = 5.0f;
  326. LineSegClass line;
  327. line.Set_Random(Vector3(-DIMENSION,-DIMENSION,-DIMENSION),Vector3(DIMENSION,DIMENSION,DIMENSION));
  328. // use the overlap_test
  329. cycles = Get_CPU_Clock();
  330. overlap = CollisionMath::Overlap_Test(box,line);
  331. cycles = Get_CPU_Clock() - cycles;
  332. // print what happened
  333. printf("test %3d cycles: %6d\tfraction: %1.8f\t",i,cycles,result.Fraction);
  334. if (overlap == CollisionMath::OUTSIDE) {
  335. printf("outside\n");
  336. } else if (overlap == CollisionMath::INSIDE) {
  337. printf("inside\n");
  338. } else {
  339. printf("overlapped\n");
  340. }
  341. // stats
  342. total_tests++;
  343. total_cycles += cycles;
  344. if (overlap == CollisionMath::OUTSIDE) {
  345. outside_tests++;
  346. outside_cycles += cycles;
  347. } else {
  348. overlap_tests++;
  349. overlap_cycles += cycles;
  350. }
  351. }
  352. printf("Total Tests: %d\n",total_tests);
  353. printf("Average Cycles: %f\n",(float)total_cycles / (float)total_tests);
  354. printf("Outside Tests: %d\n",outside_tests);
  355. printf("Average Outside Cycles: %f\n",(float)outside_cycles / (float)outside_tests);
  356. printf("Overlap Tests: %d\n",overlap_tests);
  357. printf("Average Overlap Cycles: %f\n",(float)overlap_cycles / (float)overlap_tests);
  358. }
  359. void line_box_test(const LineSegClass & line,const OBBoxClass & box,CastResultStruct * res)
  360. {
  361. Vector3 point[8];
  362. point[0] = box.Center + box.Basis * Vector3( box.Extent.X, box.Extent.Y, box.Extent.Z);
  363. point[1] = box.Center + box.Basis * Vector3(-box.Extent.X, box.Extent.Y, box.Extent.Z);
  364. point[2] = box.Center + box.Basis * Vector3(-box.Extent.X,-box.Extent.Y, box.Extent.Z);
  365. point[3] = box.Center + box.Basis * Vector3( box.Extent.X,-box.Extent.Y, box.Extent.Z);
  366. point[4] = box.Center + box.Basis * Vector3( box.Extent.X, box.Extent.Y,-box.Extent.Z);
  367. point[5] = box.Center + box.Basis * Vector3(-box.Extent.X, box.Extent.Y,-box.Extent.Z);
  368. point[6] = box.Center + box.Basis * Vector3(-box.Extent.X,-box.Extent.Y,-box.Extent.Z);
  369. point[7] = box.Center + box.Basis * Vector3( box.Extent.X,-box.Extent.Y,-box.Extent.Z);
  370. static int triverts[12][3] =
  371. {
  372. { 0,1,2 },
  373. { 0,2,3 },
  374. { 4,5,1 },
  375. { 4,1,0 },
  376. { 1,5,6 },
  377. { 1,6,2 },
  378. { 3,2,6 },
  379. { 3,6,7 },
  380. { 4,0,3 },
  381. { 4,3,7 },
  382. { 4,7,6 },
  383. { 4,6,5 }
  384. };
  385. // first, check if ray starts inside box?
  386. // now, check for collision with each triangle
  387. for (int i=0; i<12; i++) {
  388. TriClass tri;
  389. Vector3 normal;
  390. tri.V[0] = &point[triverts[i][0]];
  391. tri.V[1] = &point[triverts[i][1]];
  392. tri.V[2] = &point[triverts[i][2]];
  393. tri.N = &normal;
  394. tri.Compute_Normal();
  395. CollisionMath::Collide(line,tri,res);
  396. }
  397. }