colmathobbtri.cpp 65 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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/wwmath/colmathobbtri.cpp $*
  25. * *
  26. * Author:: Greg Hjelstrom *
  27. * *
  28. * $Modtime:: 11/28/01 5:56p $*
  29. * *
  30. * $Revision:: 16 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * obbtri_collision_separation_test -- test the projected extents for separation *
  35. * obbtri_check_collision_axis -- project the obb and tri onto an arbitrary axis *
  36. * obbtri_check_collision_cross_axis -- intersection check for a "cross-product" axis *
  37. * obbtri_check_collision_basis_axis -- intersection check for a basis axis *
  38. * obbtri_check_collision_normal_axis -- intersection check for the triangle normal *
  39. * eval_side -- returns -1,0,+1 depending on the signe of val and side *
  40. * obbtri_compute_contact_normal -- computes the normal of the collision *
  41. * eval_A0_point -- contact point parameters for A0xEx *
  42. * eval_A1_point -- contact point parameters for A1xEx *
  43. * eval_A2_point -- contact point parameters for A2xEx *
  44. * obbtri_compute_contact_point -- compute the contact point *
  45. * CollisionMath::Collide -- collide an obbox into a triangle *
  46. * obbtri_intersection_separation_test -- test the projected extents for intersection *
  47. * obbtri_check_intersection_cross_axis -- intersection check for a "cross-product" axis *
  48. * obbtri_check_intersection_basis_axis -- intersection check for a basis axis *
  49. * obbtri_check_intersection_normal_axis -- intersection check for the triangle normal *
  50. * CollisionMath::Intersection_Test -- Intersection check for an OBBox and a triangle *
  51. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  52. #include "colmath.h"
  53. #include "obbox.h"
  54. #include "tri.h"
  55. #include "wwdebug.h"
  56. /*
  57. ** Separating Axes have to be rejected if their length is smaller than some epsilon.
  58. ** Otherwise, erroneous results can be reported.
  59. */
  60. #define AXISLEN_EPSILON2 WWMATH_EPSILON * WWMATH_EPSILON // squared length of a separating axis must be larger than this
  61. /*
  62. ** Axes used in Box-Tri intersection tests
  63. ** The axes of the box are A0,A1,A2. N is the normal of the triangle,
  64. ** E0,E1,E2 are direction vectors for the edges of the triangle.
  65. ** (the box axes are labeled A0,A1,A2 as a holdover from the obbox-obbox
  66. ** collision code which this was derived from where there are two boxes
  67. ** A and B)
  68. */
  69. enum
  70. {
  71. INTERSECTION = 0,
  72. AXIS_N, // normal of the triangle
  73. AXIS_A0, // first basis vector of the box
  74. AXIS_A1, // second basis vector of the box
  75. AXIS_A2, // third basis vector of the box
  76. AXIS_A0E0, // box0 x edge0...
  77. AXIS_A1E0,
  78. AXIS_A2E0,
  79. AXIS_A0E1,
  80. AXIS_A1E1,
  81. AXIS_A2E1,
  82. AXIS_A0E2,
  83. AXIS_A1E2,
  84. AXIS_A2E2
  85. };
  86. /******************************************************************************************
  87. OBBox->Triangle collision
  88. As with most of the collision detection functions, this code is based on the theorem
  89. that given any two non-intersecting convex polyhedra, a separating plane/axis
  90. can be found that will be defined by one of the face normals of one of the polyhedra
  91. or the cross product of an edge from each polyhedra. The key optimization for
  92. boxes is that many of these axes are the same. The normal to a face is the same
  93. as the axis for four of the edges of the box, etc.
  94. This test must be done using the tri normal, the three basis vectors
  95. for the box, and three vectors defined by the cross products of the basis
  96. and edge vectors. When testing the basis vectors, there will be two extents for
  97. the tri (the other two vectors projected onto the axis being tested).
  98. The appropriate extent must be used in the test (largest value which is pointing
  99. towards the direction of the box). In addition, I've found that I must test axes
  100. defined by the cross products of the move vector and the box axes. These tests
  101. catch "false collisions" where the box passes very close to the triangle but does
  102. not actually hit it.
  103. Each axis test will use the following logic:
  104. Project D onto the axis being used, it is the separation distance. If the
  105. projection of the extent of the box + the projection of the extent of the
  106. tri is greater than D*axis then the two intersect
  107. ******************************************************************************************/
  108. /**
  109. ** BoxTriColStruct
  110. ** Scratchpad variables for the OBBox-Triangle collision detection functions. One instance
  111. ** of this structure will be used for all of the local variables and its pointer will be
  112. ** handed of to various inline functions for the axis tests.
  113. ** Note that much of the code needs the un-normalized triangle normal. For this reason,
  114. ** I have to compute N rather than copying it from the triangle. (commenting this to
  115. ** avoid re-generating a difficult to find bug that I had)
  116. **
  117. ** Note that the MaxFrac variable starts negative so that we can accept slightly negative
  118. ** fractions (interpenetrations). But we clamp the returned result to 0.0 so that we never
  119. ** allow an object to get more embedded, just to possibly break itself free if it is within
  120. ** the epsilon. This is important because sometimes objects seem to intersect simply due to
  121. ** floating point roundoff error...
  122. */
  123. struct BTCollisionStruct
  124. {
  125. BTCollisionStruct(const OBBoxClass &box,const Vector3 &move,const TriClass &tri,const Vector3 &trimove) :
  126. Box(box),
  127. Tri(tri),
  128. BoxMove(move),
  129. TriMove(trimove)
  130. {
  131. Reset();
  132. }
  133. void Reset(void)
  134. {
  135. StartBad = true; // true until an axis clears it
  136. MaxFrac = -1.0f; // maximum move allowed so far (accept slightly negative but clamp to zero at end)
  137. AxisId = INTERSECTION; // axis that allowed the longest move
  138. Point = 0; // index of triangle point that was closest to the box
  139. Side = 0; // side of the interval
  140. Vector3::Subtract(*Tri.V[0],Box.Center,&D); // vector from center of box to vertex 0
  141. Vector3::Subtract(BoxMove,TriMove,&Move); // move vector relative to stationary triangle
  142. Vector3::Subtract(*Tri.V[1],*Tri.V[0],&E[0]);
  143. Vector3::Subtract(*Tri.V[2],*Tri.V[0],&E[1]);
  144. Vector3::Subtract(E[1],E[0],&E[2]);
  145. A[0].Set(Box.Basis[0][0],Box.Basis[1][0],Box.Basis[2][0]);
  146. A[1].Set(Box.Basis[0][1],Box.Basis[1][1],Box.Basis[2][1]);
  147. A[2].Set(Box.Basis[0][2],Box.Basis[1][2],Box.Basis[2][2]);
  148. Vector3::Cross_Product(E[0],E[1],&N);
  149. }
  150. bool StartBad; // Inital configuration is intersecting?
  151. float MaxFrac; // Longest move allowed so far
  152. int AxisId; // Last separating axis
  153. int Point; // Index of the "closest" triangle point (or one of them)
  154. int Side; // which side of the interval
  155. int TestSide; // Was the axis we're working on flipped
  156. int TestAxisId; // Axis 'id' we're working on
  157. int TestPoint; // Index of the closest vertex
  158. Vector3 TestAxis; // Axis we're working on
  159. Vector3 D; // Vector from the center of the box to v0
  160. Vector3 Move; // Move vector relative to stationary triangle
  161. float AE[3][3]; // Dot products of the Basis vectors and edges
  162. float AN[3]; // Dot products of the Basis vectors and the normal
  163. Vector3 AxE[3][3]; // Cross produts of the Basis vectors and edges
  164. Vector3 A[3]; // basis vectors for the box
  165. Vector3 E[3]; // edge vectors for the triangle
  166. Vector3 N; // normal (NOT normalized!!!)
  167. Vector3 FinalD; // Vector from center of box to v0 at end of move
  168. const OBBoxClass & Box;
  169. const TriClass & Tri;
  170. const Vector3 & BoxMove;
  171. const Vector3 & TriMove;
  172. private:
  173. // not implemented
  174. BTCollisionStruct(const BTCollisionStruct &);
  175. BTCollisionStruct & operator = (const BTCollisionStruct &);
  176. };
  177. /***********************************************************************************************
  178. * obbtri_collision_separation_test -- test the projected extents for separation *
  179. * *
  180. * Once the extents are projected onto the axis, this function contains *
  181. * the logic that determines the allowed fraction. *
  182. * *
  183. * INPUT: *
  184. * *
  185. * OUTPUT: *
  186. * true = objects are SEPARATED *
  187. * *
  188. * WARNINGS: *
  189. * *
  190. * HISTORY: *
  191. * 4/8/99 GTH : Created. *
  192. *=============================================================================================*/
  193. static inline bool obbtri_collision_separation_test
  194. (
  195. BTCollisionStruct & context,
  196. float lp,float leb0,float leb1
  197. )
  198. {
  199. /*
  200. ** - compute 'EPSILON' normalized to the length of the axis
  201. ** - If (I'm no more than 'EPSILON' embedded in the polygon)
  202. ** - not startbad
  203. ** - If (I'm moving towards)
  204. ** - fraction = How far I can move before embedding (can be negative if started embedded)
  205. ** - If (I can take entire move)
  206. ** - accept entire move
  207. ** - Else If (I can move more than I could have before; *negative always fails!)
  208. ** - update fraction
  209. ** - Else
  210. ** - Accept entire move since I'm not moving towards the polygon
  211. */
  212. float eps = 0.0f;
  213. if (lp - leb0 <= 0.0f) {
  214. eps = COLLISION_EPSILON * context.TestAxis.Length(); // trying to only compute epsilon if I have to
  215. }
  216. if (lp - leb0 > -eps) {
  217. context.StartBad = false;
  218. if (leb1 - leb0 > 0.0f) {
  219. float frac = (lp-leb0)/(leb1-leb0);
  220. if (frac >= 1.0f) {
  221. /* moving toward but not hitting triangle */
  222. context.AxisId = context.TestAxisId;
  223. context.MaxFrac = 1.0f;
  224. return true;
  225. } else {
  226. /* moving toward, hitting triangle */
  227. if (frac > context.MaxFrac) {
  228. context.MaxFrac = frac;
  229. context.AxisId = context.TestAxisId;
  230. context.Side = context.TestSide;
  231. context.Point = context.TestPoint;
  232. }
  233. }
  234. } else {
  235. /* moving away or not moving */
  236. context.AxisId = context.TestAxisId;
  237. context.MaxFrac = 1.0f;
  238. return true;
  239. }
  240. }
  241. return false;
  242. }
  243. /***********************************************************************************************
  244. * obbtri_check_collision_axis -- project the obb and tri onto an arbitrary axis *
  245. * *
  246. * projects the box and the triangle onto the given axis and calls *
  247. * obbtri_separation_test. return true if separation was detected *
  248. * *
  249. * INPUT: *
  250. * *
  251. * OUTPUT: *
  252. * true = objects are SEPARATED *
  253. * *
  254. * WARNINGS: *
  255. * *
  256. * HISTORY: *
  257. * 4/8/99 GTH : Created. *
  258. *=============================================================================================*/
  259. static inline bool obbtri_check_collision_axis(BTCollisionStruct & context)
  260. {
  261. float dist; // separation along the axis
  262. float axismove; // size of the move along the axis.
  263. float leb0; // initial coordinate of the leading edge of the box
  264. float leb1; // final coordinate of the leading edge of the box
  265. float lp; // leading edge of the polygon.
  266. float tmp; // temporary
  267. dist = Vector3::Dot_Product(context.D,context.TestAxis);
  268. axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
  269. // I want the axis centered at the box, pointing towards the triangle
  270. if (dist < 0) {
  271. dist = -dist;
  272. axismove = -axismove;
  273. context.TestAxis = -context.TestAxis;
  274. context.TestSide = -1.0f;
  275. } else {
  276. context.TestSide = 1.0f;
  277. }
  278. // compute coordinates of the leading edge of the box at t0 and t1
  279. leb0 = context.Box.Extent.X * WWMath::Fabs(Vector3::Dot_Product(context.TestAxis,context.A[0])) +
  280. context.Box.Extent.Y * WWMath::Fabs(Vector3::Dot_Product(context.TestAxis,context.A[1])) +
  281. context.Box.Extent.Z * WWMath::Fabs(Vector3::Dot_Product(context.TestAxis,context.A[2]));
  282. leb1 = leb0 + axismove;
  283. // compute coordinate of "leading edge of the triangle" relative to the box center.
  284. lp = 0;
  285. tmp = Vector3::Dot_Product(context.E[0],context.TestAxis); if (tmp < lp) lp = tmp;
  286. tmp = Vector3::Dot_Product(context.E[1],context.TestAxis); if (tmp < lp) lp = tmp;
  287. lp = dist + lp;
  288. return obbtri_collision_separation_test(context,lp,leb0,leb1);
  289. }
  290. /***********************************************************************************************
  291. * obbtri_check_collision_cross_axis -- projects obb and tri onto a "cross" axis *
  292. * *
  293. * Assumes that the axis given is one generated from a cross product of one of the edge and *
  294. * basis vectors. In this case, the box extent can be optimized and only two triangle verts *
  295. * need to be checked. *
  296. * *
  297. * INPUT: *
  298. * *
  299. * OUTPUT: *
  300. * true = objects are SEPARATED *
  301. * *
  302. * WARNINGS: *
  303. * *
  304. * HISTORY: *
  305. * 4/8/99 GTH : Created. *
  306. *=============================================================================================*/
  307. static inline bool obbtri_check_collision_cross_axis
  308. (
  309. BTCollisionStruct & context,
  310. float dp,
  311. int dpi,
  312. float leb0
  313. )
  314. {
  315. float p0; // distance from box center to vertex 0
  316. float axismove; // size of the move along the axis.
  317. float leb1; // final coordinate of the leading edge of the box
  318. float lp; // leading edge of the polygon.
  319. p0 = Vector3::Dot_Product(context.D,context.TestAxis);
  320. axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
  321. // I want the axis centered at the box, pointing towards the triangle
  322. if (p0 < 0) {
  323. p0 = -p0;
  324. axismove = -axismove;
  325. dp = -dp;
  326. context.TestAxis = -context.TestAxis;
  327. context.TestSide = -1.0f;
  328. } else {
  329. context.TestSide = 1.0f;
  330. }
  331. // compute coordinates of the leading edge of the box at t1
  332. leb1 = leb0 + axismove;
  333. // compute coordinate of "leading edge of the triangle" relative to the box center.
  334. lp = 0; context.TestPoint = 0;
  335. if (dp < 0) { lp = dp; context.TestPoint = dpi; }
  336. lp = p0 + lp;
  337. return obbtri_collision_separation_test(context,lp,leb0,leb1);
  338. }
  339. /***********************************************************************************************
  340. * obbtri_check_collision_basis_axis -- projects the obb and tri onto a basis axis *
  341. * *
  342. * Projects the box and triangle onto an axis that is assumed to be a basis *
  343. * vector from the box. In this case, we can skip a dot-product and use the *
  344. * corresponding extent of the box (which is passed in as leb0). *
  345. * *
  346. * INPUT: *
  347. * *
  348. * OUTPUT: *
  349. * true = objects are SEPARATED *
  350. * *
  351. * WARNINGS: *
  352. * *
  353. * HISTORY: *
  354. * 4/8/99 GTH : Created. *
  355. *=============================================================================================*/
  356. static inline bool obbtri_check_collision_basis_axis
  357. (
  358. BTCollisionStruct & context,
  359. float leb0,
  360. float dp1,
  361. float dp2
  362. )
  363. {
  364. float dist; // separation along the axis
  365. float axismove; // size of the move along the axis.
  366. float leb1; // final coordinate of the leading edge of the box
  367. float lp; // leading edge of the polygon.
  368. dist = Vector3::Dot_Product(context.D,context.TestAxis);
  369. axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
  370. // we want the axis centered at the box, pointing towards the triangle
  371. if (dist < 0) {
  372. dist = -dist;
  373. axismove = -axismove;
  374. dp1 = -dp1;
  375. dp2 = -dp2;
  376. context.TestAxis = -context.TestAxis;
  377. context.TestSide = -1.0f;
  378. } else {
  379. context.TestSide = 1.0f;
  380. }
  381. // this is the "optimization", leb0 = one of the extents
  382. leb1 = leb0 + axismove;
  383. // compute coordinate of "leading edge of the polygon" relative to the box center.
  384. lp = 0; context.TestPoint = 0;
  385. if (dp1 < lp) { lp = dp1; context.TestPoint = 1; }
  386. if (dp2 < lp) { lp = dp2; context.TestPoint = 2; }
  387. lp = dist + lp;
  388. return obbtri_collision_separation_test(context,lp,leb0,leb1);
  389. }
  390. /***********************************************************************************************
  391. * obbtri_check_collision_normal_axis -- project the box and tri onto the tri-normal *
  392. * *
  393. * Projects the box and triangle onto an axis that is assumed to be the normal *
  394. * vector from the triangle. In this case, the triangle extents are zero. *
  395. * *
  396. * INPUT: *
  397. * *
  398. * OUTPUT: *
  399. * true = objects are SEPARATED *
  400. * *
  401. * WARNINGS: *
  402. * *
  403. * HISTORY: *
  404. * 4/8/99 GTH : Created. *
  405. *=============================================================================================*/
  406. static inline bool obbtri_check_collision_normal_axis(BTCollisionStruct & context)
  407. {
  408. float dist; // separation along the axis
  409. float axismove; // size of the move along the axis.
  410. float leb0; // initial coordinate of the leading edge of the box
  411. float leb1; // final coordinate of the leading edge of the box
  412. float lp; // leading edge of the polygon.
  413. dist = Vector3::Dot_Product(context.D,context.TestAxis);
  414. axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
  415. // we want the axis centered at the box, pointing towards the triangle
  416. if (dist < 0) {
  417. dist = -dist;
  418. axismove = -axismove;
  419. context.TestAxis = -context.TestAxis;
  420. context.TestSide = -1.0f;
  421. } else {
  422. context.TestSide = 1.0f;
  423. }
  424. leb0 = context.Box.Extent.X * WWMath::Fabs(context.AN[0]) +
  425. context.Box.Extent.Y * WWMath::Fabs(context.AN[1]) +
  426. context.Box.Extent.Z * WWMath::Fabs(context.AN[2]);
  427. leb1 = leb0 + axismove;
  428. context.TestPoint = 0;
  429. lp = dist; // this is the "optimization", don't have to find lp
  430. return obbtri_collision_separation_test(context,lp,leb0,leb1);
  431. }
  432. /***********************************************************************************************
  433. * eval_side -- returns -1,0,+1 depending on the sign of val and side *
  434. * *
  435. * INPUT: *
  436. * *
  437. * OUTPUT: *
  438. * *
  439. * WARNINGS: *
  440. * *
  441. * HISTORY: *
  442. * 4/8/99 GTH : Created. *
  443. *=============================================================================================*/
  444. static inline float eval_side(float val,int side)
  445. {
  446. if (val > 0.0f) {
  447. return side;
  448. } else if (val < 0.0f) {
  449. return -side;
  450. } else {
  451. return 0.0f;
  452. }
  453. }
  454. /***********************************************************************************************
  455. * obbtri_compute_contact_normal -- computes the normal of the collision *
  456. * *
  457. * INPUT: *
  458. * *
  459. * OUTPUT: *
  460. * *
  461. * WARNINGS: *
  462. * *
  463. * HISTORY: *
  464. * 4/8/99 GTH : Created. *
  465. *=============================================================================================*/
  466. static inline void obbtri_compute_contact_normal
  467. (
  468. const BTCollisionStruct & context,
  469. Vector3 * set_normal
  470. )
  471. {
  472. switch(context.AxisId)
  473. {
  474. case INTERSECTION:
  475. // WWASSERT(0);
  476. break;
  477. case AXIS_N:
  478. *set_normal = -context.Side * *context.Tri.N;
  479. break;
  480. case AXIS_A0:
  481. *set_normal = -context.Side * context.A[0];
  482. break;
  483. case AXIS_A1:
  484. *set_normal = -context.Side * context.A[1];
  485. break;
  486. case AXIS_A2:
  487. *set_normal = -context.Side * context.A[2];
  488. break;
  489. case AXIS_A0E0:
  490. *set_normal = -context.Side * context.AxE[0][0];
  491. set_normal->Normalize();
  492. break;
  493. case AXIS_A1E0:
  494. *set_normal = -context.Side * context.AxE[1][0];
  495. set_normal->Normalize();
  496. break;
  497. case AXIS_A2E0:
  498. *set_normal = -context.Side * context.AxE[2][0];
  499. set_normal->Normalize();
  500. break;
  501. case AXIS_A0E1:
  502. *set_normal = -context.Side * context.AxE[0][1];
  503. set_normal->Normalize();
  504. break;
  505. case AXIS_A1E1:
  506. *set_normal = -context.Side * context.AxE[1][1];
  507. set_normal->Normalize();
  508. break;
  509. case AXIS_A2E1:
  510. *set_normal = -context.Side * context.AxE[2][1];
  511. set_normal->Normalize();
  512. break;
  513. case AXIS_A0E2:
  514. *set_normal = -context.Side * context.AxE[0][2];
  515. set_normal->Normalize();
  516. break;
  517. case AXIS_A1E2:
  518. *set_normal = -context.Side * context.AxE[1][2];
  519. set_normal->Normalize();
  520. break;
  521. case AXIS_A2E2:
  522. *set_normal = -context.Side * context.AxE[2][2];
  523. set_normal->Normalize();
  524. break;
  525. }
  526. WWASSERT(set_normal->Length2() > 0.0f);
  527. }
  528. /***********************************************************************************************
  529. * eval_A0_point -- contact point parameters for A0xEx *
  530. * *
  531. * INPUT: *
  532. * *
  533. * OUTPUT: *
  534. * *
  535. * WARNINGS: *
  536. * *
  537. * HISTORY: *
  538. * 4/8/99 GTH : Created. *
  539. *=============================================================================================*/
  540. static inline void eval_A0_point(const BTCollisionStruct & context,float * x,int edge)
  541. {
  542. float yval,den;
  543. Vector3 DxE;
  544. x[1] = -eval_side(context.AE[2][edge],context.Side) * context.Box.Extent[1];
  545. x[2] = eval_side(context.AE[1][edge],context.Side) * context.Box.Extent[2];
  546. if (context.Point == 0) { yval = 0.0f; } else { yval = 1.0f; }
  547. den = Vector3::Dot_Product(context.N,context.AxE[0][edge]);
  548. if (WWMath::Fabs(den) > 0.0f) {
  549. Vector3::Cross_Product(context.FinalD,context.E[edge],&DxE);
  550. x[0] = Vector3::Dot_Product(context.N,DxE);
  551. if (edge == 0) {
  552. x[0] -= context.N.Length2() * yval;
  553. } else {
  554. x[0] += context.N.Length2() * yval;
  555. }
  556. x[0] -= Vector3::Dot_Product(context.N,context.AxE[1][edge]) * x[1];
  557. x[0] -= Vector3::Dot_Product(context.N,context.AxE[2][edge]) * x[2];
  558. x[0] /= den;
  559. } else {
  560. x[0] = 0.0f;
  561. }
  562. }
  563. /***********************************************************************************************
  564. * eval_A1_point -- contact point parameters for A1xEx *
  565. * *
  566. * INPUT: *
  567. * *
  568. * OUTPUT: *
  569. * *
  570. * WARNINGS: *
  571. * *
  572. * HISTORY: *
  573. * 4/8/99 GTH : Created. *
  574. *=============================================================================================*/
  575. static inline void eval_A1_point(const BTCollisionStruct & context,float * x,int edge)
  576. {
  577. float yval,den;
  578. Vector3 DxE;
  579. x[0] = eval_side(context.AE[2][edge],context.Side) * context.Box.Extent[0];
  580. x[2] = -eval_side(context.AE[0][edge],context.Side) * context.Box.Extent[2];
  581. if (context.Point == 0) { yval = 0.0f; } else { yval = 1.0f; }
  582. den = Vector3::Dot_Product(context.N,context.AxE[1][edge]);
  583. if (WWMath::Fabs(den) > 0.0f) {
  584. Vector3::Cross_Product(context.FinalD,context.E[edge],&DxE);
  585. x[1] = Vector3::Dot_Product(context.N,DxE);
  586. if (edge == 0) {
  587. x[1] -= context.N.Length2() * yval;
  588. } else {
  589. x[1] += context.N.Length2() * yval;
  590. }
  591. x[1] -= Vector3::Dot_Product(context.N,context.AxE[0][edge]) * x[0];
  592. x[1] -= Vector3::Dot_Product(context.N,context.AxE[2][edge]) * x[2];
  593. x[1] /= den;
  594. } else {
  595. x[1] = 0.0f;
  596. }
  597. }
  598. /***********************************************************************************************
  599. * eval_A2_point -- contact point parameters for A2xEx *
  600. * *
  601. * INPUT: *
  602. * *
  603. * OUTPUT: *
  604. * *
  605. * WARNINGS: *
  606. * *
  607. * HISTORY: *
  608. * 4/8/99 GTH : Created. *
  609. *=============================================================================================*/
  610. static inline void eval_A2_point(const BTCollisionStruct & context,float * x,int edge)
  611. {
  612. float yval,den;
  613. Vector3 DxE;
  614. x[0] = -eval_side(context.AE[1][edge],context.Side) * context.Box.Extent[0];
  615. x[1] = eval_side(context.AE[0][edge],context.Side) * context.Box.Extent[1];
  616. if (context.Point == 0) { yval = 0.0f; } else { yval = 1.0f; }
  617. den = Vector3::Dot_Product(context.N,context.AxE[2][edge]);
  618. if (WWMath::Fabs(den) > 0.0f) {
  619. Vector3::Cross_Product(context.FinalD,context.E[edge],&DxE);
  620. x[2] = Vector3::Dot_Product(context.N,DxE);
  621. if (edge == 0) {
  622. x[2] -= context.N.Length2() * yval;
  623. } else {
  624. x[2] += context.N.Length2() * yval;
  625. }
  626. x[2] -= Vector3::Dot_Product(context.N,context.AxE[0][edge]) * x[0];
  627. x[2] -= Vector3::Dot_Product(context.N,context.AxE[1][edge]) * x[1];
  628. x[2] /= den;
  629. } else {
  630. x[2] = 0.0f;
  631. }
  632. }
  633. /***********************************************************************************************
  634. * obbtri_compute_contact_point -- compute the contact point *
  635. * *
  636. * INPUT: *
  637. * *
  638. * OUTPUT: *
  639. * *
  640. * WARNINGS: *
  641. * *
  642. * HISTORY: *
  643. * 4/8/99 GTH : Created. *
  644. *=============================================================================================*/
  645. static inline void obbtri_compute_contact_point
  646. (
  647. BTCollisionStruct & context,
  648. CastResultStruct * result
  649. )
  650. {
  651. int i;
  652. float x[3];
  653. if (context.AxisId >= AXIS_A0E0) {
  654. Vector3 newc = context.Box.Center + result->Fraction * context.BoxMove;
  655. Vector3 newv0 = *context.Tri.V[0] + result->Fraction * context.TriMove;
  656. context.FinalD = newv0 - newc;
  657. }
  658. switch (context.AxisId)
  659. {
  660. case INTERSECTION:
  661. WWASSERT(0);
  662. return;
  663. case AXIS_N: // part of the box is touching the face of the triangle
  664. for (i = 0; i < 3; i++) {
  665. x[i] = eval_side(context.AN[i],context.Side) * context.Box.Extent[i];
  666. }
  667. break;
  668. case AXIS_A0: // part of the triangle is touching a face of the box
  669. case AXIS_A1:
  670. case AXIS_A2:
  671. if (context.Point == 0) {
  672. result->ContactPoint = *context.Tri.V[0];
  673. } else if (context.Point == 1) {
  674. result->ContactPoint = *context.Tri.V[1];
  675. } else {
  676. result->ContactPoint = *context.Tri.V[2];
  677. }
  678. Vector3::Add(result->ContactPoint,result->Fraction * context.TriMove,&(result->ContactPoint));
  679. return;
  680. case AXIS_A0E0: // one of the x-aligned edges of the box is contacting edge0
  681. eval_A0_point(context,x,0);
  682. break;
  683. case AXIS_A0E1: // one of the x-aligned edges of the box is contacting edge1
  684. eval_A0_point(context,x,1);
  685. break;
  686. case AXIS_A0E2: // one of the x-aligned edges of the box is contacting edge2
  687. eval_A0_point(context,x,2);
  688. break;
  689. case AXIS_A1E0: // one of the y-aligned edges of the box is contacting edge0
  690. eval_A1_point(context,x,0);
  691. break;
  692. case AXIS_A1E1: // one of the y-aligned edges of the box is contacting edge1
  693. eval_A1_point(context,x,1);
  694. break;
  695. case AXIS_A1E2: // one of the y-aligned edges of the box is contacting edge2
  696. eval_A1_point(context,x,2);
  697. break;
  698. case AXIS_A2E0: // one of the z-aligned edges of the box is contacting edge0
  699. eval_A2_point(context,x,0);
  700. break;
  701. case AXIS_A2E1: // one of the z-aligned edges of the box is contacting edge1
  702. eval_A2_point(context,x,1);
  703. break;
  704. case AXIS_A2E2: // one of the z-aligned edges of the box is contacting edge3
  705. eval_A2_point(context,x,2);
  706. break;
  707. }
  708. /*
  709. ** In certain circumstances, the x's computed above are outside the size of
  710. ** the box. I have tracked at least one of these cases to a situation where the
  711. ** separating axis is the triangle normal but one of the AxE axes also lines up
  712. ** with the normal. In this case, the Normal was 0,0,25 and the A0xE0 axis
  713. ** was 0,0,5. When the fraction was calculated for both of these, they were
  714. ** the same up to the six decimal places that MSVC will show me in the debugger.
  715. ** However, the fraction computed for the 0,0,5 axis was larger by some very small
  716. ** amount. This causes it to be used as the "separating axis". Looks like floating
  717. ** point roundoff error to me. The problem is that since the axis was perpendicular
  718. ** to the triangle, the "nearest-point" logic chose V0 which resulted in the
  719. ** calculation for x[0] being way off. The equations for finding the contact point
  720. ** in the AxE axis case assume that the axis is not normal to the triangle since
  721. ** that should be handled in the simpler routine for the N separating axis...
  722. ** Since I am at a loss for how to deal with this problem, I'm going to clamp all
  723. ** of the x's to be within the box here...
  724. */
  725. if (x[0] > context.Box.Extent.X) {
  726. x[0] = context.Box.Extent.X;
  727. } else if (x[0] < -context.Box.Extent.X) {
  728. x[0] = -context.Box.Extent.X;
  729. }
  730. if (x[1] > context.Box.Extent.Y) {
  731. x[1] = context.Box.Extent.Y;
  732. } else if (x[1] < -context.Box.Extent.Y) {
  733. x[1] = -context.Box.Extent.Y;
  734. }
  735. if (x[2] > context.Box.Extent.Z) {
  736. x[2] = context.Box.Extent.Z;
  737. } else if (x[2] < -context.Box.Extent.Z) {
  738. x[2] = -context.Box.Extent.Z;
  739. }
  740. /*
  741. ** Now, compute the point
  742. */
  743. result->ContactPoint.X = context.Box.Center.X +
  744. x[0]*context.A[0].X +
  745. x[1]*context.A[1].X +
  746. x[2]*context.A[2].X;
  747. result->ContactPoint.Y = context.Box.Center.Y +
  748. x[0]*context.A[0].Y +
  749. x[1]*context.A[1].Y +
  750. x[2]*context.A[2].Y;
  751. result->ContactPoint.Z = context.Box.Center.Z +
  752. x[0]*context.A[0].Z +
  753. x[1]*context.A[1].Z +
  754. x[2]*context.A[2].Z;
  755. Vector3::Add(result->ContactPoint,result->Fraction * context.BoxMove,&(result->ContactPoint));
  756. }
  757. /***********************************************************************************************
  758. * CollisionMath::Collide -- collide an obbox into a triangle *
  759. * *
  760. * INPUT: *
  761. * *
  762. * OUTPUT: *
  763. * *
  764. * WARNINGS: *
  765. * *
  766. * HISTORY: *
  767. * 4/8/99 GTH : Created. *
  768. *=============================================================================================*/
  769. bool CollisionMath::Collide
  770. (
  771. const OBBoxClass & box,
  772. const Vector3 & move,
  773. const TriClass & tri,
  774. const Vector3 & trimove,
  775. CastResultStruct * result
  776. )
  777. {
  778. TRACK_COLLISION_OBBOX_TRI;
  779. float dp,leb0;
  780. BTCollisionStruct context(box,move,tri,trimove);
  781. /*
  782. ** AXIS_N
  783. */
  784. context.TestAxis = context.N;
  785. context.TestAxisId = AXIS_N;
  786. context.AN[0] = Vector3::Dot_Product(context.A[0],context.N);
  787. context.AN[1] = Vector3::Dot_Product(context.A[1],context.N);
  788. context.AN[2] = Vector3::Dot_Product(context.A[2],context.N);
  789. if (obbtri_check_collision_normal_axis(context)) goto exit;
  790. /*
  791. ** AXIS_A0
  792. */
  793. context.TestAxis = context.A[0];
  794. context.TestAxisId = AXIS_A0;
  795. context.AE[0][0] = Vector3::Dot_Product(context.A[0],context.E[0]);
  796. context.AE[0][1] = Vector3::Dot_Product(context.A[0],context.E[1]);
  797. if (obbtri_check_collision_basis_axis(context,box.Extent.X,context.AE[0][0],context.AE[0][1])) goto exit;
  798. /*
  799. ** AXIS_A1
  800. */
  801. context.TestAxis = context.A[1];
  802. context.TestAxisId = AXIS_A1;
  803. context.AE[1][0] = Vector3::Dot_Product(context.A[1],context.E[0]);
  804. context.AE[1][1] = Vector3::Dot_Product(context.A[1],context.E[1]);
  805. if (obbtri_check_collision_basis_axis(context,box.Extent.Y,context.AE[1][0],context.AE[1][1])) goto exit;
  806. /*
  807. ** AXIS_A2
  808. */
  809. context.TestAxis = context.A[2];
  810. context.TestAxisId = AXIS_A2;
  811. context.AE[2][0] = Vector3::Dot_Product(context.A[2],context.E[0]);
  812. context.AE[2][1] = Vector3::Dot_Product(context.A[2],context.E[1]);
  813. if (obbtri_check_collision_basis_axis(context,box.Extent.Z,context.AE[2][0],context.AE[2][1])) goto exit;
  814. /*
  815. ** AXIS_A0xE0
  816. */
  817. Vector3::Cross_Product(context.A[0],context.E[0],&context.AxE[0][0]);
  818. context.TestAxis = context.AxE[0][0];
  819. context.TestAxisId = AXIS_A0E0;
  820. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  821. dp = context.AN[0];
  822. leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[1][0]);
  823. if (obbtri_check_collision_cross_axis(context,dp,2,leb0)) goto exit;
  824. }
  825. /*
  826. ** AXIS_A0xE1
  827. */
  828. Vector3::Cross_Product(context.A[0],context.E[1],&context.AxE[0][1]);
  829. context.TestAxis = context.AxE[0][1];
  830. context.TestAxisId = AXIS_A0E1;
  831. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  832. dp = -context.AN[0];
  833. leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[1][1]);
  834. if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
  835. }
  836. /*
  837. ** AXIS_A0xE2
  838. */
  839. Vector3::Cross_Product(context.A[0],context.E[2],&context.AxE[0][2]);
  840. context.TestAxis = context.AxE[0][2];
  841. context.TestAxisId = AXIS_A0E2;
  842. context.AE[1][2] = Vector3::Dot_Product(context.A[1],context.E[2]);
  843. context.AE[2][2] = Vector3::Dot_Product(context.A[2],context.E[2]);
  844. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  845. dp = -context.AN[0];
  846. leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[1][2]);
  847. if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
  848. }
  849. /*
  850. ** AXIS_A1xE0
  851. */
  852. Vector3::Cross_Product(context.A[1],context.E[0],&context.AxE[1][0]);
  853. context.TestAxis = context.AxE[1][0];
  854. context.TestAxisId = AXIS_A1E0;
  855. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  856. dp = context.AN[1];
  857. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[0][0]);
  858. if (obbtri_check_collision_cross_axis(context,dp,2,leb0)) goto exit;
  859. }
  860. /*
  861. ** AXIS_A1xE1
  862. */
  863. Vector3::Cross_Product(context.A[1],context.E[1],&context.AxE[1][1]);
  864. context.TestAxis = context.AxE[1][1];
  865. context.TestAxisId = AXIS_A1E1;
  866. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  867. dp = -context.AN[1];
  868. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[0][1]);
  869. if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
  870. }
  871. /*
  872. ** AXIS_A1xE2
  873. */
  874. Vector3::Cross_Product(context.A[1],context.E[2],&context.AxE[1][2]);
  875. context.TestAxis = context.AxE[1][2];
  876. context.TestAxisId = AXIS_A1E2;
  877. context.AE[0][2] = Vector3::Dot_Product(context.A[0],context.E[2]);
  878. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  879. dp = -context.AN[1];
  880. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[0][2]);
  881. if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
  882. }
  883. /*
  884. ** AXIS_A2xE0
  885. */
  886. Vector3::Cross_Product(context.A[2],context.E[0],&context.AxE[2][0]);
  887. context.TestAxis = context.AxE[2][0];
  888. context.TestAxisId = AXIS_A2E0;
  889. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  890. dp = context.AN[2];
  891. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][0]) + box.Extent[1]*WWMath::Fabs(context.AE[0][0]);
  892. if (obbtri_check_collision_cross_axis(context,dp,2,leb0)) goto exit;
  893. }
  894. /*
  895. ** AXIS_A2xE1
  896. */
  897. Vector3::Cross_Product(context.A[2],context.E[1],&context.AxE[2][1]);
  898. context.TestAxis = context.AxE[2][1];
  899. context.TestAxisId = AXIS_A2E1;
  900. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  901. dp = -context.AN[2];
  902. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][1]) + box.Extent[1]*WWMath::Fabs(context.AE[0][1]);
  903. if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
  904. }
  905. /*
  906. ** AXIS_A2xE2
  907. */
  908. Vector3::Cross_Product(context.A[2],context.E[2],&context.AxE[2][2]);
  909. context.TestAxis = context.AxE[2][2];
  910. context.TestAxisId = AXIS_A2E2;
  911. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  912. dp = -context.AN[2];
  913. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][2]) + box.Extent[1]*WWMath::Fabs(context.AE[0][2]);
  914. if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
  915. }
  916. /*
  917. ** Last ditch effort, check an axis based on the move vector
  918. */
  919. if (!context.StartBad) {
  920. context.TestPoint = context.Point;
  921. context.TestAxisId = context.AxisId;
  922. Vector3::Cross_Product(context.Move,context.A[0],&context.TestAxis);
  923. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  924. if (obbtri_check_collision_axis(context)) goto exit;
  925. }
  926. Vector3::Cross_Product(context.Move,context.A[1],&context.TestAxis);
  927. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  928. if (obbtri_check_collision_axis(context)) goto exit;
  929. }
  930. Vector3::Cross_Product(context.Move,context.A[2],&context.TestAxis);
  931. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  932. if (obbtri_check_collision_axis(context)) goto exit;
  933. }
  934. }
  935. exit:
  936. #pragma message ("(gth) disabling an assert in obb->tri collision, investigate later\n")
  937. #if 0
  938. WWASSERT((context.AxisId != INTERSECTION) || (context.StartBad));
  939. #else
  940. if (context.AxisId == INTERSECTION) {
  941. context.StartBad = true;
  942. }
  943. #endif
  944. /*
  945. ** If the triangle and box are intersecting before the move, return that
  946. ** result.
  947. */
  948. if (context.StartBad) {
  949. result->StartBad = true;
  950. result->Fraction = 0.0f;
  951. result->Normal = *tri.N;
  952. TRACK_COLLISION_OBBOX_TRI_HIT;
  953. return true;
  954. }
  955. /*
  956. ** If the fraction allowed is basically equal to the fraction allowed by
  957. ** another polygon, try to pick the polygon which is least "edge-on" to the
  958. ** move.
  959. */
  960. if (context.MaxFrac < 0.0f) {
  961. context.MaxFrac = 0.0f;
  962. }
  963. if ((context.MaxFrac < 1.0f) && (context.MaxFrac <= result->Fraction)) {
  964. Vector3 normal;
  965. obbtri_compute_contact_normal(context,&normal);
  966. if ( (WWMath::Fabs(context.MaxFrac - result->Fraction) > WWMATH_EPSILON) ||
  967. (Vector3::Dot_Product(normal,move) < Vector3::Dot_Product(result->Normal,move)) )
  968. {
  969. result->Normal = normal; //obbtri_compute_contact_normal(context,result);
  970. }
  971. result->Fraction = context.MaxFrac;
  972. if (result->ComputeContactPoint) {
  973. obbtri_compute_contact_point(context,result);
  974. }
  975. TRACK_COLLISION_OBBOX_TRI_HIT;
  976. return true;
  977. }
  978. return false;
  979. }
  980. /***********************************************************************************************
  981. OBBox-Triangle Intersection
  982. The following code implements a simple boolean intersection check. It uses the same
  983. algorithm as the collision function but can avoid some of the calculations. For a very
  984. simple implementation of this algorithm, see Oriented_Box_Intersects_Tri in obbox.h
  985. ***********************************************************************************************/
  986. /**
  987. ** BTIntersectStruct
  988. ** Scratchpad variables for the OBBox-Triangle intersection functions. One instance
  989. ** of this structure will be used for all of the local variables and its pointer will be
  990. ** handed of to various inline functions for the axis tests.
  991. ** Note that much of the code needs the un-normalized triangle normal. For this reason,
  992. ** I have to compute N rather than copying it from the triangle. (commenting this to
  993. ** avoid re-generating a difficult to find bug that I had)
  994. */
  995. struct BTIntersectStruct
  996. {
  997. BTIntersectStruct(const OBBoxClass &box,const TriClass &tri) :
  998. Box(box),
  999. Tri(tri)
  1000. {
  1001. Vector3::Subtract(*tri.V[0],box.Center,&D); // vector from center of box to vertex 0
  1002. Vector3::Subtract(*tri.V[1],*tri.V[0],&E[0]);
  1003. Vector3::Subtract(*tri.V[2],*tri.V[0],&E[1]);
  1004. Vector3::Subtract(E[1],E[0],&E[2]);
  1005. A[0].Set(box.Basis[0][0],box.Basis[1][0],box.Basis[2][0]);
  1006. A[1].Set(box.Basis[0][1],box.Basis[1][1],box.Basis[2][1]);
  1007. A[2].Set(box.Basis[0][2],box.Basis[1][2],box.Basis[2][2]);
  1008. Vector3::Cross_Product(E[0],E[1],&N);
  1009. }
  1010. Vector3 D; // Vector from the center of the box to v0
  1011. float AE[3][3]; // Dot products of the Basis vectors and edges
  1012. float AN[3]; // Dot products of the Basis vectors and the normal
  1013. Vector3 AxE[3][3]; // Cross produts of the Basis vectors and edges
  1014. Vector3 A[3]; // basis vectors for the box
  1015. Vector3 E[3]; // edge vectors for the triangle
  1016. Vector3 N; // normal (NOT normalized!!!)
  1017. Vector3 TestAxis; // separating axis currently being tested
  1018. const OBBoxClass & Box;
  1019. const TriClass & Tri;
  1020. private:
  1021. // not implemented
  1022. BTIntersectStruct(const BTIntersectStruct &);
  1023. BTIntersectStruct & operator = (const BTIntersectStruct &);
  1024. };
  1025. /***********************************************************************************************
  1026. * obbtri_intersection_separation_test -- test the projected extents for intersection *
  1027. * *
  1028. * Once the extents are projected onto the axis, this function contains *
  1029. * the logic that determines whether the box and triangle intersect. *
  1030. * *
  1031. * INPUT: *
  1032. * context - the BTIntersectStruct containing the data for this intersection test *
  1033. * lp - the leading edge of the polygon projected onto the axis *
  1034. * leb0 - the leading edge of the box projected onto the axis *
  1035. * *
  1036. * OUTPUT: *
  1037. * true = objects are separated *
  1038. * *
  1039. * WARNINGS: *
  1040. * *
  1041. * HISTORY: *
  1042. * 2/3/2000 gth : Created. *
  1043. *=============================================================================================*/
  1044. static inline bool obbtri_intersection_separation_test
  1045. (
  1046. BTIntersectStruct & context,
  1047. float lp,
  1048. float leb0
  1049. )
  1050. {
  1051. /*
  1052. ** Only compute the normalized epsilon if we need to.
  1053. ** - compute 'EPSILON' normalized to the length of the axis
  1054. ** - If (I'm no more than 'EPSILON' embedded in the polygon) then the box and tri are separated
  1055. */
  1056. float eps = 0.0f;
  1057. if (lp - leb0 <= 0.0f) {
  1058. eps = COLLISION_EPSILON * context.TestAxis.Length(); // trying to only compute epsilon if I have to
  1059. }
  1060. return (lp - leb0 > -eps);
  1061. }
  1062. /***********************************************************************************************
  1063. * obbtri_check_intersection_cross_axis -- intersection check for a "cross-product" axis *
  1064. * *
  1065. * axis being checked is a cross product between a triangle edge and a box basis vector *
  1066. * *
  1067. * INPUT: *
  1068. * *
  1069. * OUTPUT: *
  1070. * *
  1071. * WARNINGS: *
  1072. * true = the objects are SEPARATED *
  1073. * *
  1074. * HISTORY: *
  1075. * 5/4/99 GTH : Created. *
  1076. *=============================================================================================*/
  1077. static inline bool obbtri_check_intersection_cross_axis
  1078. (
  1079. BTIntersectStruct & context,
  1080. float dp,
  1081. float leb0
  1082. )
  1083. {
  1084. float p0; // distance from box center to vertex 0
  1085. float lp; // leading edge of the polygon.
  1086. p0 = Vector3::Dot_Product(context.D,context.TestAxis);
  1087. // I want the axis centered at the box, pointing towards the triangle
  1088. if (p0 < 0) {
  1089. context.TestAxis = -context.TestAxis;
  1090. p0 = -p0;
  1091. dp = -dp;
  1092. }
  1093. // compute coordinate of "leading edge of the triangle" relative to the box center.
  1094. lp = 0;
  1095. if (dp < 0) { lp = dp; }
  1096. lp = p0 + lp;
  1097. return obbtri_intersection_separation_test(context,lp,leb0);
  1098. }
  1099. /***********************************************************************************************
  1100. * obbtri_check_intersection_basis_axis -- intersection check for a basis axis *
  1101. * *
  1102. * axis being checked is one of the basis vectors for the box *
  1103. * *
  1104. * INPUT: *
  1105. * *
  1106. * OUTPUT: *
  1107. * true = the objects are SEPARATED *
  1108. * *
  1109. * WARNINGS: *
  1110. * *
  1111. * HISTORY: *
  1112. * 5/4/99 GTH : Created. *
  1113. *=============================================================================================*/
  1114. static inline bool obbtri_check_intersection_basis_axis
  1115. (
  1116. BTIntersectStruct & context,
  1117. float leb0,
  1118. float dp1,
  1119. float dp2
  1120. )
  1121. {
  1122. float dist; // separation along the axis
  1123. float lp; // leading edge of the polygon.
  1124. dist = Vector3::Dot_Product(context.D,context.TestAxis);
  1125. // we want the axis centered at the box, pointing towards the triangle
  1126. if (dist < 0) {
  1127. context.TestAxis = -context.TestAxis;
  1128. dist = -dist;
  1129. dp1 = -dp1;
  1130. dp2 = -dp2;
  1131. }
  1132. // compute coordinate of "leading edge of the polygon" relative to the box center.
  1133. lp = 0;
  1134. if (dp1 < lp) { lp = dp1; }
  1135. if (dp2 < lp) { lp = dp2; }
  1136. lp = dist + lp;
  1137. return obbtri_intersection_separation_test(context,lp,leb0);
  1138. }
  1139. /***********************************************************************************************
  1140. * obbtri_check_intersection_normal_axis -- intersection check for the triangle normal *
  1141. * *
  1142. * axis being checked is the triangle's normal *
  1143. * *
  1144. * INPUT: *
  1145. * *
  1146. * OUTPUT: *
  1147. * true = the objects are SEPARATED *
  1148. * *
  1149. * WARNINGS: *
  1150. * *
  1151. * HISTORY: *
  1152. * 5/4/99 GTH : Created. *
  1153. *=============================================================================================*/
  1154. static inline bool obbtri_check_intersection_normal_axis
  1155. (
  1156. BTIntersectStruct & context
  1157. )
  1158. {
  1159. float dist; // separation along the axis
  1160. float leb0; // initial coordinate of the leading edge of the box
  1161. float lp; // leading edge of the polygon.
  1162. dist = Vector3::Dot_Product(context.D,context.TestAxis);
  1163. // we want the axis centered at the box, pointing towards the triangle
  1164. if (dist < 0) {
  1165. context.TestAxis = -context.TestAxis;
  1166. dist = -dist;
  1167. }
  1168. leb0 = context.Box.Extent.X * WWMath::Fabs(context.AN[0]) +
  1169. context.Box.Extent.Y * WWMath::Fabs(context.AN[1]) +
  1170. context.Box.Extent.Z * WWMath::Fabs(context.AN[2]);
  1171. lp = dist; // this is the "optimization", don't have to find lp
  1172. return obbtri_intersection_separation_test(context,lp,leb0);
  1173. }
  1174. /***********************************************************************************************
  1175. * CollisionMath::Intersection_Test -- Intersection check for an OBBox and a triangle *
  1176. * *
  1177. * INPUT: *
  1178. * box - obbox to be tested *
  1179. * tri - triangle to be tested *
  1180. * *
  1181. * OUTPUT: *
  1182. * true = objects INTERSECT *
  1183. * *
  1184. * WARNINGS: *
  1185. * note that the other inline functions are "quick-reject" functions which return true when *
  1186. * the objects are separated. *
  1187. * *
  1188. * HISTORY: *
  1189. * 5/4/99 GTH : Created. *
  1190. *=============================================================================================*/
  1191. bool CollisionMath::Intersection_Test(const OBBoxClass & box,const TriClass & tri)
  1192. {
  1193. float dp,leb0;
  1194. BTIntersectStruct context(box,tri);
  1195. /*
  1196. ** AXIS_N
  1197. */
  1198. context.TestAxis = context.N;
  1199. context.AN[0] = Vector3::Dot_Product(context.A[0],context.N);
  1200. context.AN[1] = Vector3::Dot_Product(context.A[1],context.N);
  1201. context.AN[2] = Vector3::Dot_Product(context.A[2],context.N);
  1202. if (obbtri_check_intersection_normal_axis(context)) return false;
  1203. /*
  1204. ** AXIS_A0
  1205. */
  1206. context.TestAxis = context.A[0];
  1207. context.AE[0][0] = Vector3::Dot_Product(context.A[0],context.E[0]);
  1208. context.AE[0][1] = Vector3::Dot_Product(context.A[0],context.E[1]);
  1209. if (obbtri_check_intersection_basis_axis(context,box.Extent.X,context.AE[0][0],context.AE[0][1])) return false;
  1210. /*
  1211. ** AXIS_A1
  1212. */
  1213. context.TestAxis = context.A[1];
  1214. context.AE[1][0] = Vector3::Dot_Product(context.A[1],context.E[0]);
  1215. context.AE[1][1] = Vector3::Dot_Product(context.A[1],context.E[1]);
  1216. if (obbtri_check_intersection_basis_axis(context,box.Extent.Y,context.AE[1][0],context.AE[1][1])) return false;
  1217. /*
  1218. ** AXIS_A2
  1219. */
  1220. context.TestAxis = context.A[2];
  1221. context.AE[2][0] = Vector3::Dot_Product(context.A[2],context.E[0]);
  1222. context.AE[2][1] = Vector3::Dot_Product(context.A[2],context.E[1]);
  1223. if (obbtri_check_intersection_basis_axis(context,box.Extent.Z,context.AE[2][0],context.AE[2][1])) return false;
  1224. /*
  1225. ** AXIS_A0xE0
  1226. */
  1227. Vector3::Cross_Product(context.A[0],context.E[0],&context.AxE[0][0]);
  1228. context.TestAxis = context.AxE[0][0];
  1229. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1230. dp = context.AN[0];
  1231. leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[1][0]);
  1232. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1233. }
  1234. /*
  1235. ** AXIS_A0xE1
  1236. */
  1237. Vector3::Cross_Product(context.A[0],context.E[1],&context.AxE[0][1]);
  1238. context.TestAxis = context.AxE[0][1];
  1239. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1240. dp = -context.AN[0];
  1241. leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[1][1]);
  1242. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1243. }
  1244. /*
  1245. ** AXIS_A0xE2
  1246. */
  1247. Vector3::Cross_Product(context.A[0],context.E[2],&context.AxE[0][2]);
  1248. context.TestAxis = context.AxE[0][2];
  1249. context.AE[1][2] = Vector3::Dot_Product(context.A[1],context.E[2]);
  1250. context.AE[2][2] = Vector3::Dot_Product(context.A[2],context.E[2]);
  1251. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1252. dp = -context.AN[0];
  1253. leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[1][2]);
  1254. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1255. }
  1256. /*
  1257. ** AXIS_A1xE0
  1258. */
  1259. Vector3::Cross_Product(context.A[1],context.E[0],&context.AxE[1][0]);
  1260. context.TestAxis = context.AxE[1][0];
  1261. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1262. dp = context.AN[1];
  1263. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[0][0]);
  1264. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1265. }
  1266. /*
  1267. ** AXIS_A1xE1
  1268. */
  1269. Vector3::Cross_Product(context.A[1],context.E[1],&context.AxE[1][1]);
  1270. context.TestAxis = context.AxE[1][1];
  1271. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1272. dp = -context.AN[1];
  1273. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[0][1]);
  1274. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1275. }
  1276. /*
  1277. ** AXIS_A1xE2
  1278. */
  1279. Vector3::Cross_Product(context.A[1],context.E[2],&context.AxE[1][2]);
  1280. context.TestAxis = context.AxE[1][2];
  1281. context.AE[0][2] = Vector3::Dot_Product(context.A[0],context.E[2]);
  1282. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1283. dp = -context.AN[1];
  1284. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[0][2]);
  1285. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1286. }
  1287. /*
  1288. ** AXIS_A2xE0
  1289. */
  1290. Vector3::Cross_Product(context.A[2],context.E[0],&context.AxE[2][0]);
  1291. context.TestAxis = context.AxE[2][0];
  1292. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1293. dp = context.AN[2];
  1294. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][0]) + box.Extent[1]*WWMath::Fabs(context.AE[0][0]);
  1295. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1296. }
  1297. /*
  1298. ** AXIS_A2xE1
  1299. */
  1300. Vector3::Cross_Product(context.A[2],context.E[1],&context.AxE[2][1]);
  1301. context.TestAxis = context.AxE[2][1];
  1302. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1303. dp = -context.AN[2];
  1304. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][1]) + box.Extent[1]*WWMath::Fabs(context.AE[0][1]);
  1305. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1306. }
  1307. /*
  1308. ** AXIS_A2xE2
  1309. */
  1310. Vector3::Cross_Product(context.A[2],context.E[2],&context.AxE[2][2]);
  1311. context.TestAxis = context.AxE[2][2];
  1312. if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
  1313. dp = -context.AN[2];
  1314. leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][2]) + box.Extent[1]*WWMath::Fabs(context.AE[0][2]);
  1315. if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
  1316. }
  1317. return true;
  1318. }