ActiveEdges.h 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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/Geometry/ClosestPoint.h>
  6. JPH_NAMESPACE_BEGIN
  7. /// An active edge is an edge that either has no neighbouring edge or if the angle between the two connecting faces is too large.
  8. namespace ActiveEdges
  9. {
  10. /// Helper function to check if an edge is active or not
  11. /// @param inNormal1 Triangle normal of triangle on the left side of the edge (when looking along the edge from the top)
  12. /// @param inNormal2 Triangle normal of triangle on the right side of the edge
  13. /// @param inEdgeDirection Vector that points along the edge
  14. /// @param inCosThresholdAngle Cosine of the threshold angle (if the angle between the two triangles is bigger than this, the edge is active, note that a concave edge is always inactive)
  15. inline static bool IsEdgeActive(Vec3Arg inNormal1, Vec3Arg inNormal2, Vec3Arg inEdgeDirection, float inCosThresholdAngle)
  16. {
  17. // If normals are opposite the edges are active (the triangles are back to back)
  18. float cos_angle_normals = inNormal1.Dot(inNormal2);
  19. if (cos_angle_normals < -0.999848f) // cos(179 degrees)
  20. return true;
  21. // Check if concave edge, if so we are not active
  22. if (inNormal1.Cross(inNormal2).Dot(inEdgeDirection) < 0.0f)
  23. return false;
  24. // Convex edge, active when angle bigger than threshold
  25. return cos_angle_normals < inCosThresholdAngle;
  26. }
  27. /// Replace normal by triangle normal if a hit is hitting an inactive edge
  28. /// @param inV0 , inV1 , inV2 form the triangle
  29. /// @param inTriangleNormal is the normal of the provided triangle (does not need to be normalized)
  30. /// @param inActiveEdges bit 0 = edge v0..v1 is active, bit 1 = edge v1..v2 is active, bit 2 = edge v2..v0 is active
  31. /// @param inPoint Collision point on the triangle
  32. /// @param inNormal Collision normal on the triangle (does not need to be normalized)
  33. /// @param inMovementDirection Can be zero. This gives an indication of in which direction the motion is to determine if when we hit an inactive edge/triangle we should return the triangle normal.
  34. /// @return Returns inNormal if an active edge was hit, otherwise returns inTriangleNormal
  35. inline static Vec3 FixNormal(Vec3Arg inV0, Vec3Arg inV1, Vec3Arg inV2, Vec3Arg inTriangleNormal, uint8 inActiveEdges, Vec3Arg inPoint, Vec3Arg inNormal, Vec3Arg inMovementDirection)
  36. {
  37. // Check: All of the edges are active, we have the correct normal already. No need to call this function!
  38. JPH_ASSERT(inActiveEdges != 0b111);
  39. // If inNormal would affect movement less than inTriangleNormal use inNormal
  40. // This is done since it is really hard to make a distinction between sliding over a horizontal triangulated grid and hitting an edge (in this case you want to use the triangle normal)
  41. // and sliding over a triangulated grid and grazing a vertical triangle with an inactive edge (in this case using the triangle normal will cause the object to bounce back so we want to use the calculated normal).
  42. // To solve this we take a movement hint to give an indication of what direction our object is moving. If the edge normal results in less motion difference than the triangle normal we use the edge normal.
  43. float normal_length = inNormal.Length();
  44. float triangle_normal_length = inTriangleNormal.Length();
  45. if (inMovementDirection.Dot(inNormal) * triangle_normal_length < inMovementDirection.Dot(inTriangleNormal) * normal_length)
  46. return inNormal;
  47. // Check: None of the edges are active, we need to use the triangle normal
  48. if (inActiveEdges == 0)
  49. return inTriangleNormal;
  50. // Some edges are active.
  51. // If normal is parallel to the triangle normal we don't need to check the active edges.
  52. if (inTriangleNormal.Dot(inNormal) > 0.999848f * normal_length * triangle_normal_length) // cos(1 degree)
  53. return inNormal;
  54. const float cEpsilon = 1.0e-4f;
  55. const float cOneMinusEpsilon = 1.0f - cEpsilon;
  56. uint colliding_edge;
  57. // Test where the contact point is in the triangle
  58. float u, v, w;
  59. ClosestPoint::GetBaryCentricCoordinates(inV0 - inPoint, inV1 - inPoint, inV2 - inPoint, u, v, w);
  60. if (u > cOneMinusEpsilon)
  61. {
  62. // Colliding with v0, edge 0 or 2 needs to be active
  63. colliding_edge = 0b101;
  64. }
  65. else if (v > cOneMinusEpsilon)
  66. {
  67. // Colliding with v1, edge 0 or 1 needs to be active
  68. colliding_edge = 0b011;
  69. }
  70. else if (w > cOneMinusEpsilon)
  71. {
  72. // Colliding with v2, edge 1 or 2 needs to be active
  73. colliding_edge = 0b110;
  74. }
  75. else if (u < cEpsilon)
  76. {
  77. // Colliding with edge v1, v2, edge 1 needs to be active
  78. colliding_edge = 0b010;
  79. }
  80. else if (v < cEpsilon)
  81. {
  82. // Colliding with edge v0, v2, edge 2 needs to be active
  83. colliding_edge = 0b100;
  84. }
  85. else if (w < cEpsilon)
  86. {
  87. // Colliding with edge v0, v1, edge 0 needs to be active
  88. colliding_edge = 0b001;
  89. }
  90. else
  91. {
  92. // Interior hit
  93. return inTriangleNormal;
  94. }
  95. // If this edge is active, use the provided normal instead of the triangle normal
  96. return (inActiveEdges & colliding_edge) != 0? inNormal : inTriangleNormal;
  97. }
  98. }
  99. JPH_NAMESPACE_END