ShapeCast.h 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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/AABox.h>
  6. #include <Jolt/Physics/Collision/CollideShape.h>
  7. #include <Jolt/Physics/Collision/Shape/Shape.h>
  8. JPH_NAMESPACE_BEGIN
  9. /// Structure that holds a single shape cast (a shape moving along a linear path in 3d space with no rotation)
  10. template <class Vec, class Mat, class ShapeCastType>
  11. struct ShapeCastT
  12. {
  13. JPH_OVERRIDE_NEW_DELETE
  14. /// Constructor
  15. ShapeCastT(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inCenterOfMassStart, Vec3Arg inDirection, const AABox &inWorldSpaceBounds) :
  16. mShape(inShape),
  17. mScale(inScale),
  18. mCenterOfMassStart(inCenterOfMassStart),
  19. mDirection(inDirection),
  20. mShapeWorldBounds(inWorldSpaceBounds)
  21. {
  22. }
  23. /// Constructor
  24. ShapeCastT(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inCenterOfMassStart, Vec3Arg inDirection) :
  25. ShapeCastT<Vec, Mat, ShapeCastType>(inShape, inScale, inCenterOfMassStart, inDirection, inShape->GetWorldSpaceBounds(inCenterOfMassStart, inScale))
  26. {
  27. }
  28. /// Construct a shape cast using a world transform for a shape instead of a center of mass transform
  29. static inline ShapeCastType sFromWorldTransform(const Shape *inShape, Vec3Arg inScale, typename Mat::ArgType inWorldTransform, Vec3Arg inDirection)
  30. {
  31. return ShapeCastType(inShape, inScale, inWorldTransform.PreTranslated(inShape->GetCenterOfMass()), inDirection);
  32. }
  33. /// Transform this shape cast using inTransform. Multiply transform on the left left hand side.
  34. ShapeCastType PostTransformed(typename Mat::ArgType inTransform) const
  35. {
  36. Mat44 start = inTransform * mCenterOfMassStart;
  37. Vec3 direction = inTransform.Multiply3x3(mDirection);
  38. return { mShape, mScale, start, direction };
  39. }
  40. /// Translate this shape cast by inTranslation.
  41. ShapeCastType PostTranslated(typename Vec::ArgType inTranslation) const
  42. {
  43. return { mShape, mScale, mCenterOfMassStart.PostTranslated(inTranslation), mDirection };
  44. }
  45. /// Get point with fraction inFraction on ray from mCenterOfMassStart to mCenterOfMassStart + mDirection (0 = start of ray, 1 = end of ray)
  46. inline Vec GetPointOnRay(float inFraction) const
  47. {
  48. return mCenterOfMassStart.GetTranslation() + inFraction * mDirection;
  49. }
  50. const Shape * mShape; ///< Shape that's being cast (cannot be mesh shape). Note that this structure does not assume ownership over the shape for performance reasons.
  51. const Vec3 mScale; ///< Scale in local space of the shape being cast
  52. const Mat mCenterOfMassStart; ///< Start position and orientation of the center of mass of the shape (construct using sFromWorldTransform if you have a world transform for your shape)
  53. const Vec3 mDirection; ///< Direction and length of the cast (anything beyond this length will not be reported as a hit)
  54. const AABox mShapeWorldBounds; ///< Cached shape's world bounds, calculated in constructor
  55. };
  56. struct ShapeCast : public ShapeCastT<Vec3, Mat44, ShapeCast>
  57. {
  58. using ShapeCastT<Vec3, Mat44, ShapeCast>::ShapeCastT;
  59. };
  60. struct RShapeCast : public ShapeCastT<RVec3, RMat44, RShapeCast>
  61. {
  62. using ShapeCastT<RVec3, RMat44, RShapeCast>::ShapeCastT;
  63. /// Convert from ShapeCast, converts single to double precision
  64. explicit RShapeCast(const ShapeCast &inCast) :
  65. RShapeCast(inCast.mShape, inCast.mScale, RMat44(inCast.mCenterOfMassStart), inCast.mDirection, inCast.mShapeWorldBounds)
  66. {
  67. }
  68. /// Convert to ShapeCast, which implies casting from double precision to single precision
  69. explicit operator ShapeCast() const
  70. {
  71. return ShapeCast(mShape, mScale, mCenterOfMassStart.ToMat44(), mDirection, mShapeWorldBounds);
  72. }
  73. };
  74. /// Settings to be passed with a shape cast
  75. class ShapeCastSettings : public CollideSettingsBase
  76. {
  77. public:
  78. JPH_OVERRIDE_NEW_DELETE
  79. /// How backfacing triangles should be treated (should we report moving out of a triangle?)
  80. EBackFaceMode mBackFaceModeTriangles = EBackFaceMode::IgnoreBackFaces;
  81. /// How backfacing convex objects should be treated (should we report starting inside an object and moving out?)
  82. EBackFaceMode mBackFaceModeConvex = EBackFaceMode::IgnoreBackFaces;
  83. /// Indicates if we want to shrink the shape by the convex radius and then expand it again. This speeds up collision detection and gives a more accurate normal at the cost of a more 'rounded' shape.
  84. bool mUseShrunkenShapeAndConvexRadius = false;
  85. /// When true, and the shape is intersecting at the beginning of the cast (fraction = 0) then this will calculate the deepest penetration point (costing additional CPU time)
  86. bool mReturnDeepestPoint = false;
  87. };
  88. /// Result of a shape cast test
  89. class ShapeCastResult : public CollideShapeResult
  90. {
  91. public:
  92. JPH_OVERRIDE_NEW_DELETE
  93. /// Default constructor
  94. ShapeCastResult() = default;
  95. /// Constructor
  96. /// @param inFraction Fraction at which the cast hit
  97. /// @param inContactPoint1 Contact point on shape 1
  98. /// @param inContactPoint2 Contact point on shape 2
  99. /// @param inContactNormalOrPenetrationDepth Contact normal pointing from shape 1 to 2 or penetration depth vector when the objects are penetrating (also from 1 to 2)
  100. /// @param inBackFaceHit If this hit was a back face hit
  101. /// @param inSubShapeID1 Sub shape id for shape 1
  102. /// @param inSubShapeID2 Sub shape id for shape 2
  103. /// @param inBodyID2 BodyID that was hit
  104. ShapeCastResult(float inFraction, Vec3Arg inContactPoint1, Vec3Arg inContactPoint2, Vec3Arg inContactNormalOrPenetrationDepth, bool inBackFaceHit, const SubShapeID &inSubShapeID1, const SubShapeID &inSubShapeID2, const BodyID &inBodyID2) :
  105. CollideShapeResult(inContactPoint1, inContactPoint2, inContactNormalOrPenetrationDepth, (inContactPoint2 - inContactPoint1).Length(), inSubShapeID1, inSubShapeID2, inBodyID2),
  106. mFraction(inFraction),
  107. mIsBackFaceHit(inBackFaceHit)
  108. {
  109. }
  110. /// Function required by the CollisionCollector. A smaller fraction is considered to be a 'better hit'. For rays/cast shapes we can just use the collision fraction. The fraction and penetration depth are combined in such a way that deeper hits at fraction 0 go first.
  111. inline float GetEarlyOutFraction() const { return mFraction > 0.0f? mFraction : -mPenetrationDepth; }
  112. /// Reverses the hit result, swapping contact point 1 with contact point 2 etc.
  113. /// @param inWorldSpaceCastDirection Direction of the shape cast in world space
  114. ShapeCastResult Reversed(Vec3Arg inWorldSpaceCastDirection) const
  115. {
  116. // Calculate by how much to shift the contact points
  117. Vec3 delta = mFraction * inWorldSpaceCastDirection;
  118. ShapeCastResult result;
  119. result.mContactPointOn2 = mContactPointOn1 - delta;
  120. result.mContactPointOn1 = mContactPointOn2 - delta;
  121. result.mPenetrationAxis = -mPenetrationAxis;
  122. result.mPenetrationDepth = mPenetrationDepth;
  123. result.mSubShapeID2 = mSubShapeID1;
  124. result.mSubShapeID1 = mSubShapeID2;
  125. result.mBodyID2 = mBodyID2;
  126. result.mFraction = mFraction;
  127. result.mIsBackFaceHit = mIsBackFaceHit;
  128. result.mShape2Face.resize(mShape1Face.size());
  129. for (Face::size_type i = 0; i < mShape1Face.size(); ++i)
  130. result.mShape2Face[i] = mShape1Face[i] - delta;
  131. result.mShape1Face.resize(mShape2Face.size());
  132. for (Face::size_type i = 0; i < mShape2Face.size(); ++i)
  133. result.mShape1Face[i] = mShape2Face[i] - delta;
  134. return result;
  135. }
  136. float mFraction; ///< This is the fraction where the shape hit the other shape: CenterOfMassOnHit = Start + value * (End - Start)
  137. bool mIsBackFaceHit; ///< True if the shape was hit from the back side
  138. };
  139. JPH_NAMESPACE_END