ContactListener.h 10.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/Physics/Collision/Shape/SubShapeIDPair.h>
  6. #include <Jolt/Core/StaticArray.h>
  7. JPH_NAMESPACE_BEGIN
  8. class Body;
  9. class CollideShapeResult;
  10. /// Array of contact points
  11. using ContactPoints = StaticArray<Vec3, 64>;
  12. /// Manifold class, describes the contact surface between two bodies
  13. class ContactManifold
  14. {
  15. public:
  16. /// Swaps shape 1 and 2
  17. ContactManifold SwapShapes() const { return { mBaseOffset, -mWorldSpaceNormal, mPenetrationDepth, mSubShapeID2, mSubShapeID1, mRelativeContactPointsOn2, mRelativeContactPointsOn1 }; }
  18. /// Access to the world space contact positions
  19. inline RVec3 GetWorldSpaceContactPointOn1(uint inIndex) const { return mBaseOffset + mRelativeContactPointsOn1[inIndex]; }
  20. inline RVec3 GetWorldSpaceContactPointOn2(uint inIndex) const { return mBaseOffset + mRelativeContactPointsOn2[inIndex]; }
  21. RVec3 mBaseOffset; ///< Offset to which all the contact points are relative
  22. Vec3 mWorldSpaceNormal; ///< Normal for this manifold, direction along which to move body 2 out of collision along the shortest path
  23. float mPenetrationDepth; ///< Penetration depth (move shape 2 by this distance to resolve the collision). If this value is negative, this is a speculative contact point and may not actually result in a velocity change as during solving the bodies may not actually collide.
  24. SubShapeID mSubShapeID1; ///< Sub shapes that formed this manifold (note that when multiple manifolds are combined because they're coplanar, we lose some information here because we only keep track of one sub shape pair that we encounter, see description at Body::SetUseManifoldReduction)
  25. SubShapeID mSubShapeID2;
  26. ContactPoints mRelativeContactPointsOn1; ///< Contact points on the surface of shape 1 relative to mBaseOffset.
  27. ContactPoints mRelativeContactPointsOn2; ///< Contact points on the surface of shape 2 relative to mBaseOffset. If there's no penetration, this will be the same as mRelativeContactPointsOn1. If there is penetration they will be different.
  28. };
  29. /// When a contact point is added or persisted, the callback gets a chance to override certain properties of the contact constraint.
  30. /// The values are filled in with their defaults by the system so the callback doesn't need to modify anything, but it can if it wants to.
  31. class ContactSettings
  32. {
  33. public:
  34. float mCombinedFriction; ///< Combined friction for the body pair (see: PhysicsSystem::SetCombineFriction)
  35. float mCombinedRestitution; ///< Combined restitution for the body pair (see: PhysicsSystem::SetCombineRestitution)
  36. float mInvMassScale1 = 1.0f; ///< Scale factor for the inverse mass of body 1 (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time.
  37. float mInvInertiaScale1 = 1.0f; ///< Scale factor for the inverse inertia of body 1 (usually same as mInvMassScale1)
  38. float mInvMassScale2 = 1.0f; ///< Scale factor for the inverse mass of body 2 (0 = infinite mass, 1 = use original mass, 2 = body has half the mass). For the same contact pair, you should strive to keep the value the same over time.
  39. float mInvInertiaScale2 = 1.0f; ///< Scale factor for the inverse inertia of body 2 (usually same as mInvMassScale2)
  40. bool mIsSensor; ///< If the contact should be treated as a sensor vs body contact (no collision response)
  41. Vec3 mRelativeLinearSurfaceVelocity = Vec3::sZero(); ///< Relative linear surface velocity between the bodies (world space surface velocity of body 2 - world space surface velocity of body 1), can be used to create a conveyor belt effect
  42. Vec3 mRelativeAngularSurfaceVelocity = Vec3::sZero(); ///< Relative angular surface velocity between the bodies (world space angular surface velocity of body 2 - world space angular surface velocity of body 1). Note that this angular velocity is relative to the center of mass of body 1, so if you want it relative to body 2's center of mass you need to add body 2 angular velocity x (body 1 world space center of mass - body 2 world space center of mass) to mRelativeLinearSurfaceVelocity.
  43. };
  44. /// Return value for the OnContactValidate callback. Determines if the contact is being processed or not.
  45. /// Results are ordered so that the strongest accept has the lowest number and the strongest reject the highest number (which allows for easy combining of results)
  46. enum class ValidateResult
  47. {
  48. AcceptAllContactsForThisBodyPair, ///< Accept this and any further contact points for this body pair
  49. AcceptContact, ///< Accept this contact only (and continue calling this callback for every contact manifold for the same body pair)
  50. RejectContact, ///< Reject this contact only (but process any other contact manifolds for the same body pair)
  51. RejectAllContactsForThisBodyPair ///< Rejects this and any further contact points for this body pair
  52. };
  53. /// A listener class that receives collision contact events.
  54. /// It can be registered with the ContactConstraintManager (or PhysicsSystem).
  55. /// Note that contact listener callbacks are called from multiple threads at the same time when all bodies are locked, you're only allowed to read from the bodies and you can't change physics state.
  56. class ContactListener
  57. {
  58. public:
  59. /// Ensure virtual destructor
  60. virtual ~ContactListener() = default;
  61. /// Called after detecting a collision between a body pair, but before calling OnContactAdded and before adding the contact constraint.
  62. /// If the function rejects the contact, the contact will not be added and any other contacts between this body pair will not be processed.
  63. /// This function will only be called once per PhysicsSystem::Update per body pair and may not be called again the next update
  64. /// if a contact persists and no new contact pairs between sub shapes are found.
  65. /// This is a rather expensive time to reject a contact point since a lot of the collision detection has happened already, make sure you
  66. /// filter out the majority of undesired body pairs through the ObjectLayerPairFilter that is registered on the PhysicsSystem.
  67. /// Note that this callback is called when all bodies are locked, so don't use any locking functions!
  68. /// Body 1 will have a motion type that is larger or equal than body 2's motion type (order from large to small: dynamic -> kinematic -> static). When motion types are equal, they are ordered by BodyID.
  69. /// The collision result (inCollisionResult) is reported relative to inBaseOffset.
  70. virtual ValidateResult OnContactValidate([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] RVec3Arg inBaseOffset, [[maybe_unused]] const CollideShapeResult &inCollisionResult) { return ValidateResult::AcceptAllContactsForThisBodyPair; }
  71. /// Called whenever a new contact point is detected.
  72. /// Note that this callback is called when all bodies are locked, so don't use any locking functions!
  73. /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
  74. /// Note that only active bodies will report contacts, as soon as a body goes to sleep the contacts between that body and all other
  75. /// bodies will receive an OnContactRemoved callback, if this is the case then Body::IsActive() will return false during the callback.
  76. /// When contacts are added, the constraint solver has not run yet, so the collision impulse is unknown at that point.
  77. /// The velocities of inBody1 and inBody2 are the velocities before the contact has been resolved, so you can use this to
  78. /// estimate the collision impulse to e.g. determine the volume of the impact sound to play (see: EstimateCollisionResponse).
  79. virtual void OnContactAdded([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ }
  80. /// Called whenever a contact is detected that was also detected last update.
  81. /// Note that this callback is called when all bodies are locked, so don't use any locking functions!
  82. /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
  83. /// If the structure of the shape of a body changes between simulation steps (e.g. by adding/removing a child shape of a compound shape),
  84. /// it is possible that the same sub shape ID used to identify the removed child shape is now reused for a different child shape. The physics
  85. /// system cannot detect this, so may send a 'contact persisted' callback even though the contact is now on a different child shape. You can
  86. /// detect this by keeping the old shape (before adding/removing a part) around until the next PhysicsSystem::Update (when the OnContactPersisted
  87. /// callbacks are triggered) and resolving the sub shape ID against both the old and new shape to see if they still refer to the same child shape.
  88. virtual void OnContactPersisted([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ }
  89. /// Called whenever a contact was detected last update but is not detected anymore.
  90. /// Note that this callback is called when all bodies are locked, so don't use any locking functions!
  91. /// Note that we're using BodyID's since the bodies may have been removed at the time of callback.
  92. /// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
  93. /// The sub shape ID were created in the previous simulation step too, so if the structure of a shape changes (e.g. by adding/removing a child shape of a compound shape),
  94. /// the sub shape ID may not be valid / may not point to the same sub shape anymore.
  95. /// If you want to know if this is the last contact between the two bodies, use PhysicsSystem::WereBodiesInContact.
  96. virtual void OnContactRemoved([[maybe_unused]] const SubShapeIDPair &inSubShapePair) { /* Do nothing */ }
  97. };
  98. JPH_NAMESPACE_END