RayAABox.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. JPH_NAMESPACE_BEGIN
  6. /// Helper structure holding the reciprocal of a ray for Ray vs AABox testing
  7. class RayInvDirection
  8. {
  9. public:
  10. /// Constructors
  11. inline RayInvDirection() = default;
  12. inline explicit RayInvDirection(Vec3Arg inDirection) { Set(inDirection); }
  13. /// Set reciprocal from ray direction
  14. inline void Set(Vec3Arg inDirection)
  15. {
  16. // if (abs(inDirection) <= Epsilon) the ray is nearly parallel to the slab.
  17. mIsParallel = Vec3::sLessOrEqual(inDirection.Abs(), Vec3::sReplicate(1.0e-20f));
  18. // Calculate 1 / direction while avoiding division by zero
  19. mInvDirection = Vec3::sSelect(inDirection, Vec3::sReplicate(1.0f), mIsParallel).Reciprocal();
  20. }
  21. Vec3 mInvDirection; ///< 1 / ray direction
  22. UVec4 mIsParallel; ///< for each component if it is parallel to the coordinate axis
  23. };
  24. /// Intersect AABB with ray, returns minimal distance along ray or FLT_MAX if no hit
  25. /// Note: Can return negative value if ray starts in box
  26. JPH_INLINE float RayAABox(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax)
  27. {
  28. // Constants
  29. Vec3 flt_min = Vec3::sReplicate(-FLT_MAX);
  30. Vec3 flt_max = Vec3::sReplicate(FLT_MAX);
  31. // Test against all three axii simultaneously.
  32. Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection;
  33. Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection;
  34. // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't
  35. // use the results from any directions parallel to the slab.
  36. Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel);
  37. Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel);
  38. // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z);
  39. t_min = Vec3::sMax(t_min, t_min.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>());
  40. t_min = Vec3::sMax(t_min, t_min.Swizzle<SWIZZLE_Z, SWIZZLE_X, SWIZZLE_Y>());
  41. // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z);
  42. t_max = Vec3::sMin(t_max, t_max.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>());
  43. t_max = Vec3::sMin(t_max, t_max.Swizzle<SWIZZLE_Z, SWIZZLE_X, SWIZZLE_Y>());
  44. // if (t_min > t_max) return FLT_MAX;
  45. UVec4 no_intersection = Vec3::sGreater(t_min, t_max);
  46. // if (t_max < 0.0f) return FLT_MAX;
  47. no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero()));
  48. // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min;
  49. UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax));
  50. no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap));
  51. no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatY());
  52. no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatZ());
  53. return Vec3::sSelect(t_min, flt_max, no_intersection).GetX();
  54. }
  55. /// Intersect 4 AABBs with ray, returns minimal distance along ray or FLT_MAX if no hit
  56. /// Note: Can return negative value if ray starts in box
  57. JPH_INLINE Vec4 RayAABox4(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec4Arg inBoundsMinX, Vec4Arg inBoundsMinY, Vec4Arg inBoundsMinZ, Vec4Arg inBoundsMaxX, Vec4Arg inBoundsMaxY, Vec4Arg inBoundsMaxZ)
  58. {
  59. // Constants
  60. Vec4 flt_min = Vec4::sReplicate(-FLT_MAX);
  61. Vec4 flt_max = Vec4::sReplicate(FLT_MAX);
  62. // Origin
  63. Vec4 originx = inOrigin.SplatX();
  64. Vec4 originy = inOrigin.SplatY();
  65. Vec4 originz = inOrigin.SplatZ();
  66. // Parallel
  67. UVec4 parallelx = inInvDirection.mIsParallel.SplatX();
  68. UVec4 parallely = inInvDirection.mIsParallel.SplatY();
  69. UVec4 parallelz = inInvDirection.mIsParallel.SplatZ();
  70. // Inverse direction
  71. Vec4 invdirx = inInvDirection.mInvDirection.SplatX();
  72. Vec4 invdiry = inInvDirection.mInvDirection.SplatY();
  73. Vec4 invdirz = inInvDirection.mInvDirection.SplatZ();
  74. // Test against all three axii simultaneously.
  75. Vec4 t1x = (inBoundsMinX - originx) * invdirx;
  76. Vec4 t1y = (inBoundsMinY - originy) * invdiry;
  77. Vec4 t1z = (inBoundsMinZ - originz) * invdirz;
  78. Vec4 t2x = (inBoundsMaxX - originx) * invdirx;
  79. Vec4 t2y = (inBoundsMaxY - originy) * invdiry;
  80. Vec4 t2z = (inBoundsMaxZ - originz) * invdirz;
  81. // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't
  82. // use the results from any directions parallel to the slab.
  83. Vec4 t_minx = Vec4::sSelect(Vec4::sMin(t1x, t2x), flt_min, parallelx);
  84. Vec4 t_miny = Vec4::sSelect(Vec4::sMin(t1y, t2y), flt_min, parallely);
  85. Vec4 t_minz = Vec4::sSelect(Vec4::sMin(t1z, t2z), flt_min, parallelz);
  86. Vec4 t_maxx = Vec4::sSelect(Vec4::sMax(t1x, t2x), flt_max, parallelx);
  87. Vec4 t_maxy = Vec4::sSelect(Vec4::sMax(t1y, t2y), flt_max, parallely);
  88. Vec4 t_maxz = Vec4::sSelect(Vec4::sMax(t1z, t2z), flt_max, parallelz);
  89. // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z);
  90. Vec4 t_min = Vec4::sMax(Vec4::sMax(t_minx, t_miny), t_minz);
  91. // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z);
  92. Vec4 t_max = Vec4::sMin(Vec4::sMin(t_maxx, t_maxy), t_maxz);
  93. // if (t_min > t_max) return FLT_MAX;
  94. UVec4 no_intersection = Vec4::sGreater(t_min, t_max);
  95. // if (t_max < 0.0f) return FLT_MAX;
  96. no_intersection = UVec4::sOr(no_intersection, Vec4::sLess(t_max, Vec4::sZero()));
  97. // if bounds are invalid return FLOAT_MAX;
  98. UVec4 bounds_invalid = UVec4::sOr(UVec4::sOr(Vec4::sGreater(inBoundsMinX, inBoundsMaxX), Vec4::sGreater(inBoundsMinY, inBoundsMaxY)), Vec4::sGreater(inBoundsMinZ, inBoundsMaxZ));
  99. no_intersection = UVec4::sOr(no_intersection, bounds_invalid);
  100. // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min;
  101. UVec4 no_parallel_overlapx = UVec4::sAnd(parallelx, UVec4::sOr(Vec4::sLess(originx, inBoundsMinX), Vec4::sGreater(originx, inBoundsMaxX)));
  102. UVec4 no_parallel_overlapy = UVec4::sAnd(parallely, UVec4::sOr(Vec4::sLess(originy, inBoundsMinY), Vec4::sGreater(originy, inBoundsMaxY)));
  103. UVec4 no_parallel_overlapz = UVec4::sAnd(parallelz, UVec4::sOr(Vec4::sLess(originz, inBoundsMinZ), Vec4::sGreater(originz, inBoundsMaxZ)));
  104. no_intersection = UVec4::sOr(no_intersection, UVec4::sOr(UVec4::sOr(no_parallel_overlapx, no_parallel_overlapy), no_parallel_overlapz));
  105. return Vec4::sSelect(t_min, flt_max, no_intersection);
  106. }
  107. /// Intersect AABB with ray, returns minimal and maximal distance along ray or FLT_MAX, -FLT_MAX if no hit
  108. /// Note: Can return negative value for outMin if ray starts in box
  109. JPH_INLINE void RayAABox(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, float &outMin, float &outMax)
  110. {
  111. // Constants
  112. Vec3 flt_min = Vec3::sReplicate(-FLT_MAX);
  113. Vec3 flt_max = Vec3::sReplicate(FLT_MAX);
  114. // Test against all three axii simultaneously.
  115. Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection;
  116. Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection;
  117. // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't
  118. // use the results from any directions parallel to the slab.
  119. Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel);
  120. Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel);
  121. // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z);
  122. t_min = Vec3::sMax(t_min, t_min.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>());
  123. t_min = Vec3::sMax(t_min, t_min.Swizzle<SWIZZLE_Z, SWIZZLE_X, SWIZZLE_Y>());
  124. // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z);
  125. t_max = Vec3::sMin(t_max, t_max.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>());
  126. t_max = Vec3::sMin(t_max, t_max.Swizzle<SWIZZLE_Z, SWIZZLE_X, SWIZZLE_Y>());
  127. // if (t_min > t_max) return FLT_MAX;
  128. UVec4 no_intersection = Vec3::sGreater(t_min, t_max);
  129. // if (t_max < 0.0f) return FLT_MAX;
  130. no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero()));
  131. // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return FLT_MAX; else return t_min;
  132. UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax));
  133. no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap));
  134. no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatY());
  135. no_intersection = UVec4::sOr(no_intersection, no_intersection.SplatZ());
  136. outMin = Vec3::sSelect(t_min, flt_max, no_intersection).GetX();
  137. outMax = Vec3::sSelect(t_max, flt_min, no_intersection).GetX();
  138. }
  139. /// Intersect AABB with ray, returns true if there is a hit closer than inClosest
  140. JPH_INLINE bool RayAABoxHits(Vec3Arg inOrigin, const RayInvDirection &inInvDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax, float inClosest)
  141. {
  142. // Constants
  143. Vec3 flt_min = Vec3::sReplicate(-FLT_MAX);
  144. Vec3 flt_max = Vec3::sReplicate(FLT_MAX);
  145. // Test against all three axii simultaneously.
  146. Vec3 t1 = (inBoundsMin - inOrigin) * inInvDirection.mInvDirection;
  147. Vec3 t2 = (inBoundsMax - inOrigin) * inInvDirection.mInvDirection;
  148. // Compute the max of min(t1,t2) and the min of max(t1,t2) ensuring we don't
  149. // use the results from any directions parallel to the slab.
  150. Vec3 t_min = Vec3::sSelect(Vec3::sMin(t1, t2), flt_min, inInvDirection.mIsParallel);
  151. Vec3 t_max = Vec3::sSelect(Vec3::sMax(t1, t2), flt_max, inInvDirection.mIsParallel);
  152. // t_min.xyz = maximum(t_min.x, t_min.y, t_min.z);
  153. t_min = Vec3::sMax(t_min, t_min.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>());
  154. t_min = Vec3::sMax(t_min, t_min.Swizzle<SWIZZLE_Z, SWIZZLE_X, SWIZZLE_Y>());
  155. // t_max.xyz = minimum(t_max.x, t_max.y, t_max.z);
  156. t_max = Vec3::sMin(t_max, t_max.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>());
  157. t_max = Vec3::sMin(t_max, t_max.Swizzle<SWIZZLE_Z, SWIZZLE_X, SWIZZLE_Y>());
  158. // if (t_min > t_max) return false;
  159. UVec4 no_intersection = Vec3::sGreater(t_min, t_max);
  160. // if (t_max < 0.0f) return false;
  161. no_intersection = UVec4::sOr(no_intersection, Vec3::sLess(t_max, Vec3::sZero()));
  162. // if (t_min > inClosest) return false;
  163. no_intersection = UVec4::sOr(no_intersection, Vec3::sGreater(t_min, Vec3::sReplicate(inClosest)));
  164. // if (inInvDirection.mIsParallel && !(Min <= inOrigin && inOrigin <= Max)) return false; else return true;
  165. UVec4 no_parallel_overlap = UVec4::sOr(Vec3::sLess(inOrigin, inBoundsMin), Vec3::sGreater(inOrigin, inBoundsMax));
  166. no_intersection = UVec4::sOr(no_intersection, UVec4::sAnd(inInvDirection.mIsParallel, no_parallel_overlap));
  167. return !no_intersection.TestAnyXYZTrue();
  168. }
  169. /// Intersect AABB with ray without hit fraction, based on separating axis test
  170. /// @see http://www.codercorner.com/RayAABB.cpp
  171. JPH_INLINE bool RayAABoxHits(Vec3Arg inOrigin, Vec3Arg inDirection, Vec3Arg inBoundsMin, Vec3Arg inBoundsMax)
  172. {
  173. Vec3 extents = inBoundsMax - inBoundsMin;
  174. Vec3 diff = 2.0f * inOrigin - inBoundsMin - inBoundsMax;
  175. Vec3 abs_diff = diff.Abs();
  176. UVec4 no_intersection = UVec4::sAnd(Vec3::sGreater(abs_diff, extents), Vec3::sGreaterOrEqual(diff * inDirection, Vec3::sZero()));
  177. Vec3 abs_dir = inDirection.Abs();
  178. Vec3 abs_dir_yzz = abs_dir.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z>();
  179. Vec3 abs_dir_xyx = abs_dir.Swizzle<SWIZZLE_X, SWIZZLE_Y, SWIZZLE_X>();
  180. Vec3 extents_yzz = extents.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_Z>();
  181. Vec3 extents_xyx = extents.Swizzle<SWIZZLE_X, SWIZZLE_Y, SWIZZLE_X>();
  182. Vec3 diff_yzx = diff.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>();
  183. Vec3 dir_yzx = inDirection.Swizzle<SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_X>();
  184. no_intersection = UVec4::sOr(no_intersection, Vec3::sGreater((inDirection * diff_yzx - dir_yzx * diff).Abs(), extents_xyx * abs_dir_yzz + extents_yzz * abs_dir_xyx));
  185. return !no_intersection.TestAnyXYZTrue();
  186. }
  187. JPH_NAMESPACE_END