|
@@ -61,9 +61,15 @@ enum class ValidateResult
|
|
RejectAllContactsForThisBodyPair ///< Rejects this and any further contact points for this body pair
|
|
RejectAllContactsForThisBodyPair ///< Rejects this and any further contact points for this body pair
|
|
};
|
|
};
|
|
|
|
|
|
-/// A listener class that receives collision contact events.
|
|
|
|
-/// It can be registered with the ContactConstraintManager (or PhysicsSystem).
|
|
|
|
-/// 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.
|
|
|
|
|
|
+/// A listener class that receives collision contact events. It can be registered through PhysicsSystem::SetContactListener.
|
|
|
|
+/// Only a single contact listener can be registered. A common pattern is to create a contact listener that casts Body::GetUserData
|
|
|
|
+/// to a game object and then forwards the call to a handler specific for that game object.
|
|
|
|
+/// Typically this is done on both objects involved in a collision event.
|
|
|
|
+///
|
|
|
|
+/// Note that contact listener callbacks are called from multiple threads at the same time when all bodies are locked, this means you cannot
|
|
|
|
+/// use PhysicsSystem::GetBodyInterface / PhysicsSystem::GetBodyLockInterface but must use PhysicsSystem::GetBodyInterfaceNoLock / PhysicsSystem::GetBodyLockInterfaceNoLock instead.
|
|
|
|
+/// If you use a locking interface, the simulation will deadlock. You're only allowed to read from the bodies and you can't change physics state.
|
|
|
|
+///
|
|
/// During OnContactRemoved you cannot access the bodies at all, see the comments at that function.
|
|
/// During OnContactRemoved you cannot access the bodies at all, see the comments at that function.
|
|
class ContactListener
|
|
class ContactListener
|
|
{
|
|
{
|
|
@@ -72,29 +78,39 @@ public:
|
|
virtual ~ContactListener() = default;
|
|
virtual ~ContactListener() = default;
|
|
|
|
|
|
/// Called after detecting a collision between a body pair, but before calling OnContactAdded and before adding the contact constraint.
|
|
/// Called after detecting a collision between a body pair, but before calling OnContactAdded and before adding the contact constraint.
|
|
- /// If the function rejects the contact, the contact will not be added and any other contacts between this body pair will not be processed.
|
|
|
|
- /// This function will only be called once per PhysicsSystem::Update per body pair and may not be called again the next update
|
|
|
|
- /// if a contact persists and no new contact pairs between sub shapes are found.
|
|
|
|
|
|
+ /// If the function rejects the contact, the contact will not be processed by the simulation.
|
|
/// This is a rather expensive time to reject a contact point since a lot of the collision detection has happened already, make sure you
|
|
/// This is a rather expensive time to reject a contact point since a lot of the collision detection has happened already, make sure you
|
|
/// filter out the majority of undesired body pairs through the ObjectLayerPairFilter that is registered on the PhysicsSystem.
|
|
/// filter out the majority of undesired body pairs through the ObjectLayerPairFilter that is registered on the PhysicsSystem.
|
|
- /// Note that this callback is called when all bodies are locked, so don't use any locking functions!
|
|
|
|
|
|
+ ///
|
|
|
|
+ /// This function may not be called again the next update if a contact persists and no new contact pairs between sub shapes are found.
|
|
|
|
+ ///
|
|
|
|
+ /// Note that this callback is called when all bodies are locked, so don't use any locking functions! See detailed class description of ContactListener.
|
|
|
|
+ ///
|
|
/// 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.
|
|
/// 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.
|
|
|
|
+ ///
|
|
/// The collision result (inCollisionResult) is reported relative to inBaseOffset.
|
|
/// The collision result (inCollisionResult) is reported relative to inBaseOffset.
|
|
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; }
|
|
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; }
|
|
|
|
|
|
/// Called whenever a new contact point is detected.
|
|
/// Called whenever a new contact point is detected.
|
|
- /// Note that this callback is called when all bodies are locked, so don't use any locking functions!
|
|
|
|
|
|
+ ///
|
|
|
|
+ /// Note that this callback is called when all bodies are locked, so don't use any locking functions! See detailed class description of ContactListener.
|
|
|
|
+ ///
|
|
/// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
|
|
/// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
|
|
|
|
+ ///
|
|
/// Note that only active bodies will report contacts, as soon as a body goes to sleep the contacts between that body and all other
|
|
/// Note that only active bodies will report contacts, as soon as a body goes to sleep the contacts between that body and all other
|
|
/// bodies will receive an OnContactRemoved callback, if this is the case then Body::IsActive() will return false during the callback.
|
|
/// bodies will receive an OnContactRemoved callback, if this is the case then Body::IsActive() will return false during the callback.
|
|
|
|
+ ///
|
|
/// When contacts are added, the constraint solver has not run yet, so the collision impulse is unknown at that point.
|
|
/// When contacts are added, the constraint solver has not run yet, so the collision impulse is unknown at that point.
|
|
/// The velocities of inBody1 and inBody2 are the velocities before the contact has been resolved, so you can use this to
|
|
/// The velocities of inBody1 and inBody2 are the velocities before the contact has been resolved, so you can use this to
|
|
/// estimate the collision impulse to e.g. determine the volume of the impact sound to play (see: EstimateCollisionResponse).
|
|
/// estimate the collision impulse to e.g. determine the volume of the impact sound to play (see: EstimateCollisionResponse).
|
|
virtual void OnContactAdded([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ }
|
|
virtual void OnContactAdded([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ }
|
|
|
|
|
|
/// Called whenever a contact is detected that was also detected last update.
|
|
/// Called whenever a contact is detected that was also detected last update.
|
|
- /// Note that this callback is called when all bodies are locked, so don't use any locking functions!
|
|
|
|
|
|
+ ///
|
|
|
|
+ /// Note that this callback is called when all bodies are locked, so don't use any locking functions! See detailed class description of ContactListener.
|
|
|
|
+ ///
|
|
/// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
|
|
/// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
|
|
|
|
+ ///
|
|
/// 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),
|
|
/// 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),
|
|
/// 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
|
|
/// 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
|
|
/// system cannot detect this, so may send a 'contact persisted' callback even though the contact is now on a different child shape. You can
|
|
/// system cannot detect this, so may send a 'contact persisted' callback even though the contact is now on a different child shape. You can
|
|
@@ -103,14 +119,18 @@ public:
|
|
virtual void OnContactPersisted([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ }
|
|
virtual void OnContactPersisted([[maybe_unused]] const Body &inBody1, [[maybe_unused]] const Body &inBody2, [[maybe_unused]] const ContactManifold &inManifold, [[maybe_unused]] ContactSettings &ioSettings) { /* Do nothing */ }
|
|
|
|
|
|
/// Called whenever a contact was detected last update but is not detected anymore.
|
|
/// Called whenever a contact was detected last update but is not detected anymore.
|
|
|
|
+ ///
|
|
/// You cannot access the bodies at the time of this callback because:
|
|
/// You cannot access the bodies at the time of this callback because:
|
|
/// - All bodies are locked at the time of this callback.
|
|
/// - All bodies are locked at the time of this callback.
|
|
/// - Some properties of the bodies are being modified from another thread at the same time.
|
|
/// - Some properties of the bodies are being modified from another thread at the same time.
|
|
/// - The body may have been removed and destroyed (you'll receive an OnContactRemoved callback in the PhysicsSystem::Update after the body has been removed).
|
|
/// - The body may have been removed and destroyed (you'll receive an OnContactRemoved callback in the PhysicsSystem::Update after the body has been removed).
|
|
|
|
+ ///
|
|
/// Cache what you need in the OnContactAdded and OnContactPersisted callbacks and store it in a separate structure to use during this callback.
|
|
/// Cache what you need in the OnContactAdded and OnContactPersisted callbacks and store it in a separate structure to use during this callback.
|
|
- /// Alternatively, you could just record that the contact was removed and process it after PhysicsSimulation::Update.
|
|
|
|
|
|
+ /// Alternatively, you could just record that the contact was removed and process it after PhysicsSystem::Update.
|
|
|
|
+ ///
|
|
/// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
|
|
/// Body 1 and 2 will be sorted such that body 1 ID < body 2 ID, so body 1 may not be dynamic.
|
|
- /// 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),
|
|
|
|
|
|
+ ///
|
|
|
|
+ /// The sub shape IDs were created in the previous simulation step, so if the structure of a shape changes (e.g. by adding/removing a child shape of a compound shape),
|
|
/// the sub shape ID may not be valid / may not point to the same sub shape anymore.
|
|
/// the sub shape ID may not be valid / may not point to the same sub shape anymore.
|
|
/// If you want to know if this is the last contact between the two bodies, use PhysicsSystem::WereBodiesInContact.
|
|
/// If you want to know if this is the last contact between the two bodies, use PhysicsSystem::WereBodiesInContact.
|
|
virtual void OnContactRemoved([[maybe_unused]] const SubShapeIDPair &inSubShapePair) { /* Do nothing */ }
|
|
virtual void OnContactRemoved([[maybe_unused]] const SubShapeIDPair &inSubShapePair) { /* Do nothing */ }
|