IntersectionTestHelpers.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Tests/Math/IntersectionTestHelpers.h>
  9. #include <AzCore/Math/MathUtils.h>
  10. namespace UnitTest::IntersectTest
  11. {
  12. template<bool oneSided>
  13. bool MollerTrumboreIntersectSegmentTriangleGeneric(
  14. const AZ::Vector3& p, const AZ::Vector3& rayEnd,
  15. const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c,
  16. AZ::Vector3& normal, float& t)
  17. {
  18. AZ::Vector3 ab = b - a;
  19. AZ::Vector3 ac = c - a;
  20. AZ::Vector3 ap = p - a;
  21. AZ::Vector3 qp = rayEnd - p;
  22. AZ::Vector3 h = qp.Cross(ac);
  23. float d = ab.Dot(h);
  24. if (d < AZ::Constants::FloatEpsilon)
  25. {
  26. if constexpr(oneSided)
  27. {
  28. return false;
  29. }
  30. else if (d > -AZ::Constants::FloatEpsilon)
  31. {
  32. return false;
  33. }
  34. }
  35. float f = 1.0f / d;
  36. float u = f * ap.Dot(h);
  37. if (u < 0.0f || u > 1.0f)
  38. {
  39. return false;
  40. }
  41. AZ::Vector3 q = ap.Cross(ab);
  42. float v = f * qp.Dot(q);
  43. if (v < 0.0f || u + v > 1.0f)
  44. {
  45. return false;
  46. }
  47. t = f * ac.Dot(q);
  48. if ((t > AZ::Constants::FloatEpsilon) && (t <= 1.0f))
  49. {
  50. normal = ab.Cross(ac).GetNormalized();
  51. if constexpr(!oneSided)
  52. {
  53. if (d < 0.0f)
  54. {
  55. normal = -normal;
  56. }
  57. }
  58. return true;
  59. }
  60. return false;
  61. }
  62. bool MollerTrumboreIntersectSegmentTriangleCCW(
  63. const AZ::Vector3& p, const AZ::Vector3& rayEnd,
  64. const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c,
  65. AZ::Vector3& triNormal, float& t)
  66. {
  67. return MollerTrumboreIntersectSegmentTriangleGeneric<true>(p, rayEnd, a, b, c, triNormal, t);
  68. }
  69. bool MollerTrumboreIntersectSegmentTriangle(
  70. const AZ::Vector3& p, const AZ::Vector3& rayEnd,
  71. const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c,
  72. AZ::Vector3& triNormal, float& t)
  73. {
  74. return MollerTrumboreIntersectSegmentTriangleGeneric<false>(p, rayEnd, a, b, c, triNormal, t);
  75. }
  76. bool ArenbergIntersectSegmentTriangleCCW(
  77. const AZ::Vector3& p, const AZ::Vector3& q,
  78. const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c,
  79. AZ::Vector3& normal, float& t)
  80. {
  81. AZ::Vector3 ab = b - a;
  82. AZ::Vector3 ac = c - a;
  83. AZ::Vector3 qp = p - q;
  84. // Compute triangle normal. Can be pre-calculated/cached if
  85. // intersecting multiple segments against the same triangle.
  86. // Note that the normal isn't normalized yet, most of the calculations below just need the direction of the normal.
  87. // It will only get normalized after we've detected a successful hit.
  88. normal = ab.Cross(ac); // Right hand CCW
  89. // Compute denominator d.
  90. // This is a projection of our ray (pq) onto the Normal.
  91. // If d == 0, the ray is perpendicular to the Normal, or parallel to the triangle, so it doesn't intersect.
  92. // We're actually projecting qp, not pq, so if our ray is going the same direction as the Normal (positive),
  93. // then we're really pointing negative which means pointing into the side of the triangle with the surface.
  94. // If we're negative, then we're really pointing positive, which means pointing into the side of the triangle without the
  95. // surface, so exit early.
  96. // The value of d contains the length of our ray segment in triangle space, since it's the length along the normal.
  97. float d = qp.Dot(normal);
  98. if (d <= 0.0f)
  99. {
  100. return false;
  101. }
  102. // Compute t, which is the length of ray segment that's above the triangle in triangle space.
  103. // If t < 0, then the entire ray segment appears below the triangle, so return early.
  104. // If t > d, then the entire ray segment appears above the triangle, so return early.
  105. // If 0 <= t <= d, then some of pq appears above the triangle, and some appears below it.
  106. // Compute intersection t value of pq with plane of triangle.
  107. // If this were a ray instead of a segment, 0 <= t would be a sufficient check.
  108. AZ::Vector3 ap = p - a;
  109. t = ap.Dot(normal);
  110. if (t < 0.0f || t > d)
  111. {
  112. return false;
  113. }
  114. // Compute barycentric coordinate components and test if within bounds
  115. AZ::Vector3 e = qp.Cross(ap);
  116. float v = ac.Dot(e);
  117. if (v < 0.0f || v > d)
  118. {
  119. return false;
  120. }
  121. float w = -ab.Dot(e);
  122. if (w < 0.0f || ((v + w) > d))
  123. {
  124. return false;
  125. }
  126. // t/d is the fractional percentage of our ray segment that appears above the triangle in triangle space.
  127. // However, because it's a percentage, it can also be used back in world space as well to determine how much of our ray
  128. // segment appears above the triangle, which consequently gives us the collision point.
  129. float ood = 1.0f / d;
  130. t *= ood;
  131. normal.Normalize();
  132. return true;
  133. }
  134. bool ArenbergIntersectSegmentTriangle(
  135. const AZ::Vector3& p, const AZ::Vector3& q,
  136. const AZ::Vector3& a, const AZ::Vector3& b, const AZ::Vector3& c,
  137. AZ::Vector3& normal, float& t)
  138. {
  139. AZ::Vector3 ab = b - a;
  140. AZ::Vector3 ac = c - a;
  141. AZ::Vector3 qp = p - q;
  142. AZ::Vector3 ap = p - a;
  143. // Compute triangle normal. Can be pre-calculated or cached if
  144. // intersecting multiple segments against the same triangle
  145. normal = ab.Cross(ac); // Right hand CCW
  146. // Compute denominator d. If d <= 0, segment is parallel to or points
  147. // away from triangle, so exit early
  148. float d = qp.Dot(normal);
  149. AZ::Vector3 e;
  150. if (d > AZ::Constants::FloatEpsilon)
  151. {
  152. // the normal is on the right side
  153. e = qp.Cross(ap);
  154. }
  155. else
  156. {
  157. normal = -normal;
  158. // so either have a parallel ray or our normal is flipped
  159. if (d >= -AZ::Constants::FloatEpsilon)
  160. {
  161. return false; // parallel
  162. }
  163. d = -d;
  164. e = ap.Cross(qp);
  165. }
  166. // Compute intersection t value of pq with plane of triangle. A ray
  167. // intersects iff 0 <= t. Segment intersects iff 0 <= t <= 1. Delay
  168. // dividing by d until intersection has been found to pierce triangle
  169. t = ap.Dot(normal);
  170. // range segment check t[0,1] (it this case [0,d])
  171. if (t < 0.0f || t > d)
  172. {
  173. return false;
  174. }
  175. // Compute barycentric coordinate components and test if within bounds
  176. float v = ac.Dot(e);
  177. if (v < 0.0f || v > d)
  178. {
  179. return false;
  180. }
  181. float w = -ab.Dot(e);
  182. if (w < 0.0f || v + w > d)
  183. {
  184. return false;
  185. }
  186. // Segment/ray intersects the triangle. Perform delayed division and
  187. // compute the last barycentric coordinate component
  188. float ood = 1.0f / d;
  189. t *= ood;
  190. normal.Normalize();
  191. return true;
  192. }
  193. }