2
0

RayCylinder.h 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // SPDX-FileCopyrightText: 2021 Jorrit Rouwe
  2. // SPDX-License-Identifier: MIT
  3. #pragma once
  4. #include <Jolt/Math/FindRoot.h>
  5. JPH_NAMESPACE_BEGIN
  6. /// Tests a ray starting at inRayOrigin and extending infinitely in inRayDirection
  7. /// against an infinite cylinder centered along the Y axis
  8. /// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray.
  9. /// @param inRayDirection Direction of the ray. Does not need to be normalized.
  10. /// @param inRayOrigin Origin of the ray. If the ray starts inside the cylinder, the returned fraction will be 0.
  11. /// @param inCylinderRadius Radius of the infinite cylinder
  12. JPH_INLINE float RayCylinder(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCylinderRadius)
  13. {
  14. // Remove Y component of ray to see of ray intersects with infinite cylinder
  15. UVec4 mask_y = UVec4(0, 0xffffffff, 0, 0);
  16. Vec3 origin_xz = Vec3::sSelect(inRayOrigin, Vec3::sZero(), mask_y);
  17. float origin_xz_len_sq = origin_xz.LengthSq();
  18. float r_sq = Square(inCylinderRadius);
  19. if (origin_xz_len_sq > r_sq)
  20. {
  21. // Ray starts outside of the infinite cylinder
  22. // Solve: |RayOrigin_xz + fraction * RayDirection_xz|^2 = r^2 to find fraction
  23. Vec3 direction_xz = Vec3::sSelect(inRayDirection, Vec3::sZero(), mask_y);
  24. float a = direction_xz.LengthSq();
  25. float b = 2.0f * origin_xz.Dot(direction_xz);
  26. float c = origin_xz_len_sq - r_sq;
  27. float fraction1, fraction2;
  28. if (FindRoot(a, b, c, fraction1, fraction2) == 0)
  29. return FLT_MAX; // No intersection with infinite cylinder
  30. // Get fraction corresponding to the ray entering the circle
  31. float fraction = min(fraction1, fraction2);
  32. if (fraction >= 0.0f)
  33. return fraction;
  34. }
  35. else
  36. {
  37. // Ray starts inside the infinite cylinder
  38. return 0.0f;
  39. }
  40. // No collision
  41. return FLT_MAX;
  42. }
  43. /// Test a ray against a cylinder centered around the origin with its axis along the Y axis and half height specified.
  44. /// @return FLT_MAX if there is no intersection, otherwise the fraction along the ray.
  45. /// @param inRayDirection Ray direction. Does not need to be normalized.
  46. /// @param inRayOrigin Origin of the ray. If the ray starts inside the cylinder, the returned fraction will be 0.
  47. /// @param inCylinderRadius Radius of the cylinder
  48. /// @param inCylinderHalfHeight Distance from the origin to the top (or bottom) of the cylinder
  49. JPH_INLINE float RayCylinder(Vec3Arg inRayOrigin, Vec3Arg inRayDirection, float inCylinderHalfHeight, float inCylinderRadius)
  50. {
  51. // Test infinite cylinder
  52. float fraction = RayCylinder(inRayOrigin, inRayDirection, inCylinderRadius);
  53. if (fraction == FLT_MAX)
  54. return FLT_MAX;
  55. // If this hit is in the finite cylinder we have our fraction
  56. if (abs(inRayOrigin.GetY() + fraction * inRayDirection.GetY()) <= inCylinderHalfHeight)
  57. return fraction;
  58. // Check if ray could hit the top or bottom plane of the cylinder
  59. float direction_y = inRayDirection.GetY();
  60. if (direction_y != 0.0f)
  61. {
  62. // Solving line equation: x = ray_origin + fraction * ray_direction
  63. // and plane equation: plane_normal . x + plane_constant = 0
  64. // fraction = (-plane_constant - plane_normal . ray_origin) / (plane_normal . ray_direction)
  65. // when the ray_direction.y < 0:
  66. // plane_constant = -cylinder_half_height, plane_normal = (0, 1, 0)
  67. // else
  68. // plane_constant = -cylinder_half_height, plane_normal = (0, -1, 0)
  69. float origin_y = inRayOrigin.GetY();
  70. float plane_fraction;
  71. if (direction_y < 0.0f)
  72. plane_fraction = (inCylinderHalfHeight - origin_y) / direction_y;
  73. else
  74. plane_fraction = -(inCylinderHalfHeight + origin_y) / direction_y;
  75. // Check if the hit is in front of the ray
  76. if (plane_fraction >= 0.0f)
  77. {
  78. // Test if this hit is inside the cylinder
  79. Vec3 point = inRayOrigin + plane_fraction * inRayDirection;
  80. float dist_sq = Square(point.GetX()) + Square(point.GetZ());
  81. if (dist_sq <= Square(inCylinderRadius))
  82. return plane_fraction;
  83. }
  84. }
  85. // No collision
  86. return FLT_MAX;
  87. }
  88. JPH_NAMESPACE_END