TriangleTest.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. //---------------------------------------------------------------------------------------------------------------------
  2. // TriangleTest.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //---------------------------------------------------------------------------------------------------------------------
  7. using System;
  8. using Microsoft.Xna.Framework;
  9. namespace CollisionSample
  10. {
  11. /// <summary>
  12. /// Represents a simple triangle by the vertices at each corner.
  13. /// </summary>
  14. public struct Triangle
  15. {
  16. public Vector3 V0;
  17. public Vector3 V1;
  18. public Vector3 V2;
  19. public Triangle(Vector3 v0, Vector3 v1, Vector3 v2)
  20. {
  21. V0 = v0;
  22. V1 = v1;
  23. V2 = v2;
  24. }
  25. }
  26. /// <summary>
  27. /// Triangle-based collision tests
  28. /// </summary>
  29. public static class TriangleTest
  30. {
  31. const float EPSILON = 1e-20F;
  32. /// <summary>
  33. /// Returns true if the given box intersects the triangle (v0,v1,v2).
  34. /// </summary>
  35. public static bool Intersects(ref BoundingBox box, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  36. {
  37. Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
  38. Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
  39. // Transform the triangle into the local space with the box center at the origin
  40. Triangle localTri = new Triangle();
  41. Vector3.Subtract(ref v0, ref boxCenter, out localTri.V0);
  42. Vector3.Subtract(ref v1, ref boxCenter, out localTri.V1);
  43. Vector3.Subtract(ref v2, ref boxCenter, out localTri.V2);
  44. return OriginBoxContains(ref boxHalfExtent, ref localTri) != ContainmentType.Disjoint;
  45. }
  46. /// <summary>
  47. /// Tests whether the given box contains, intersects, or is disjoint from the triangle (v0,v1,v2).
  48. /// </summary>
  49. public static ContainmentType Contains(ref BoundingBox box, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  50. {
  51. Vector3 boxCenter = (box.Max + box.Min) * 0.5f;
  52. Vector3 boxHalfExtent = (box.Max - box.Min) * 0.5f;
  53. // Transform the triangle into the local space with the box center at the origin
  54. Triangle localTri;
  55. Vector3.Subtract(ref v0, ref boxCenter, out localTri.V0);
  56. Vector3.Subtract(ref v1, ref boxCenter, out localTri.V1);
  57. Vector3.Subtract(ref v2, ref boxCenter, out localTri.V2);
  58. return OriginBoxContains(ref boxHalfExtent, ref localTri);
  59. }
  60. /// <summary>
  61. /// Tests whether the given box contains, intersects, or is disjoint from the given triangle.
  62. /// </summary>
  63. public static ContainmentType Contains(ref BoundingBox box, ref Triangle triangle)
  64. {
  65. return Contains(ref box, ref triangle.V0, ref triangle.V1, ref triangle.V2);
  66. }
  67. /// <summary>
  68. /// Returns true if the given BoundingOrientedBox intersects the triangle (v0,v1,v2)
  69. /// </summary>
  70. public static bool Intersects(ref BoundingOrientedBox obox, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  71. {
  72. // Transform the triangle into the local space of the box, so we can use a
  73. // faster axis-aligned box test.
  74. // Note than when transforming more than one point, using an intermediate matrix
  75. // is faster than doing multiple quaternion transforms directly.
  76. Quaternion qinv;
  77. Quaternion.Conjugate(ref obox.Orientation, out qinv);
  78. Matrix minv = Matrix.CreateFromQuaternion(qinv);
  79. Triangle localTri = new Triangle();
  80. localTri.V0 = Vector3.TransformNormal(v0 - obox.Center, minv);
  81. localTri.V1 = Vector3.TransformNormal(v1 - obox.Center, minv);
  82. localTri.V2 = Vector3.TransformNormal(v2 - obox.Center, minv);
  83. return OriginBoxContains(ref obox.HalfExtent, ref localTri) != ContainmentType.Disjoint;
  84. }
  85. /// <summary>
  86. /// Determines whether the given BoundingOrientedBox contains/intersects/is disjoint from the triangle
  87. /// (v0,v1,v2)
  88. /// </summary>
  89. public static ContainmentType Contains(ref BoundingOrientedBox obox, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  90. {
  91. // Transform the triangle into the local space of the box, so we can use a
  92. // faster axis-aligned box test.
  93. // Note than when transforming more than one point, using an intermediate matrix
  94. // is faster than doing multiple quaternion transforms directly.
  95. Quaternion qinv;
  96. Quaternion.Conjugate(ref obox.Orientation, out qinv);
  97. Matrix minv;
  98. Matrix.CreateFromQuaternion(ref qinv, out minv);
  99. Triangle localTri = new Triangle();
  100. localTri.V0 = Vector3.TransformNormal(v0 - obox.Center, minv);
  101. localTri.V1 = Vector3.TransformNormal(v1 - obox.Center, minv);
  102. localTri.V2 = Vector3.TransformNormal(v2 - obox.Center, minv);
  103. return OriginBoxContains(ref obox.HalfExtent, ref localTri);
  104. }
  105. /// <summary>
  106. /// Determines whether the given BoundingOrientedBox contains/intersects/is disjoint from the
  107. /// given triangle.
  108. /// </summary>
  109. public static ContainmentType Contains(ref BoundingOrientedBox obox, ref Triangle triangle)
  110. {
  111. return Contains(ref obox, ref triangle.V0, ref triangle.V1, ref triangle.V2);
  112. }
  113. /// <summary>
  114. /// Returns true if the given sphere intersects the triangle (v0,v1,v2).
  115. /// </summary>
  116. public static bool Intersects(ref BoundingSphere sphere, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  117. {
  118. Vector3 p = NearestPointOnTriangle(ref sphere.Center, ref v0, ref v1, ref v2);
  119. return Vector3.DistanceSquared(sphere.Center, p) < sphere.Radius * sphere.Radius;
  120. }
  121. /// <summary>
  122. /// Returns true if the given sphere intersects the given triangle.
  123. /// </summary>
  124. public static bool Intersects(ref BoundingSphere sphere, ref Triangle t)
  125. {
  126. Vector3 p = NearestPointOnTriangle(ref sphere.Center, ref t.V0, ref t.V1, ref t.V2);
  127. return Vector3.DistanceSquared(sphere.Center, p) < sphere.Radius * sphere.Radius;
  128. }
  129. /// <summary>
  130. /// Determines whether the given sphere contains/intersects/is disjoint from the triangle
  131. /// (v0,v1,v2)
  132. /// </summary>
  133. public static ContainmentType Contains(ref BoundingSphere sphere, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  134. {
  135. float r2 = sphere.Radius * sphere.Radius;
  136. if (Vector3.DistanceSquared(v0, sphere.Center) <= r2 &&
  137. Vector3.DistanceSquared(v1, sphere.Center) <= r2 &&
  138. Vector3.DistanceSquared(v2, sphere.Center) <= r2)
  139. return ContainmentType.Contains;
  140. return Intersects(ref sphere, ref v0, ref v1, ref v2)
  141. ? ContainmentType.Intersects : ContainmentType.Disjoint;
  142. }
  143. /// <summary>
  144. /// Determines whether the given sphere contains/intersects/is disjoint from the
  145. /// given triangle.
  146. /// </summary>
  147. public static ContainmentType Contains(ref BoundingSphere sphere, ref Triangle triangle)
  148. {
  149. return Contains(ref sphere, ref triangle.V0, ref triangle.V1, ref triangle.V2);
  150. }
  151. /// <summary>
  152. /// Returns true if the given frustum intersects the triangle (v0,v1,v2).
  153. /// </summary>
  154. public static bool Intersects(BoundingFrustum frustum, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  155. {
  156. // A BoundingFrustum is defined by a matrix that projects the frustum shape
  157. // into the box from (-1,-1,0) to (1,1,1). We will project the triangle
  158. // through this matrix, and then do a simpler box-triangle test.
  159. Matrix m = frustum.Matrix;
  160. Triangle localTri;
  161. GeomUtil.PerspectiveTransform(ref v0, ref m, out localTri.V0);
  162. GeomUtil.PerspectiveTransform(ref v1, ref m, out localTri.V1);
  163. GeomUtil.PerspectiveTransform(ref v2, ref m, out localTri.V2);
  164. BoundingBox box;
  165. box.Min = new Vector3(-1, -1, 0);
  166. box.Max = new Vector3(1, 1, 1);
  167. return Intersects(ref box, ref localTri.V0, ref localTri.V1, ref localTri.V2);
  168. }
  169. /// <summary>
  170. /// Determines whether the given frustum contains/intersects/is disjoint from the triangle
  171. /// (v0,v1,v2)
  172. /// </summary>
  173. public static ContainmentType Contains(BoundingFrustum frustum, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  174. {
  175. // A BoundingFrustum is defined by a matrix that projects the frustum shape
  176. // into the box from (-1,-1,0) to (1,1,1). We will project the triangle
  177. // through this matrix, and then do a simpler box-triangle test.
  178. Matrix m = frustum.Matrix;
  179. Triangle localTri;
  180. GeomUtil.PerspectiveTransform(ref v0, ref m, out localTri.V0);
  181. GeomUtil.PerspectiveTransform(ref v1, ref m, out localTri.V1);
  182. GeomUtil.PerspectiveTransform(ref v2, ref m, out localTri.V2);
  183. // Center the projected box at the origin
  184. Vector3 halfExtent = new Vector3(1, 1, 0.5f);
  185. localTri.V0.Z -= 0.5f;
  186. localTri.V1.Z -= 0.5f;
  187. localTri.V2.Z -= 0.5f;
  188. return OriginBoxContains(ref halfExtent, ref localTri);
  189. }
  190. /// <summary>
  191. /// Determines whether the given frustum contains/intersects/is disjoint from the
  192. /// given triangle.
  193. /// </summary>
  194. public static ContainmentType Contains(BoundingFrustum frustum, ref Triangle triangle)
  195. {
  196. return Contains(frustum, ref triangle.V0, ref triangle.V1, ref triangle.V2);
  197. }
  198. /// <summary>
  199. /// Classify the triangle (v0,v1,v2) with respect to the given plane.
  200. /// </summary>
  201. public static PlaneIntersectionType Intersects(ref Plane plane, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  202. {
  203. float dV0 = plane.DotCoordinate(v0);
  204. float dV1 = plane.DotCoordinate(v1);
  205. float dV2 = plane.DotCoordinate(v2);
  206. if (Math.Min(dV0, Math.Min(dV1, dV2)) >= 0)
  207. {
  208. return PlaneIntersectionType.Front;
  209. }
  210. if (Math.Max(dV0, Math.Max(dV1, dV2)) <= 0)
  211. {
  212. return PlaneIntersectionType.Back;
  213. }
  214. return PlaneIntersectionType.Intersecting;
  215. }
  216. /// <summary>
  217. /// Determine whether the triangle (v0,v1,v2) intersects the given ray. If there is intersection,
  218. /// returns the parametric value of the intersection point on the ray. Otherwise returns null.
  219. /// </summary>
  220. public static float? Intersects(ref Ray ray, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  221. {
  222. // The algorithm is based on Moller, Tomas and Trumbore, "Fast, Minimum Storage
  223. // Ray-Triangle Intersection", Journal of Graphics Tools, vol. 2, no. 1,
  224. // pp 21-28, 1997.
  225. Vector3 e1 = v1 - v0;
  226. Vector3 e2 = v2 - v0;
  227. Vector3 p = Vector3.Cross(ray.Direction, e2);
  228. float det = Vector3.Dot(e1, p);
  229. float t;
  230. if (det >= EPSILON)
  231. {
  232. // Determinate is positive (front side of the triangle).
  233. Vector3 s = ray.Position - v0;
  234. float u = Vector3.Dot(s, p);
  235. if (u < 0 || u > det)
  236. return null;
  237. Vector3 q = Vector3.Cross(s, e1);
  238. float v = Vector3.Dot(ray.Direction, q);
  239. if (v < 0 || ((u + v) > det))
  240. return null;
  241. t = Vector3.Dot(e2, q);
  242. if (t < 0)
  243. return null;
  244. }
  245. else if (det <= -EPSILON)
  246. {
  247. // Determinate is negative (back side of the triangle).
  248. Vector3 s = ray.Position - v0;
  249. float u = Vector3.Dot(s, p);
  250. if (u > 0 || u < det)
  251. return null;
  252. Vector3 q = Vector3.Cross(s, e1);
  253. float v = Vector3.Dot(ray.Direction, q);
  254. if (v > 0 || ((u + v) < det))
  255. return null;
  256. t = Vector3.Dot(e2, q);
  257. if (t > 0)
  258. return null;
  259. }
  260. else
  261. {
  262. // Parallel ray.
  263. return null;
  264. }
  265. return t / det;
  266. }
  267. /// <summary>
  268. /// Determine whether the given triangle intersects the given ray. If there is intersection,
  269. /// returns the parametric value of the intersection point on the ray. Otherwise returns null.
  270. /// </summary>
  271. public static float? Intersects(ref Ray ray, ref Triangle tri)
  272. {
  273. return Intersects(ref ray, ref tri.V0, ref tri.V1, ref tri.V2);
  274. }
  275. /// <summary>
  276. /// Return the point on triangle (v0,v1,v2) closest to point p.
  277. /// </summary>
  278. public static Vector3 NearestPointOnTriangle(ref Vector3 p, ref Vector3 v0, ref Vector3 v1, ref Vector3 v2)
  279. {
  280. // We'll work in a space where v0 is the origin.
  281. // Let D=p-v0 be the local position of p, E1=v1-v0 and E2=v2-v0 be the
  282. // local positions of v1 and v2.
  283. //
  284. // Points on the triangle are defined by
  285. // P=v0 + s*E1 + t*E2
  286. // for s >= 0, t >= 0, s+t <= 1
  287. //
  288. // To compute (s,t) for p, note that s=the ratio of the components of d and e1 which
  289. // are perpendicular to e2 in the plane of the triangle.
  290. //
  291. // s = project(perp(D,E2),E1) / project(perp(E1,E2),E1)
  292. // where project(A,B) = B*(A . B)/(B . B)
  293. // perp(A,B) = A - project(A,B)
  294. //
  295. // expanding and rearranging terms a bit gives:
  296. //
  297. // (D . E1)*(E2 . E2) - (D . E2)*(E1 . E2)
  298. // s = ---------------------------------------
  299. // (E1 . E1)*(E2 . E2) - (E1 . E2)^2
  300. //
  301. // t = [same thing with E1/E2 swapped]
  302. //
  303. // Note that the denominator is the same for s and t, so we only need to compute it
  304. // once, and that the denominator is never negative. So we can compute the numerator
  305. // and denominator separately, and only do the division in case we actually need to
  306. // produce s and/or t.
  307. //
  308. // We also need the parametric projections of p onto each edge:
  309. // u1 onto E1, u2 onto E2, u12 onto the (v2-v1) edge.
  310. // u1 = (D . E1)/(E1 . E1)
  311. // u2 = (D . E2)/(E2 . E2)
  312. Vector3 D = p - v0;
  313. Vector3 E1 = (v1 - v0);
  314. Vector3 E2 = (v2 - v0);
  315. float dot11 = E1.LengthSquared();
  316. float dot12 = Vector3.Dot(E1, E2);
  317. float dot22 = E2.LengthSquared();
  318. float dot1d = Vector3.Dot(E1, D);
  319. float dot2d = Vector3.Dot(E2, D);
  320. float dotdd = D.LengthSquared();
  321. float s = dot1d * dot22 - dot2d * dot12;
  322. float t = dot2d * dot11 - dot1d * dot12;
  323. float d = dot11 * dot22 - dot12 * dot12;
  324. if (dot1d <= 0 && dot2d <= 0)
  325. {
  326. // nearest point is V0
  327. return v0;
  328. }
  329. if (s <= 0 && dot2d >= 0 && dot2d <= dot22)
  330. {
  331. // nearest point is on E2
  332. return v0 + E2 * (dot2d / dot22);
  333. }
  334. if (t <= 0 && dot1d >= 0 && dot1d <= dot11)
  335. {
  336. // nearest point is on E1
  337. return v0 + E1 * (dot1d / dot11);
  338. }
  339. if (s >= 0 && t >= 0 && s + t <= d)
  340. {
  341. // nearest point is inside the triangle
  342. float dr = 1.0f / d;
  343. return v0 + (s * dr) * E1 + (t * dr) * E2;
  344. }
  345. // we need to compute u12. This is hairier than
  346. // u1 or u2 because we're not in a convenient
  347. // basis any more.
  348. float u12_num = dot2d - dot1d - dot12 + dot11;
  349. float u12_den = dot22 + dot11 - 2 * dot12;
  350. if (u12_num <= 0)
  351. {
  352. return v1;
  353. }
  354. if (u12_num >= u12_den)
  355. {
  356. return v2;
  357. }
  358. return v1 + (v2 - v1) * (u12_num / u12_den);
  359. }
  360. /// <summary>
  361. /// Check if an origin-centered, axis-aligned box with the given half extents contains,
  362. /// intersects, or is disjoint from the given triangle. This is used for the box and
  363. /// frustum vs. triangle tests.
  364. /// </summary>
  365. public static ContainmentType OriginBoxContains(ref Vector3 halfExtent, ref Triangle tri)
  366. {
  367. BoundingBox triBounds = new BoundingBox(); // 'new' to work around NetCF bug
  368. triBounds.Min.X = Math.Min(tri.V0.X, Math.Min(tri.V1.X, tri.V2.X));
  369. triBounds.Min.Y = Math.Min(tri.V0.Y, Math.Min(tri.V1.Y, tri.V2.Y));
  370. triBounds.Min.Z = Math.Min(tri.V0.Z, Math.Min(tri.V1.Z, tri.V2.Z));
  371. triBounds.Max.X = Math.Max(tri.V0.X, Math.Max(tri.V1.X, tri.V2.X));
  372. triBounds.Max.Y = Math.Max(tri.V0.Y, Math.Max(tri.V1.Y, tri.V2.Y));
  373. triBounds.Max.Z = Math.Max(tri.V0.Z, Math.Max(tri.V1.Z, tri.V2.Z));
  374. Vector3 triBoundhalfExtent;
  375. triBoundhalfExtent.X = (triBounds.Max.X - triBounds.Min.X) * 0.5f;
  376. triBoundhalfExtent.Y = (triBounds.Max.Y - triBounds.Min.Y) * 0.5f;
  377. triBoundhalfExtent.Z = (triBounds.Max.Z - triBounds.Min.Z) * 0.5f;
  378. Vector3 triBoundCenter;
  379. triBoundCenter.X = (triBounds.Max.X + triBounds.Min.X) * 0.5f;
  380. triBoundCenter.Y = (triBounds.Max.Y + triBounds.Min.Y) * 0.5f;
  381. triBoundCenter.Z = (triBounds.Max.Z + triBounds.Min.Z) * 0.5f;
  382. if (triBoundhalfExtent.X + halfExtent.X <= Math.Abs(triBoundCenter.X) ||
  383. triBoundhalfExtent.Y + halfExtent.Y <= Math.Abs(triBoundCenter.Y) ||
  384. triBoundhalfExtent.Z + halfExtent.Z <= Math.Abs(triBoundCenter.Z))
  385. {
  386. return ContainmentType.Disjoint;
  387. }
  388. if (triBoundhalfExtent.X + Math.Abs(triBoundCenter.X) <= halfExtent.X &&
  389. triBoundhalfExtent.Y + Math.Abs(triBoundCenter.Y) <= halfExtent.Y &&
  390. triBoundhalfExtent.Z + Math.Abs(triBoundCenter.Z) <= halfExtent.Z)
  391. {
  392. return ContainmentType.Contains;
  393. }
  394. Vector3 edge1, edge2, edge3;
  395. Vector3.Subtract(ref tri.V1, ref tri.V0, out edge1);
  396. Vector3.Subtract(ref tri.V2, ref tri.V0, out edge2);
  397. Vector3 normal;
  398. Vector3.Cross(ref edge1, ref edge2, out normal);
  399. float triangleDist = Vector3.Dot(tri.V0, normal);
  400. if(Math.Abs(normal.X*halfExtent.X) + Math.Abs(normal.Y*halfExtent.Y) + Math.Abs(normal.Z*halfExtent.Z) <= Math.Abs(triangleDist))
  401. {
  402. return ContainmentType.Disjoint;
  403. }
  404. // Worst case: we need to check all 9 possible separating planes
  405. // defined by Cross(box edge,triangle edge)
  406. // Check for separation in plane containing an axis of box A and and axis of box B
  407. //
  408. // We need to compute all 9 cross products to find them, but a lot of terms drop out
  409. // since we're working in A's local space. Also, since each such plane is parallel
  410. // to the defining axis in each box, we know those dot products will be 0 and can
  411. // omit them.
  412. Vector3.Subtract(ref tri.V1, ref tri.V2, out edge3);
  413. float dv0, dv1, dv2, dhalf;
  414. // a.X ^ b.X = (1,0,0) ^ edge1
  415. // axis = Vector3(0, -edge1.Z, edge1.Y);
  416. dv0 = tri.V0.Z * edge1.Y - tri.V0.Y * edge1.Z;
  417. dv1 = tri.V1.Z * edge1.Y - tri.V1.Y * edge1.Z;
  418. dv2 = tri.V2.Z * edge1.Y - tri.V2.Y * edge1.Z;
  419. dhalf = Math.Abs(halfExtent.Y * edge1.Z) + Math.Abs(halfExtent.Z * edge1.Y);
  420. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  421. return ContainmentType.Disjoint;
  422. // a.X ^ b.Y = (1,0,0) ^ edge2
  423. // axis = Vector3(0, -edge2.Z, edge2.Y);
  424. dv0 = tri.V0.Z * edge2.Y - tri.V0.Y * edge2.Z;
  425. dv1 = tri.V1.Z * edge2.Y - tri.V1.Y * edge2.Z;
  426. dv2 = tri.V2.Z * edge2.Y - tri.V2.Y * edge2.Z;
  427. dhalf = Math.Abs(halfExtent.Y * edge2.Z) + Math.Abs(halfExtent.Z * edge2.Y);
  428. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  429. return ContainmentType.Disjoint;
  430. // a.X ^ b.Y = (1,0,0) ^ edge3
  431. // axis = Vector3(0, -edge3.Z, edge3.Y);
  432. dv0 = tri.V0.Z * edge3.Y - tri.V0.Y * edge3.Z;
  433. dv1 = tri.V1.Z * edge3.Y - tri.V1.Y * edge3.Z;
  434. dv2 = tri.V2.Z * edge3.Y - tri.V2.Y * edge3.Z;
  435. dhalf = Math.Abs(halfExtent.Y * edge3.Z) + Math.Abs(halfExtent.Z * edge3.Y);
  436. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  437. return ContainmentType.Disjoint;
  438. // a.Y ^ b.X = (0,1,0) ^ edge1
  439. // axis = Vector3(edge1.Z, 0, -edge1.X);
  440. dv0 = tri.V0.X * edge1.Z - tri.V0.Z * edge1.X;
  441. dv1 = tri.V1.X * edge1.Z - tri.V1.Z * edge1.X;
  442. dv2 = tri.V2.X * edge1.Z - tri.V2.Z * edge1.X;
  443. dhalf = Math.Abs(halfExtent.X * edge1.Z) + Math.Abs(halfExtent.Z * edge1.X);
  444. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  445. return ContainmentType.Disjoint;
  446. // a.Y ^ b.X = (0,1,0) ^ edge2
  447. // axis = Vector3(edge2.Z, 0, -edge2.X);
  448. dv0 = tri.V0.X * edge2.Z - tri.V0.Z * edge2.X;
  449. dv1 = tri.V1.X * edge2.Z - tri.V1.Z * edge2.X;
  450. dv2 = tri.V2.X * edge2.Z - tri.V2.Z * edge2.X;
  451. dhalf = Math.Abs(halfExtent.X * edge2.Z) + Math.Abs(halfExtent.Z * edge2.X);
  452. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  453. return ContainmentType.Disjoint;
  454. // a.Y ^ b.X = (0,1,0) ^ bX
  455. // axis = Vector3(edge3.Z, 0, -edge3.X);
  456. dv0 = tri.V0.X * edge3.Z - tri.V0.Z * edge3.X;
  457. dv1 = tri.V1.X * edge3.Z - tri.V1.Z * edge3.X;
  458. dv2 = tri.V2.X * edge3.Z - tri.V2.Z * edge3.X;
  459. dhalf = Math.Abs(halfExtent.X * edge3.Z) + Math.Abs(halfExtent.Z * edge3.X);
  460. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  461. return ContainmentType.Disjoint;
  462. // a.Y ^ b.X = (0,0,1) ^ edge1
  463. // axis = Vector3(-edge1.Y, edge1.X, 0);
  464. dv0 = tri.V0.Y * edge1.X - tri.V0.X * edge1.Y;
  465. dv1 = tri.V1.Y * edge1.X - tri.V1.X * edge1.Y;
  466. dv2 = tri.V2.Y * edge1.X - tri.V2.X * edge1.Y;
  467. dhalf = Math.Abs(halfExtent.Y * edge1.X) + Math.Abs(halfExtent.X * edge1.Y);
  468. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  469. return ContainmentType.Disjoint;
  470. // a.Y ^ b.X = (0,0,1) ^ edge2
  471. // axis = Vector3(-edge2.Y, edge2.X, 0);
  472. dv0 = tri.V0.Y * edge2.X - tri.V0.X * edge2.Y;
  473. dv1 = tri.V1.Y * edge2.X - tri.V1.X * edge2.Y;
  474. dv2 = tri.V2.Y * edge2.X - tri.V2.X * edge2.Y;
  475. dhalf = Math.Abs(halfExtent.Y * edge2.X) + Math.Abs(halfExtent.X * edge2.Y);
  476. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  477. return ContainmentType.Disjoint;
  478. // a.Y ^ b.X = (0,0,1) ^ edge3
  479. // axis = Vector3(-edge3.Y, edge3.X, 0);
  480. dv0 = tri.V0.Y * edge3.X - tri.V0.X * edge3.Y;
  481. dv1 = tri.V1.Y * edge3.X - tri.V1.X * edge3.Y;
  482. dv2 = tri.V2.Y * edge3.X - tri.V2.X * edge3.Y;
  483. dhalf = Math.Abs(halfExtent.Y * edge3.X) + Math.Abs(halfExtent.X * edge3.Y);
  484. if (Math.Min(dv0, Math.Min(dv1, dv2)) >= dhalf || Math.Max(dv0, Math.Max(dv1, dv2)) <= -dhalf)
  485. return ContainmentType.Disjoint;
  486. return ContainmentType.Intersects;
  487. }
  488. }
  489. }