2
0

RayCylinder.h 3.9 KB

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