diff --git a/CHANGELOG.md b/CHANGELOG.md index 27a22bc4..535ce246 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ - The RigidBody::getLocalInertiaTensor() method has been added to retrieve the local-space inertia tensor of a rigid body. - The RigidBody::updateMassFromColliders() method has been added to compute and set the mass of a body using its colliders - A Material nows has a mass density parameter that can be set using the Material::setMassDensity() method. The mass density is used to compute the mass of a collider when computing the mass of a rigid body + - A Collider can now be a trigger. This collider will be used to only report collisions with another collider but no collision response will be applied. You can use the Collider::setIsTrigger() method for this. + - The EventListener class now has a onTrigger() method that is called when a trigger collider is colling with another collider. + - In the EventListener, the onContact() and onTrigger() method now reports the type of event (start, stay, exit) for each contact. This way the user can know whether it's a new contact or not or when two colliders are not in contact anymore. ### Fixed diff --git a/include/reactphysics3d/collision/Collider.h b/include/reactphysics3d/collision/Collider.h index 041a3535..1abdb1d3 100644 --- a/include/reactphysics3d/collision/Collider.h +++ b/include/reactphysics3d/collision/Collider.h @@ -160,6 +160,12 @@ class Collider { /// Set a new material for this collider void setMaterial(const Material& material); + /// Return true if the collider is a trigger + bool getIsTrigger() const; + + /// Set whether the collider is a trigger + void setIsTrigger(bool isTrigger) const; + #ifdef IS_PROFILING_ACTIVE /// Set the profiler diff --git a/include/reactphysics3d/collision/CollisionCallback.h b/include/reactphysics3d/collision/CollisionCallback.h index 04c5dbc7..b6f29a76 100644 --- a/include/reactphysics3d/collision/CollisionCallback.h +++ b/include/reactphysics3d/collision/CollisionCallback.h @@ -108,6 +108,22 @@ class CollisionCallback { */ class ContactPair { + public: + + /// Enumeration EventType that describes the type of contact event + enum class EventType { + + /// This contact is a new contact between the two + /// colliders (the colliders where not touching in the previous frame) + ContactStart, + + /// The two colliders were already touching in the previous frame and this is a new or updated contact + ContactStay, + + /// The two colliders were in contact in the previous frame and are not in contact anymore + ContactExit + }; + private: // -------------------- Attributes -------------------- // @@ -120,11 +136,14 @@ class CollisionCallback { /// Reference to the physics world PhysicsWorld& mWorld; + /// True if this is a lost contact pair (contact pair colliding in previous frame but not in current one) + bool mIsLostContactPair; + // -------------------- Methods -------------------- // /// Constructor - ContactPair(const reactphysics3d::ContactPair& contactPair, List* contactPoints, - PhysicsWorld& world); + ContactPair(const reactphysics3d::ContactPair& contactPair, List* contactPoints, + PhysicsWorld& world, bool mIsLostContactPair); public: @@ -157,6 +176,9 @@ class CollisionCallback { /// Return a pointer to the second collider in contact (in body 2) Collider* getCollider2() const; + /// Return the corresponding type of event for this contact pair + EventType getEventType() const; + // -------------------- Friendship -------------------- // friend class CollisionCallback; @@ -172,7 +194,7 @@ class CollisionCallback { // -------------------- Attributes -------------------- // - /// Pointer to the list of contact pairs + /// Pointer to the list of contact pairs (contains contacts and triggers events) List* mContactPairs; /// Pointer to the list of contact manifolds @@ -181,6 +203,15 @@ class CollisionCallback { /// Pointer to the contact points List* mContactPoints; + /// Pointer to the list of lost contact pairs (contains contacts and triggers events) + List& mLostContactPairs; + + /// List of indices of the mContactPairs list that are contact events (not overlap/triggers) + List mContactPairsIndices; + + /// List of indices of the mLostContactPairs list that are contact events (not overlap/triggers) + List mLostContactPairsIndices; + /// Reference to the physics world PhysicsWorld& mWorld; @@ -188,7 +219,8 @@ class CollisionCallback { /// Constructor CallbackData(List* contactPairs, List* manifolds, - List* contactPoints, PhysicsWorld& world); + List* contactPoints, List& lostContactPairs, + PhysicsWorld& world); /// Deleted copy constructor CallbackData(const CallbackData& callbackData) = delete; @@ -226,7 +258,7 @@ class CollisionCallback { * @return The number of contact pairs */ inline uint CollisionCallback::CallbackData::getNbContactPairs() const { - return mContactPairs->size(); + return mContactPairsIndices.size() + mLostContactPairsIndices.size(); } // Return the number of contact points in the contact pair diff --git a/include/reactphysics3d/collision/ContactPair.h b/include/reactphysics3d/collision/ContactPair.h index 518c29de..2a661ea7 100644 --- a/include/reactphysics3d/collision/ContactPair.h +++ b/include/reactphysics3d/collision/ContactPair.h @@ -80,15 +80,21 @@ struct ContactPair { /// Total number of contact points in all the manifolds of the contact pair uint nbToTalContactPoints; + /// True if the colliders of the pair were already colliding in the previous frame + bool collidingInPreviousFrame; + + /// True if one of the two involved colliders is a trigger + bool isTrigger; + // -------------------- Methods -------------------- // /// Constructor ContactPair(uint64 pairId, Entity body1Entity, Entity body2Entity, Entity collider1Entity, - Entity collider2Entity, uint contactPairIndex, MemoryAllocator& allocator) + Entity collider2Entity, uint contactPairIndex, bool collidingInPreviousFrame, bool isTrigger, MemoryAllocator& allocator) : pairId(pairId), potentialContactManifoldsIndices(allocator), body1Entity(body1Entity), body2Entity(body2Entity), collider1Entity(collider1Entity), collider2Entity(collider2Entity), isAlreadyInIsland(false), contactPairIndex(contactPairIndex), contactManifoldsIndex(0), nbContactManifolds(0), - contactPointsIndex(0), nbToTalContactPoints(0) { + contactPointsIndex(0), nbToTalContactPoints(0), collidingInPreviousFrame(collidingInPreviousFrame), isTrigger(isTrigger) { } }; diff --git a/include/reactphysics3d/collision/OverlapCallback.h b/include/reactphysics3d/collision/OverlapCallback.h index 7b54dd27..6f3ba12e 100644 --- a/include/reactphysics3d/collision/OverlapCallback.h +++ b/include/reactphysics3d/collision/OverlapCallback.h @@ -28,6 +28,7 @@ // Libraries #include +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -53,20 +54,39 @@ class OverlapCallback { */ class OverlapPair { + public: + + /// Enumeration EventType that describes the type of overlapping event + enum class EventType { + + /// This overlap is a new overlap between the two + /// colliders (the colliders where not overlapping in the previous frame) + OverlapStart, + + /// The two colliders were already overlapping in the previous frame and this is a new or updated overlap + OverlapStay, + + /// The two colliders were overlapping in the previous frame and are not overlapping anymore + OverlapExit + }; + private: // -------------------- Attributes -------------------- // - /// Pair of overlapping body entities - Pair& mOverlapPair; + /// Contact pair + ContactPair& mContactPair; /// Reference to the physics world PhysicsWorld& mWorld; + /// True if the pair were overlapping in the previous frame but not in the current one + bool mIsLostOverlapPair; + // -------------------- Methods -------------------- // /// Constructor - OverlapPair(Pair& overlapPair, reactphysics3d::PhysicsWorld& world); + OverlapPair(ContactPair& contactPair, reactphysics3d::PhysicsWorld& world, bool isLostOverlappingPair); public: @@ -81,12 +101,21 @@ class OverlapCallback { /// Destructor ~OverlapPair() = default; + /// Return a pointer to the first collider in contact + Collider* getCollider1() const; + + /// Return a pointer to the second collider in contact + Collider* getCollider2() const; + /// Return a pointer to the first body in contact CollisionBody* getBody1() const; /// Return a pointer to the second body in contact CollisionBody* getBody2() const; + /// Return the corresponding type of event for this overlapping pair + EventType getEventType() const; + // -------------------- Friendship -------------------- // friend class OverlapCallback; @@ -102,7 +131,17 @@ class OverlapCallback { // -------------------- Attributes -------------------- // - List>& mOverlapBodies; + /// Reference to the list of contact pairs (contains contacts and triggers events) + List& mContactPairs; + + /// Reference to the list of lost contact pairs (contains contacts and triggers events) + List& mLostContactPairs; + + /// List of indices of the mContactPairs list that are overlap/triggers events (not contact events) + List mContactPairsIndices; + + /// List of indices of the mLostContactPairs list that are overlap/triggers events (not contact events) + List mLostContactPairsIndices; /// Reference to the physics world PhysicsWorld& mWorld; @@ -110,7 +149,7 @@ class OverlapCallback { // -------------------- Methods -------------------- // /// Constructor - CallbackData(List>& overlapColliders, PhysicsWorld& world); + CallbackData(List& contactPairs, List& lostContactPairs, PhysicsWorld& world); /// Deleted copy constructor CallbackData(const CallbackData& callbackData) = delete; @@ -147,7 +186,7 @@ class OverlapCallback { // Return the number of overlapping pairs of bodies inline uint OverlapCallback::CallbackData::getNbOverlappingPairs() const { - return mOverlapBodies.size(); + return mContactPairsIndices.size() + mLostContactPairsIndices.size(); } // Return a given overlapping pair of bodies @@ -158,7 +197,15 @@ inline OverlapCallback::OverlapPair OverlapCallback::CallbackData::getOverlappin assert(index < getNbOverlappingPairs()); - return OverlapPair(mOverlapBodies[index], mWorld); + if (index < mContactPairsIndices.size()) { + // Return a contact pair + return OverlapCallback::OverlapPair((mContactPairs)[mContactPairsIndices[index]], mWorld, false); + } + else { + + // Return a lost contact pair + return OverlapCallback::OverlapPair(mLostContactPairs[mLostContactPairsIndices[index - mContactPairsIndices.size()]], mWorld, true); + } } } diff --git a/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h b/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h index 7ad58b44..9e6d209f 100644 --- a/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h @@ -67,7 +67,7 @@ class CapsuleVsCapsuleAlgorithm : public NarrowPhaseAlgorithm { /// Compute the narrow-phase collision detection between two capsules bool testCollision(CapsuleVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, MemoryAllocator& memoryAllocator); + uint batchNbItems, MemoryAllocator& memoryAllocator); }; } diff --git a/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.h b/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.h index 8d628e37..fef287fc 100644 --- a/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.h +++ b/include/reactphysics3d/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.h @@ -58,18 +58,18 @@ struct CapsuleVsCapsuleNarrowPhaseInfoBatch : public NarrowPhaseInfoBatch { CapsuleVsCapsuleNarrowPhaseInfoBatch(MemoryAllocator& allocator, OverlappingPairs& overlappingPairs); /// Destructor - virtual ~CapsuleVsCapsuleNarrowPhaseInfoBatch() = default; + virtual ~CapsuleVsCapsuleNarrowPhaseInfoBatch() override = default; /// Add shapes to be tested during narrow-phase collision detection into the batch virtual void addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, const Transform& shape1Transform, - const Transform& shape2Transform); + const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator& shapeAllocator) override; // Initialize the containers using cached capacity - virtual void reserveMemory(); + virtual void reserveMemory() override; /// Clear all the objects in the batch - virtual void clear(); + virtual void clear() override; }; } diff --git a/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h b/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h index cce8396f..644bf7e5 100644 --- a/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h @@ -70,7 +70,7 @@ class CapsuleVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm { /// Compute the narrow-phase collision detection between a capsule and a polyhedron bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool clipWithPreviousAxisIfStillColliding, + uint batchNbItems, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator); }; diff --git a/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h b/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h index 9b7393e3..971816cc 100644 --- a/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h @@ -64,7 +64,7 @@ class ConvexPolyhedronVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm ConvexPolyhedronVsConvexPolyhedronAlgorithm& operator=(const ConvexPolyhedronVsConvexPolyhedronAlgorithm& algorithm) = delete; /// Compute the narrow-phase collision detection between two convex polyhedra - bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool reportContacts, + bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator); }; diff --git a/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h b/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h index bd7f72b5..ab1d605d 100644 --- a/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/GJK/GJKAlgorithm.h @@ -98,7 +98,7 @@ class GJKAlgorithm { /// Compute a contact info if the two bounding volumes collide. void testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, List& gjkResults, bool reportContacts); + uint batchNbItems, List& gjkResults); #ifdef IS_PROFILING_ACTIVE diff --git a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h index 1e0ac81f..b67bf19f 100644 --- a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h +++ b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h @@ -81,6 +81,9 @@ struct NarrowPhaseInfoBatch { /// List of transforms that maps from collision shape 2 local-space to world-space List shape2ToWorldTransforms; + /// True for each pair of objects that we need to report contacts (false for triggers for instance) + List reportContacts; + /// Result of the narrow-phase collision detection test List isColliding; @@ -103,9 +106,9 @@ struct NarrowPhaseInfoBatch { uint getNbObjects() const; /// Add shapes to be tested during narrow-phase collision detection into the batch - void addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, + virtual void addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, const Transform& shape1Transform, - const Transform& shape2Transform, MemoryAllocator& shapeAllocator); + const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator& shapeAllocator); /// Add a new contact point virtual void addContactPoint(uint index, const Vector3& contactNormal, decimal penDepth, diff --git a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h index 6d2bb759..38740997 100644 --- a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h +++ b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h @@ -61,6 +61,7 @@ class NarrowPhaseInput { NarrowPhaseInfoBatch mSphereVsConvexPolyhedronBatch; NarrowPhaseInfoBatch mCapsuleVsConvexPolyhedronBatch; NarrowPhaseInfoBatch mConvexPolyhedronVsConvexPolyhedronBatch; + public: /// Constructor @@ -69,7 +70,7 @@ class NarrowPhaseInput { /// Add shapes to be tested during narrow-phase collision detection into the batch void addNarrowPhaseTest(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, const Transform& shape1Transform, - const Transform& shape2Transform, NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, + const Transform& shape2Transform, NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, bool reportContacts, MemoryAllocator& shapeAllocator); /// Get a reference to the sphere vs sphere batch @@ -98,32 +99,32 @@ class NarrowPhaseInput { }; -// Get a reference to the sphere vs sphere batch +// Get a reference to the sphere vs sphere batch contacts inline SphereVsSphereNarrowPhaseInfoBatch& NarrowPhaseInput::getSphereVsSphereBatch() { return mSphereVsSphereBatch; } -// Get a reference to the sphere vs capsule batch +// Get a reference to the sphere vs capsule batch contacts inline SphereVsCapsuleNarrowPhaseInfoBatch& NarrowPhaseInput::getSphereVsCapsuleBatch() { return mSphereVsCapsuleBatch; } -// Get a reference to the capsule vs capsule batch +// Get a reference to the capsule vs capsule batch contacts inline CapsuleVsCapsuleNarrowPhaseInfoBatch& NarrowPhaseInput::getCapsuleVsCapsuleBatch() { return mCapsuleVsCapsuleBatch; } -// Get a reference to the sphere vs convex polyhedron batch +// Get a reference to the sphere vs convex polyhedron batch contacts inline NarrowPhaseInfoBatch& NarrowPhaseInput::getSphereVsConvexPolyhedronBatch() { return mSphereVsConvexPolyhedronBatch; } -// Get a reference to the capsule vs convex polyhedron batch +// Get a reference to the capsule vs convex polyhedron batch contacts inline NarrowPhaseInfoBatch& NarrowPhaseInput::getCapsuleVsConvexPolyhedronBatch() { return mCapsuleVsConvexPolyhedronBatch; } -// Get a reference to the convex polyhedron vs convex polyhedron batch +// Get a reference to the convex polyhedron vs convex polyhedron batch contacts inline NarrowPhaseInfoBatch& NarrowPhaseInput::getConvexPolyhedronVsConvexPolyhedronBatch() { return mConvexPolyhedronVsConvexPolyhedronBatch; } diff --git a/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h b/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h index ec5a5751..30479921 100644 --- a/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/SAT/SATAlgorithm.h @@ -154,11 +154,10 @@ class SATAlgorithm { /// Test collision between a sphere and a convex mesh bool testCollisionSphereVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, - uint batchStartIndex, uint batchNbItems, - bool reportContacts) const; + uint batchStartIndex, uint batchNbItems) const; /// Test collision between a capsule and a convex mesh - bool testCollisionCapsuleVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex, bool reportContacts) const; + bool testCollisionCapsuleVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex) const; /// Compute the two contact points between a polyhedron and a capsule when the separating axis is a face normal of the polyhedron bool computeCapsulePolyhedronFaceContactPoints(uint referenceFaceIndex, decimal capsuleRadius, const ConvexPolyhedronShape* polyhedron, @@ -172,8 +171,7 @@ class SATAlgorithm { const Vector3& edgeAdjacentFace2Normal) const; /// Test collision between two convex meshes - bool testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts) const; + bool testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems) const; #ifdef IS_PROFILING_ACTIVE diff --git a/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h b/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h index dbecf770..f531b352 100644 --- a/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleAlgorithm.h @@ -66,7 +66,7 @@ class SphereVsCapsuleAlgorithm : public NarrowPhaseAlgorithm { /// Compute the narrow-phase collision detection between a sphere and a capsule bool testCollision(SphereVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, MemoryAllocator& memoryAllocator); + uint batchNbItems, MemoryAllocator& memoryAllocator); }; } diff --git a/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.h b/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.h index 7781af01..4c1b45a8 100644 --- a/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.h +++ b/include/reactphysics3d/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.h @@ -58,18 +58,18 @@ struct SphereVsCapsuleNarrowPhaseInfoBatch : public NarrowPhaseInfoBatch { SphereVsCapsuleNarrowPhaseInfoBatch(MemoryAllocator& allocator, OverlappingPairs& overlappingPairs); /// Destructor - virtual ~SphereVsCapsuleNarrowPhaseInfoBatch() = default; + virtual ~SphereVsCapsuleNarrowPhaseInfoBatch() override = default; /// Add shapes to be tested during narrow-phase collision detection into the batch virtual void addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, const Transform& shape1Transform, - const Transform& shape2Transform); + const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator& shapeAllocator) override; // Initialize the containers using cached capacity - virtual void reserveMemory(); + virtual void reserveMemory() override; /// Clear all the objects in the batch - virtual void clear(); + virtual void clear() override; }; } diff --git a/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h b/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h index 5143d1ff..fabeeba3 100644 --- a/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h @@ -70,7 +70,7 @@ class SphereVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm { SphereVsConvexPolyhedronAlgorithm& operator=(const SphereVsConvexPolyhedronAlgorithm& algorithm) = delete; /// Compute the narrow-phase collision detection between a sphere and a convex polyhedron - bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool reportContacts, + bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator); }; diff --git a/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h b/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h index 7171c3cb..0f7f2a09 100644 --- a/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h +++ b/include/reactphysics3d/collision/narrowphase/SphereVsSphereAlgorithm.h @@ -66,7 +66,7 @@ class SphereVsSphereAlgorithm : public NarrowPhaseAlgorithm { /// Compute a contact info if the two bounding volume collide bool testCollision(SphereVsSphereNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, MemoryAllocator& memoryAllocator); + uint batchNbItems, MemoryAllocator& memoryAllocator); }; } diff --git a/include/reactphysics3d/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.h b/include/reactphysics3d/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.h index 25fa4773..73d040c6 100644 --- a/include/reactphysics3d/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.h +++ b/include/reactphysics3d/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.h @@ -55,9 +55,9 @@ struct SphereVsSphereNarrowPhaseInfoBatch : public NarrowPhaseInfoBatch { virtual ~SphereVsSphereNarrowPhaseInfoBatch() override = default; /// Add shapes to be tested during narrow-phase collision detection into the batch - virtual void addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, + virtual void addNarrowPhaseInfo(uint64 airId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, const Transform& shape1Transform, - const Transform& shape2Transform); + const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator& shapeAllocator) override; // Initialize the containers using cached capacity virtual void reserveMemory() override; diff --git a/include/reactphysics3d/components/ColliderComponents.h b/include/reactphysics3d/components/ColliderComponents.h index 16e51604..6f8cfa1c 100644 --- a/include/reactphysics3d/components/ColliderComponents.h +++ b/include/reactphysics3d/components/ColliderComponents.h @@ -96,6 +96,9 @@ class ColliderComponents : public Components { /// has been changed by the user bool* mHasCollisionShapeChangedSize; + /// True if the collider is a trigger + bool* mIsTrigger; + // -------------------- Methods -------------------- // @@ -192,9 +195,15 @@ class ColliderComponents : public Components { /// Return true if the size of collision shape of the collider has been changed by the user bool getHasCollisionShapeChangedSize(Entity colliderEntity) const; - /// Return true if the size of collision shape of the collider has been changed by the user + /// Set whether the size of collision shape of the collider has been changed by the user void setHasCollisionShapeChangedSize(Entity colliderEntity, bool hasCollisionShapeChangedSize); + /// Return true if a collider is a trigger + bool getIsTrigger(Entity colliderEntity) const; + + /// Set whether a collider is a trigger + void setIsTrigger(Entity colliderEntity, bool isTrigger); + // -------------------- Friendship -------------------- // friend class BroadPhaseSystem; @@ -331,6 +340,23 @@ inline void ColliderComponents::setHasCollisionShapeChangedSize(Entity colliderE mHasCollisionShapeChangedSize[mMapEntityToComponentIndex[colliderEntity]] = hasCollisionShapeChangedSize; } + +// Return true if a collider is a trigger +inline bool ColliderComponents::getIsTrigger(Entity colliderEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(colliderEntity)); + + return mIsTrigger[mMapEntityToComponentIndex[colliderEntity]]; +} + +// Set whether a collider is a trigger +inline void ColliderComponents::setIsTrigger(Entity colliderEntity, bool isTrigger) { + + assert(mMapEntityToComponentIndex.containsKey(colliderEntity)); + + mIsTrigger[mMapEntityToComponentIndex[colliderEntity]] = isTrigger; +} + } #endif diff --git a/include/reactphysics3d/components/RigidBodyComponents.h b/include/reactphysics3d/components/RigidBodyComponents.h index 826b706b..a618b896 100644 --- a/include/reactphysics3d/components/RigidBodyComponents.h +++ b/include/reactphysics3d/components/RigidBodyComponents.h @@ -110,7 +110,6 @@ class RigidBodyComponents : public Components { Vector3* mLocalInertiaTensors; /// Array with the inverse of the inertia tensor of each component - // TODO : We should use a Vector3 here for the diagonal instead of a Matrix3x3 Vector3* mInverseInertiaTensorsLocal; /// Array with the constrained linear velocity of each component diff --git a/include/reactphysics3d/engine/EventListener.h b/include/reactphysics3d/engine/EventListener.h index 0db10123..e797b58c 100644 --- a/include/reactphysics3d/engine/EventListener.h +++ b/include/reactphysics3d/engine/EventListener.h @@ -28,6 +28,7 @@ // Libraries #include +#include namespace reactphysics3d { @@ -54,6 +55,12 @@ class EventListener : public CollisionCallback { * @param collisionInfo Information about the contacts */ virtual void onContact(const CollisionCallback::CallbackData& callbackData) override {} + + /// Called when some trigger events occur + virtual void onTrigger(const OverlapCallback::CallbackData& callbackData) {} + + /// Called to report rigid bodies that started to sleep or has woken up in the previous frame + virtual void onSleep() {} }; } diff --git a/include/reactphysics3d/engine/OverlappingPairs.h b/include/reactphysics3d/engine/OverlappingPairs.h index 676ccc22..35a5342f 100644 --- a/include/reactphysics3d/engine/OverlappingPairs.h +++ b/include/reactphysics3d/engine/OverlappingPairs.h @@ -173,6 +173,12 @@ class OverlappingPairs { /// True if the first shape of the pair is convex bool* mIsShape1Convex; + /// True if the colliders of the overlapping pair were colliding in the previous frame + bool* mCollidingInPreviousFrame; + + /// True if the colliders of the overlapping pair are colliding in the current frame + bool* mCollidingInCurrentFrame; + /// Reference to the colliders components ColliderComponents& mColliderComponents; @@ -276,12 +282,21 @@ class OverlappingPairs { /// Delete all the obsolete last frame collision info void clearObsoleteLastFrameCollisionInfos(); + /// Set the collidingInPreviousFrame value with the collidinginCurrentFrame value for each pair + void updateCollidingInPreviousFrame(); + /// Return the pair of bodies index of the pair static bodypair computeBodiesIndexPair(Entity body1Entity, Entity body2Entity); /// Set if we need to test a given pair for overlap void setNeedToTestOverlap(uint64 pairId, bool needToTestOverlap); + /// Return true if the two colliders of the pair were already colliding the previous frame + bool getCollidingInPreviousFrame(uint64 pairId) const; + + /// Set to true if the two colliders of the pair were already colliding the previous frame + void setCollidingInPreviousFrame(uint64 pairId, bool wereCollidingInPreviousFrame); + #ifdef IS_PROFILING_ACTIVE /// Set the profiler @@ -380,6 +395,18 @@ inline void OverlappingPairs::setNeedToTestOverlap(uint64 pairId, bool needToTes mNeedToTestOverlap[mMapPairIdToPairIndex[pairId]] = needToTestOverlap; } +// Return true if the two colliders of the pair were already colliding the previous frame +inline bool OverlappingPairs::getCollidingInPreviousFrame(uint64 pairId) const { + assert(mMapPairIdToPairIndex.containsKey(pairId)); + return mCollidingInPreviousFrame[mMapPairIdToPairIndex[pairId]]; +} + +// Set to true if the two colliders of the pair were already colliding the previous frame +inline void OverlappingPairs::setCollidingInPreviousFrame(uint64 pairId, bool wereCollidingInPreviousFrame) { + assert(mMapPairIdToPairIndex.containsKey(pairId)); + mCollidingInPreviousFrame[mMapPairIdToPairIndex[pairId]] = wereCollidingInPreviousFrame; +} + #ifdef IS_PROFILING_ACTIVE // Set the profiler diff --git a/include/reactphysics3d/engine/PhysicsWorld.h b/include/reactphysics3d/engine/PhysicsWorld.h index e6092c28..6ddcdb63 100644 --- a/include/reactphysics3d/engine/PhysicsWorld.h +++ b/include/reactphysics3d/engine/PhysicsWorld.h @@ -481,6 +481,8 @@ class PhysicsWorld { friend class FixedJoint; friend class HingeJoint; friend class SliderJoint; + friend class CollisionCallback::CallbackData; + friend class OverlapCallback::CallbackData; }; // Set the collision dispatch configuration diff --git a/include/reactphysics3d/systems/CollisionDetectionSystem.h b/include/reactphysics3d/systems/CollisionDetectionSystem.h index 3a6e467c..08057cfb 100644 --- a/include/reactphysics3d/systems/CollisionDetectionSystem.h +++ b/include/reactphysics3d/systems/CollisionDetectionSystem.h @@ -122,6 +122,9 @@ class CollisionDetectionSystem { /// Pointer to the list of contact pairs of the current frame (either mContactPairs1 or mContactPairs2) List* mCurrentContactPairs; + /// List of lost contact pairs (contact pairs in contact in previous frame but not in the current one) + List mLostContactPairs; + /// First map of overlapping pair id to the index of the corresponding pair contact Map mMapPairIdToContactPairIndex1; @@ -176,10 +179,11 @@ class CollisionDetectionSystem { void computeBroadPhase(); /// Compute the middle-phase collision detection - void computeMiddlePhase(NarrowPhaseInput& narrowPhaseInput); + void computeMiddlePhase(NarrowPhaseInput& narrowPhaseInput, bool needToReportContacts); // Compute the middle-phase collision detection - void computeMiddlePhaseCollisionSnapshot(List& convexPairs, List& concavePairs, NarrowPhaseInput& narrowPhaseInput); + void computeMiddlePhaseCollisionSnapshot(List& convexPairs, List& concavePairs, NarrowPhaseInput& narrowPhaseInput, + bool reportContacts); /// Compute the narrow-phase collision detection void computeNarrowPhase(); @@ -191,10 +195,11 @@ class CollisionDetectionSystem { bool computeNarrowPhaseCollisionSnapshot(NarrowPhaseInput& narrowPhaseInput, CollisionCallback& callback); /// Process the potential contacts after narrow-phase collision detection - void computeSnapshotContactPairs(NarrowPhaseInput& narrowPhaseInput, List>& overlapPairs) const; + void computeOverlapSnapshotContactPairs(NarrowPhaseInput& narrowPhaseInput, List& contactPairs) const; /// Convert the potential contact into actual contacts - void computeSnapshotContactPairs(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, List>& overlapPairs) const; + void computeOverlapSnapshotContactPairs(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, List& contactPairs, + Set& setOverlapContactPairId) const; /// Take a list of overlapping nodes in the broad-phase and create new overlapping pairs if necessary void updateOverlappingPairs(const List >& overlappingNodes); @@ -202,8 +207,11 @@ class CollisionDetectionSystem { /// Remove pairs that are not overlapping anymore void removeNonOverlappingPairs(); + /// Add a lost contact pair (pair of colliders that are not in contact anymore) + void addLostContactPair(uint64 overlappingPairIndex); + /// Execute the narrow-phase collision detection algorithm on batches - bool testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool reportContacts, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& allocator); + bool testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& allocator); /// Compute the concave vs convex middle-phase algorithm for a given pair of bodies void computeConvexVsConcaveMiddlePhase(uint64 pairIndex, MemoryAllocator& allocator, @@ -222,7 +230,7 @@ class CollisionDetectionSystem { /// Process the potential contacts after narrow-phase collision detection void processAllPotentialContacts(NarrowPhaseInput& narrowPhaseInput, bool updateLastFrameInfo, List& potentialContactPoints, Map* mapPairIdToContactPairIndex, - List &potentialContactManifolds, List* contactPairs, + List& potentialContactManifolds, List* contactPairs, Map>& mapBodyToContactPairs); /// Reduce the potential contact manifolds and contact points of the overlapping pair contacts @@ -232,6 +240,9 @@ class CollisionDetectionSystem { /// Create the actual contact manifolds and contacts points (from potential contacts) for a given contact pair void createContacts(); + /// Compute the lost contact pairs (contact pairs in contact in the previous frame but not in the current one) + void computeLostContactPairs(); + /// Create the actual contact manifolds and contacts points for testCollision() methods void createSnapshotContacts(List& contactPairs, List &contactManifolds, List& contactPoints, @@ -247,7 +258,10 @@ class CollisionDetectionSystem { /// Report contacts void reportContacts(CollisionCallback& callback, List* contactPairs, - List* manifolds, List* contactPoints); + List* manifolds, List* contactPoints, List& lostContactPairs); + + /// Report all triggers + void reportTriggers(EventListener& eventListener, List* contactPairs, List& lostContactPairs); /// Return the largest depth of all the contact points of a potential manifold decimal computePotentialManifoldLargestContactDepth(const ContactManifoldInfo& manifold, @@ -307,8 +321,8 @@ class CollisionDetectionSystem { /// Notify that the overlapping pairs where a given collider is involved need to be tested for overlap void notifyOverlappingPairsToTestOverlap(Collider* collider); - /// Report contacts - void reportContacts(); + /// Report contacts and triggers + void reportContactsAndTriggers(); /// Compute the collision detection void computeCollisionDetection(); diff --git a/src/collision/Collider.cpp b/src/collision/Collider.cpp index d4193baa..ce11510d 100644 --- a/src/collision/Collider.cpp +++ b/src/collision/Collider.cpp @@ -224,6 +224,16 @@ const Transform Collider::getLocalToWorldTransform() const { return mBody->mWorld.mCollidersComponents.getLocalToWorldTransform(mEntity); } +// Return true if the collider is a trigger +bool Collider::getIsTrigger() const { + return mBody->mWorld.mCollidersComponents.getIsTrigger(mEntity); +} + +// Set whether the collider is a trigger +void Collider::setIsTrigger(bool isTrigger) const { + mBody->mWorld.mCollidersComponents.setIsTrigger(mEntity, isTrigger); +} + #ifdef IS_PROFILING_ACTIVE // Set the profiler diff --git a/src/collision/CollisionCallback.cpp b/src/collision/CollisionCallback.cpp index 58507b6c..81825d00 100644 --- a/src/collision/CollisionCallback.cpp +++ b/src/collision/CollisionCallback.cpp @@ -39,9 +39,9 @@ CollisionCallback::ContactPoint::ContactPoint(const reactphysics3d::ContactPoint // Contact Pair Constructor CollisionCallback::ContactPair::ContactPair(const reactphysics3d::ContactPair& contactPair, - List* contactPoints, PhysicsWorld& world) + List* contactPoints, PhysicsWorld& world, bool isLostContactPair) :mContactPair(contactPair), mContactPoints(contactPoints), - mWorld(world) { + mWorld(world), mIsLostContactPair(isLostContactPair) { } @@ -65,11 +65,38 @@ Collider* CollisionCallback::ContactPair::getCollider2() const { return mWorld.mCollidersComponents.getCollider(mContactPair.collider2Entity); } +// Return the corresponding type of event for this contact pair +CollisionCallback::ContactPair::EventType CollisionCallback::ContactPair::getEventType() const { + + if (mIsLostContactPair) return EventType::ContactExit; + + if (mContactPair.collidingInPreviousFrame) return EventType::ContactStay; + + return EventType::ContactStart; +} + // CollisionCallbackInfo Constructor CollisionCallback::CallbackData::CallbackData(List* contactPairs, List* manifolds, - List* contactPoints, PhysicsWorld &world) - :mContactPairs(contactPairs), mContactManifolds(manifolds), mContactPoints(contactPoints), mWorld(world) { + List* contactPoints, List& lostContactPairs, PhysicsWorld& world) + :mContactPairs(contactPairs), mContactManifolds(manifolds), mContactPoints(contactPoints), mLostContactPairs(lostContactPairs), + mContactPairsIndices(world.mMemoryManager.getHeapAllocator()), mLostContactPairsIndices(world.mMemoryManager.getHeapAllocator()), mWorld(world) { + // Filter the contact pairs to only keep the contact events (not the overlap/trigger events) + for (uint i=0; i < mContactPairs->size(); i++) { + + // If the contact pair contains contacts (and is therefore not an overlap/trigger event) + if (!(*mContactPairs)[i].isTrigger) { + mContactPairsIndices.add(i); + } + } + // Filter the lost contact pairs to only keep the contact events (not the overlap/trigger events) + for (uint i=0; i < mLostContactPairs.size(); i++) { + + // If the contact pair contains contacts (and is therefore not an overlap/trigger event) + if (!mLostContactPairs[i].isTrigger) { + mLostContactPairsIndices.add(i); + } + } } // Return a given contact point of the contact pair @@ -91,5 +118,13 @@ CollisionCallback::ContactPair CollisionCallback::CallbackData::getContactPair(u assert(index < getNbContactPairs()); - return CollisionCallback::ContactPair((*mContactPairs)[index], mContactPoints, mWorld); + if (index < mContactPairsIndices.size()) { + // Return a contact pair + return CollisionCallback::ContactPair((*mContactPairs)[mContactPairsIndices[index]], mContactPoints, mWorld, false); + } + else { + + // Return a lost contact pair + return CollisionCallback::ContactPair(mLostContactPairs[mLostContactPairsIndices[index - mContactPairsIndices.size()]], mContactPoints, mWorld, true); + } } diff --git a/src/collision/OverlapCallback.cpp b/src/collision/OverlapCallback.cpp index b0448486..af5f17ff 100644 --- a/src/collision/OverlapCallback.cpp +++ b/src/collision/OverlapCallback.cpp @@ -31,23 +31,60 @@ using namespace reactphysics3d; // Contact Pair Constructor -OverlapCallback::OverlapPair::OverlapPair(Pair& overlapPair, PhysicsWorld& world) - : mOverlapPair(overlapPair), mWorld(world) { +OverlapCallback::OverlapPair::OverlapPair(ContactPair& contactPair, PhysicsWorld& world, bool isLostOverlappingPair) + : mContactPair(contactPair), mWorld(world), mIsLostOverlapPair(isLostOverlappingPair) { } +// Return a pointer to the first collider in contact +Collider* OverlapCallback::OverlapPair::getCollider1() const { + return static_cast(mWorld.mCollidersComponents.getCollider(mContactPair.collider1Entity)); +} + +// Return a pointer to the second collider in contact +Collider* OverlapCallback::OverlapPair::getCollider2() const { + return static_cast(mWorld.mCollidersComponents.getCollider(mContactPair.collider2Entity)); +} + // Return a pointer to the first body in contact CollisionBody* OverlapCallback::OverlapPair::getBody1() const { - return static_cast(mWorld.mCollisionBodyComponents.getBody(mOverlapPair.first)); + return static_cast(mWorld.mCollisionBodyComponents.getBody(mContactPair.body1Entity)); } // Return a pointer to the second body in contact CollisionBody* OverlapCallback::OverlapPair::getBody2() const { - return static_cast(mWorld.mCollisionBodyComponents.getBody(mOverlapPair.second)); + return static_cast(mWorld.mCollisionBodyComponents.getBody(mContactPair.body2Entity)); +} + +// Return the corresponding type of event for this overlapping pair +OverlapCallback::OverlapPair::EventType OverlapCallback::OverlapPair::getEventType() const { + + if (mIsLostOverlapPair) return EventType::OverlapExit; + + if (mContactPair.collidingInPreviousFrame) return EventType::OverlapStay; + + return EventType::OverlapStart; } // CollisionCallbackData Constructor -OverlapCallback::CallbackData::CallbackData(List>& overlapColliders, PhysicsWorld& world) - :mOverlapBodies(overlapColliders), mWorld(world) { +OverlapCallback::CallbackData::CallbackData(List& contactPairs, List& lostContactPairs, PhysicsWorld& world) + :mContactPairs(contactPairs), mLostContactPairs(lostContactPairs), + mContactPairsIndices(world.mMemoryManager.getHeapAllocator()), mLostContactPairsIndices(world.mMemoryManager.getHeapAllocator()), mWorld(world) { + // Filter the contact pairs to only keep the overlap/trigger events (not the contact events) + for (uint i=0; i < mContactPairs.size(); i++) { + + // If the contact pair contains contacts (and is therefore not an overlap/trigger event) + if (mContactPairs[i].isTrigger) { + mContactPairsIndices.add(i); + } + } + // Filter the lost contact pairs to only keep the overlap/trigger events (not the contact events) + for (uint i=0; i < mLostContactPairs.size(); i++) { + + // If the contact pair contains contacts (and is therefore not an overlap/trigger event) + if (mLostContactPairs[i].isTrigger) { + mLostContactPairsIndices.add(i); + } + } } diff --git a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp index 7f69964c..4def6d9d 100755 --- a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp +++ b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp @@ -35,7 +35,7 @@ using namespace reactphysics3d; // This technique is based on the "Robust Contact Creation for Physics Simulations" presentation // by Dirk Gregorius. bool CapsuleVsCapsuleAlgorithm::testCollision(CapsuleVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, MemoryAllocator& memoryAllocator) { + MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; @@ -95,7 +95,7 @@ bool CapsuleVsCapsuleAlgorithm::testCollision(CapsuleVsCapsuleNarrowPhaseInfoBat // If the segments were overlapping (the clip segment is valid) if (t1 > decimal(0.0) && t2 > decimal(0.0)) { - if (reportContacts) { + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { // Clip the inner segment of capsule 2 if (t1 > decimal(1.0)) t1 = decimal(1.0); @@ -171,7 +171,7 @@ bool CapsuleVsCapsuleAlgorithm::testCollision(CapsuleVsCapsuleNarrowPhaseInfoBat // If the collision shapes overlap if (closestPointsDistanceSquare < sumRadius * sumRadius) { - if (reportContacts) { + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { // If the distance between the inner segments is not zero if (closestPointsDistanceSquare > MACHINE_EPSILON) { diff --git a/src/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.cpp b/src/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.cpp index c8780c02..48d772d9 100644 --- a/src/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.cpp +++ b/src/collision/narrowphase/CapsuleVsCapsuleNarrowPhaseInfoBatch.cpp @@ -39,7 +39,10 @@ CapsuleVsCapsuleNarrowPhaseInfoBatch::CapsuleVsCapsuleNarrowPhaseInfoBatch(Memor // Add shapes to be tested during narrow-phase collision detection into the batch void CapsuleVsCapsuleNarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, - const Transform& shape1Transform, const Transform& shape2Transform) { + const Transform& shape1Transform, const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator &shapeAllocator) { + + NarrowPhaseInfoBatch::addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, + shape2Transform, needToReportContacts, shapeAllocator); assert(shape1->getType() == CollisionShapeType::CAPSULE); assert(shape2->getType() == CollisionShapeType::CAPSULE); @@ -47,34 +50,16 @@ void CapsuleVsCapsuleNarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uin const CapsuleShape* capsule1 = static_cast(shape1); const CapsuleShape* capsule2 = static_cast(shape2); - colliderEntities1.add(collider1); - colliderEntities2.add(collider2); capsule1Radiuses.add(capsule1->getRadius()); capsule2Radiuses.add(capsule2->getRadius()); capsule1Heights.add(capsule1->getHeight()); capsule2Heights.add(capsule2->getHeight()); - shape1ToWorldTransforms.add(shape1Transform); - shape2ToWorldTransforms.add(shape2Transform); - overlappingPairIds.add(pairId); - contactPoints.add(List(mMemoryAllocator)); - isColliding.add(false); - - // Add a collision info for the two collision shapes into the overlapping pair (if not present yet) - LastFrameCollisionInfo* lastFrameInfo = mOverlappingPairs.addLastFrameInfoIfNecessary(pairIndex, shape1->getId(), shape2->getId()); - lastFrameCollisionInfos.add(lastFrameInfo); } // Initialize the containers using cached capacity void CapsuleVsCapsuleNarrowPhaseInfoBatch::reserveMemory() { - overlappingPairIds.reserve(mCachedCapacity); - colliderEntities1.reserve(mCachedCapacity); - colliderEntities2.reserve(mCachedCapacity); - shape1ToWorldTransforms.reserve(mCachedCapacity); - shape2ToWorldTransforms.reserve(mCachedCapacity); - lastFrameCollisionInfos.reserve(mCachedCapacity); - isColliding.reserve(mCachedCapacity); - contactPoints.reserve(mCachedCapacity); + NarrowPhaseInfoBatch::reserveMemory(); capsule1Radiuses.reserve(mCachedCapacity); capsule2Radiuses.reserve(mCachedCapacity); @@ -90,16 +75,7 @@ void CapsuleVsCapsuleNarrowPhaseInfoBatch::clear() { // allocated in the next frame at a possibly different location in memory (remember that the // location of the allocated memory of a single frame allocator might change between two frames) - mCachedCapacity = overlappingPairIds.size(); - - overlappingPairIds.clear(true); - colliderEntities1.clear(true); - colliderEntities2.clear(true); - shape1ToWorldTransforms.clear(true); - shape2ToWorldTransforms.clear(true); - lastFrameCollisionInfos.clear(true); - isColliding.clear(true); - contactPoints.clear(true); + NarrowPhaseInfoBatch::clear(); capsule1Radiuses.clear(true); capsule2Radiuses.clear(true); diff --git a/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp b/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp index 0342487b..efcaf3c3 100644 --- a/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp +++ b/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp @@ -41,7 +41,7 @@ using namespace reactphysics3d; // by Dirk Gregorius. bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool clipWithPreviousAxisIfStillColliding, + bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; @@ -59,7 +59,7 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar // Run the GJK algorithm List gjkResults(memoryAllocator); - gjkAlgorithm.testCollision(narrowPhaseInfoBatch, batchStartIndex, batchNbItems, gjkResults, reportContacts); + gjkAlgorithm.testCollision(narrowPhaseInfoBatch, batchStartIndex, batchNbItems, gjkResults); assert(gjkResults.size() == batchNbItems); for (uint batchIndex = batchStartIndex; batchIndex < batchStartIndex + batchNbItems; batchIndex++) { @@ -78,7 +78,8 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar // If we have found a contact point inside the margins (shallow penetration) if (gjkResults[batchIndex] == GJKAlgorithm::GJKResult::COLLIDE_IN_MARGIN) { - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { bool noContact = false; @@ -173,7 +174,7 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar if (gjkResults[batchIndex] == GJKAlgorithm::GJKResult::INTERPENETRATE) { // Run the SAT algorithm to find the separating axis and compute contact point - narrowPhaseInfoBatch.isColliding[batchIndex] = satAlgorithm.testCollisionCapsuleVsConvexPolyhedron(narrowPhaseInfoBatch, batchIndex, reportContacts); + narrowPhaseInfoBatch.isColliding[batchIndex] = satAlgorithm.testCollisionCapsuleVsConvexPolyhedron(narrowPhaseInfoBatch, batchIndex); lastFrameCollisionInfo->wasUsingGJK = false; lastFrameCollisionInfo->wasUsingSAT = true; diff --git a/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp b/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp index ac4791c1..7cea8c2b 100644 --- a/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp +++ b/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp @@ -37,7 +37,7 @@ using namespace reactphysics3d; // by Dirk Gregorius. bool ConvexPolyhedronVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { + bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { // Run the SAT algorithm to find the separating axis and compute contact point SATAlgorithm satAlgorithm(clipWithPreviousAxisIfStillColliding, memoryAllocator); @@ -48,8 +48,7 @@ bool ConvexPolyhedronVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoB #endif - bool isCollisionFound = satAlgorithm.testCollisionConvexPolyhedronVsConvexPolyhedron(narrowPhaseInfoBatch, batchStartIndex, - batchNbItems, reportContacts); + bool isCollisionFound = satAlgorithm.testCollisionConvexPolyhedronVsConvexPolyhedron(narrowPhaseInfoBatch, batchStartIndex, batchNbItems); for (uint batchIndex = batchStartIndex; batchIndex < batchStartIndex + batchNbItems; batchIndex++) { diff --git a/src/collision/narrowphase/GJK/GJKAlgorithm.cpp b/src/collision/narrowphase/GJK/GJKAlgorithm.cpp index ff39943d..30b30e60 100644 --- a/src/collision/narrowphase/GJK/GJKAlgorithm.cpp +++ b/src/collision/narrowphase/GJK/GJKAlgorithm.cpp @@ -48,7 +48,7 @@ using namespace reactphysics3d; /// origin, they we give that simplex polytope to the EPA algorithm which will compute /// the correct penetration depth and contact points between the enlarged objects. void GJKAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, List& gjkResults, bool reportContacts) { + uint batchNbItems, List& gjkResults) { RP3D_PROFILE("GJKAlgorithm::testCollision()", mProfiler); @@ -214,7 +214,8 @@ void GJKAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uin continue; } - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle TriangleShape::computeSmoothTriangleMeshContact(shape1, shape2, pA, pB, transform1, transform2, diff --git a/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp b/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp index f7a5ce21..56f01f98 100644 --- a/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp +++ b/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp @@ -36,7 +36,7 @@ using namespace reactphysics3d; NarrowPhaseInfoBatch::NarrowPhaseInfoBatch(MemoryAllocator& allocator, OverlappingPairs& overlappingPairs) : mMemoryAllocator(allocator), mOverlappingPairs(overlappingPairs), overlappingPairIds(allocator), colliderEntities1(allocator), colliderEntities2(allocator), collisionShapes1(allocator), collisionShapes2(allocator), - shape1ToWorldTransforms(allocator), shape2ToWorldTransforms(allocator), + shape1ToWorldTransforms(allocator), shape2ToWorldTransforms(allocator), reportContacts(allocator), isColliding(allocator), contactPoints(allocator), collisionShapeAllocators(allocator), lastFrameCollisionInfos(allocator) { @@ -49,7 +49,7 @@ NarrowPhaseInfoBatch::~NarrowPhaseInfoBatch() { // Add shapes to be tested during narrow-phase collision detection into the batch void NarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, - const Transform& shape1Transform, const Transform& shape2Transform, + const Transform& shape1Transform, const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator& shapeAllocator) { overlappingPairIds.add(pairId); @@ -59,6 +59,7 @@ void NarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, E collisionShapes2.add(shape2); shape1ToWorldTransforms.add(shape1Transform); shape2ToWorldTransforms.add(shape2Transform); + reportContacts.add(needToReportContacts); collisionShapeAllocators.add(&shapeAllocator); contactPoints.add(List(mMemoryAllocator)); isColliding.add(false); @@ -72,6 +73,7 @@ void NarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, E void NarrowPhaseInfoBatch::addContactPoint(uint index, const Vector3& contactNormal, decimal penDepth, const Vector3& localPt1, const Vector3& localPt2) { + assert(reportContacts[index]); assert(penDepth > decimal(0.0)); // Get the memory allocator @@ -116,6 +118,7 @@ void NarrowPhaseInfoBatch::reserveMemory() { collisionShapes2.reserve(mCachedCapacity); shape1ToWorldTransforms.reserve(mCachedCapacity); shape2ToWorldTransforms.reserve(mCachedCapacity); + reportContacts.reserve(mCachedCapacity); collisionShapeAllocators.reserve(mCachedCapacity); lastFrameCollisionInfos.reserve(mCachedCapacity); isColliding.reserve(mCachedCapacity); @@ -155,6 +158,7 @@ void NarrowPhaseInfoBatch::clear() { collisionShapes2.clear(true); shape1ToWorldTransforms.clear(true); shape2ToWorldTransforms.clear(true); + reportContacts.clear(true); collisionShapeAllocators.clear(true); lastFrameCollisionInfos.clear(true); isColliding.clear(true); diff --git a/src/collision/narrowphase/NarrowPhaseInput.cpp b/src/collision/narrowphase/NarrowPhaseInput.cpp index aff929ed..3ca8d4b6 100644 --- a/src/collision/narrowphase/NarrowPhaseInput.cpp +++ b/src/collision/narrowphase/NarrowPhaseInput.cpp @@ -41,26 +41,26 @@ NarrowPhaseInput::NarrowPhaseInput(MemoryAllocator& allocator, OverlappingPairs& // Add shapes to be tested during narrow-phase collision detection into the batch void NarrowPhaseInput::addNarrowPhaseTest(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, const Transform& shape1Transform, const Transform& shape2Transform, - NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, MemoryAllocator& shapeAllocator) { + NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, bool reportContacts, MemoryAllocator& shapeAllocator) { switch (narrowPhaseAlgorithmType) { case NarrowPhaseAlgorithmType::SphereVsSphere: - mSphereVsSphereBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform); + mSphereVsSphereBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator); break; case NarrowPhaseAlgorithmType::SphereVsCapsule: - mSphereVsCapsuleBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform); + mSphereVsCapsuleBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator); break; case NarrowPhaseAlgorithmType::CapsuleVsCapsule: - mCapsuleVsCapsuleBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform); + mCapsuleVsCapsuleBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator); break; case NarrowPhaseAlgorithmType::SphereVsConvexPolyhedron: - mSphereVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, shapeAllocator); + mSphereVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator); break; case NarrowPhaseAlgorithmType::CapsuleVsConvexPolyhedron: - mCapsuleVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, shapeAllocator); + mCapsuleVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator); break; case NarrowPhaseAlgorithmType::ConvexPolyhedronVsConvexPolyhedron: - mConvexPolyhedronVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, shapeAllocator); + mConvexPolyhedronVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator); break; case NarrowPhaseAlgorithmType::None: // Must never happen diff --git a/src/collision/narrowphase/SAT/SATAlgorithm.cpp b/src/collision/narrowphase/SAT/SATAlgorithm.cpp index e9c89853..69b0c236 100644 --- a/src/collision/narrowphase/SAT/SATAlgorithm.cpp +++ b/src/collision/narrowphase/SAT/SATAlgorithm.cpp @@ -55,8 +55,7 @@ SATAlgorithm::SATAlgorithm(bool clipWithPreviousAxisIfStillColliding, MemoryAllo // Test collision between a sphere and a convex mesh bool SATAlgorithm::testCollisionSphereVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, - uint batchStartIndex, uint batchNbItems, - bool reportContacts) const { + uint batchStartIndex, uint batchNbItems) const { bool isCollisionFound = false; @@ -114,7 +113,8 @@ bool SATAlgorithm::testCollisionSphereVsConvexPolyhedron(NarrowPhaseInfoBatch& n continue; } - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { const Vector3 minFaceNormal = polyhedron->getFaceNormal(minFaceIndex); Vector3 minFaceNormalWorld = polyhedronToWorldTransform.getOrientation() * minFaceNormal; @@ -162,7 +162,7 @@ decimal SATAlgorithm::computePolyhedronFaceVsSpherePenetrationDepth(uint faceInd } // Test collision between a capsule and a convex mesh -bool SATAlgorithm::testCollisionCapsuleVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex, bool reportContacts) const { +bool SATAlgorithm::testCollisionCapsuleVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex) const { RP3D_PROFILE("SATAlgorithm::testCollisionCapsuleVsConvexPolyhedron()", mProfiler); @@ -277,7 +277,8 @@ bool SATAlgorithm::testCollisionCapsuleVsConvexPolyhedron(NarrowPhaseInfoBatch& // We need to clip the inner capsule segment with the adjacent faces of the separating face if (isMinPenetrationFaceNormal) { - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { return computeCapsulePolyhedronFaceContactPoints(minFaceIndex, capsuleRadius, polyhedron, minPenetrationDepth, polyhedronToCapsuleTransform, normalWorld, separatingAxisCapsuleSpace, @@ -287,7 +288,8 @@ bool SATAlgorithm::testCollisionCapsuleVsConvexPolyhedron(NarrowPhaseInfoBatch& } else { // The separating axis is the cross product of a polyhedron edge and the inner capsule segment - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { // Compute the closest points between the inner capsule segment and the // edge of the polyhedron in polyhedron local-space @@ -475,7 +477,7 @@ bool SATAlgorithm::isMinkowskiFaceCapsuleVsEdge(const Vector3& capsuleSegment, c } // Test collision between two convex polyhedrons -bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool reportContacts) const { +bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems) const { RP3D_PROFILE("SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron()", mProfiler); @@ -485,6 +487,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn assert(narrowPhaseInfoBatch.collisionShapes1[batchIndex]->getType() == CollisionShapeType::CONVEX_POLYHEDRON); assert(narrowPhaseInfoBatch.collisionShapes2[batchIndex]->getType() == CollisionShapeType::CONVEX_POLYHEDRON); + assert(narrowPhaseInfoBatch.contactPoints[batchIndex].size() == 0); const ConvexPolyhedronShape* polyhedron1 = static_cast(narrowPhaseInfoBatch.collisionShapes1[batchIndex]); const ConvexPolyhedronShape* polyhedron2 = static_cast(narrowPhaseInfoBatch.collisionShapes2[batchIndex]); @@ -646,32 +649,37 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn decimal t2 = vec2.dot(edge1Direction) / edge1LengthSquare; if (t1 >= decimal(0.0) && t1 <= decimal(1) && t2 >= decimal(0.0) && t2 <= decimal(1.0)) { - // Compute the contact point on polyhedron 1 edge in the local-space of polyhedron 1 - Vector3 closestPointPolyhedron1EdgeLocalSpace = polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge; + // If we need to report contact points + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { + + // Compute the contact point on polyhedron 1 edge in the local-space of polyhedron 1 + Vector3 closestPointPolyhedron1EdgeLocalSpace = polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge; + + // Compute the world normal + // We use the direction from the centroid to the edge of the shape that is not a triangle + // to avoid possible degeneracies when axis direction is orthogonal to triangle normal + Vector3 normal; + if (isShape1Triangle) { + normal = polyhedron2->getCentroid() - closestPointPolyhedron2Edge; + } + else { + normal = polyhedron1ToPolyhedron2.getOrientation() * ((polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge) - polyhedron1->getCentroid()); + } + + //Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * minEdgeVsEdgeSeparatingAxisPolyhedron2Space; + Vector3 normalWorld = narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex].getOrientation() * normal.getUnit(); + + // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle + TriangleShape::computeSmoothTriangleMeshContact(narrowPhaseInfoBatch.collisionShapes1[batchIndex], narrowPhaseInfoBatch.collisionShapes2[batchIndex], + closestPointPolyhedron1EdgeLocalSpace, closestPointPolyhedron2Edge, + narrowPhaseInfoBatch.shape1ToWorldTransforms[batchIndex], narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex], + penetrationDepth, normalWorld); + + // Create the contact point + narrowPhaseInfoBatch.addContactPoint(batchIndex, normalWorld, penetrationDepth, + closestPointPolyhedron1EdgeLocalSpace, closestPointPolyhedron2Edge); - // Compute the world normal - // We use the direction from the centroid to the edge of the shape that is not a triangle - // to avoid possible degeneracies when axis direction is orthogonal to triangle normal - Vector3 normal; - if (isShape1Triangle) { - normal = polyhedron2->getCentroid() - closestPointPolyhedron2Edge; } - else { - normal = polyhedron1ToPolyhedron2.getOrientation() * ((polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge) - polyhedron1->getCentroid()); - } - - //Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * minEdgeVsEdgeSeparatingAxisPolyhedron2Space; - Vector3 normalWorld = narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex].getOrientation() * normal.getUnit(); - - // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle - TriangleShape::computeSmoothTriangleMeshContact(narrowPhaseInfoBatch.collisionShapes1[batchIndex], narrowPhaseInfoBatch.collisionShapes2[batchIndex], - closestPointPolyhedron1EdgeLocalSpace, closestPointPolyhedron2Edge, - narrowPhaseInfoBatch.shape1ToWorldTransforms[batchIndex], narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex], - penetrationDepth, normalWorld); - - // Create the contact point - narrowPhaseInfoBatch.addContactPoint(batchIndex, normalWorld, penetrationDepth, - closestPointPolyhedron1EdgeLocalSpace, closestPointPolyhedron2Edge); // The shapes are overlapping on the previous axis (the contact manifold is not empty). Therefore // we return without running the whole SAT algorithm @@ -819,24 +827,21 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn // If the minimum separating axis is a face normal if (isMinPenetrationFaceNormal) { - if (reportContacts) { + // Compute the contact points between two faces of two convex polyhedra. + bool contactsFound = computePolyhedronVsPolyhedronFaceContactPoints(isMinPenetrationFaceNormalPolyhedron1, polyhedron1, + polyhedron2, polyhedron1ToPolyhedron2, polyhedron2ToPolyhedron1, + minFaceIndex, narrowPhaseInfoBatch, batchIndex); - // Compute the contact points between two faces of two convex polyhedra. - bool contactsFound = computePolyhedronVsPolyhedronFaceContactPoints(isMinPenetrationFaceNormalPolyhedron1, polyhedron1, - polyhedron2, polyhedron1ToPolyhedron2, polyhedron2ToPolyhedron1, - minFaceIndex, narrowPhaseInfoBatch, batchIndex); + // There should be clipping points here. If it is not the case, it might be + // because of a numerical issue + if (!contactsFound) { - // There should be clipping points here. If it is not the case, it might be - // because of a numerical issue - if (!contactsFound) { + lastFrameCollisionInfo->satIsAxisFacePolyhedron1 = isMinPenetrationFaceNormalPolyhedron1; + lastFrameCollisionInfo->satIsAxisFacePolyhedron2 = !isMinPenetrationFaceNormalPolyhedron1; + lastFrameCollisionInfo->satMinAxisFaceIndex = minFaceIndex; - lastFrameCollisionInfo->satIsAxisFacePolyhedron1 = isMinPenetrationFaceNormalPolyhedron1; - lastFrameCollisionInfo->satIsAxisFacePolyhedron2 = !isMinPenetrationFaceNormalPolyhedron1; - lastFrameCollisionInfo->satMinAxisFaceIndex = minFaceIndex; - - // Return no collision - continue; - } + // Return no collision + continue; } lastFrameCollisionInfo->satIsAxisFacePolyhedron1 = isMinPenetrationFaceNormalPolyhedron1; @@ -845,7 +850,8 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn } else { // If we have an edge vs edge contact - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { // Compute the closest points between the two edges (in the local-space of poylhedron 2) Vector3 closestPointPolyhedron1Edge, closestPointPolyhedron2Edge; @@ -981,25 +987,29 @@ bool SATAlgorithm::computePolyhedronVsPolyhedronFaceContactPoints(bool isMinPene contactPointsFound = true; - Vector3 outWorldNormal = normalWorld; + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { - // Convert the clip incident polyhedron vertex into the incident polyhedron local-space - Vector3 contactPointIncidentPolyhedron = referenceToIncidentTransform * clipPolygonVertices[i]; + Vector3 outWorldNormal = normalWorld; - // Project the contact point onto the reference face - Vector3 contactPointReferencePolyhedron = projectPointOntoPlane(clipPolygonVertices[i], axisReferenceSpace, referenceFaceVertex); + // Convert the clip incident polyhedron vertex into the incident polyhedron local-space + Vector3 contactPointIncidentPolyhedron = referenceToIncidentTransform * clipPolygonVertices[i]; - // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle - TriangleShape::computeSmoothTriangleMeshContact(narrowPhaseInfoBatch.collisionShapes1[batchIndex], narrowPhaseInfoBatch.collisionShapes2[batchIndex], - isMinPenetrationFaceNormalPolyhedron1 ? contactPointReferencePolyhedron : contactPointIncidentPolyhedron, - isMinPenetrationFaceNormalPolyhedron1 ? contactPointIncidentPolyhedron : contactPointReferencePolyhedron, - narrowPhaseInfoBatch.shape1ToWorldTransforms[batchIndex], narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex], - penetrationDepth, outWorldNormal); + // Project the contact point onto the reference face + Vector3 contactPointReferencePolyhedron = projectPointOntoPlane(clipPolygonVertices[i], axisReferenceSpace, referenceFaceVertex); - // Create a new contact point - narrowPhaseInfoBatch.addContactPoint(batchIndex, outWorldNormal, penetrationDepth, - isMinPenetrationFaceNormalPolyhedron1 ? contactPointReferencePolyhedron : contactPointIncidentPolyhedron, - isMinPenetrationFaceNormalPolyhedron1 ? contactPointIncidentPolyhedron : contactPointReferencePolyhedron); + // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle + TriangleShape::computeSmoothTriangleMeshContact(narrowPhaseInfoBatch.collisionShapes1[batchIndex], narrowPhaseInfoBatch.collisionShapes2[batchIndex], + isMinPenetrationFaceNormalPolyhedron1 ? contactPointReferencePolyhedron : contactPointIncidentPolyhedron, + isMinPenetrationFaceNormalPolyhedron1 ? contactPointIncidentPolyhedron : contactPointReferencePolyhedron, + narrowPhaseInfoBatch.shape1ToWorldTransforms[batchIndex], narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex], + penetrationDepth, outWorldNormal); + + // Create a new contact point + narrowPhaseInfoBatch.addContactPoint(batchIndex, outWorldNormal, penetrationDepth, + isMinPenetrationFaceNormalPolyhedron1 ? contactPointReferencePolyhedron : contactPointIncidentPolyhedron, + isMinPenetrationFaceNormalPolyhedron1 ? contactPointIncidentPolyhedron : contactPointReferencePolyhedron); + } } } diff --git a/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp b/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp index fe2dab68..0a272672 100755 --- a/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp +++ b/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp @@ -36,7 +36,7 @@ using namespace reactphysics3d; // This technique is based on the "Robust Contact Creation for Physics Simulations" presentation // by Dirk Gregorius. bool SphereVsCapsuleAlgorithm::testCollision(SphereVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, MemoryAllocator& memoryAllocator) { + MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; @@ -79,7 +79,8 @@ bool SphereVsCapsuleAlgorithm::testCollision(SphereVsCapsuleNarrowPhaseInfoBatch Vector3 contactPointSphereLocal; Vector3 contactPointCapsuleLocal; - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { // If the sphere center is not on the capsule inner segment if (sphereSegmentDistanceSquare > MACHINE_EPSILON) { diff --git a/src/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.cpp b/src/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.cpp index 7d33d609..630fd202 100644 --- a/src/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.cpp +++ b/src/collision/narrowphase/SphereVsCapsuleNarrowPhaseInfoBatch.cpp @@ -40,7 +40,11 @@ SphereVsCapsuleNarrowPhaseInfoBatch::SphereVsCapsuleNarrowPhaseInfoBatch(MemoryA // Add shapes to be tested during narrow-phase collision detection into the batch void SphereVsCapsuleNarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, - const Transform& shape1Transform, const Transform& shape2Transform) { + const Transform& shape1Transform, const Transform& shape2Transform, + bool needToReportContacts, MemoryAllocator& shapeAllocator) { + + NarrowPhaseInfoBatch::addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, + shape2Transform, needToReportContacts, shapeAllocator); bool isSphereShape1 = shape1->getType() == CollisionShapeType::SPHERE; isSpheresShape1.add(isSphereShape1); @@ -54,30 +58,12 @@ void SphereVsCapsuleNarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint sphereRadiuses.add(sphereShape->getRadius()); capsuleRadiuses.add(capsuleShape->getRadius()); capsuleHeights.add(capsuleShape->getHeight()); - shape1ToWorldTransforms.add(shape1Transform); - shape2ToWorldTransforms.add(shape2Transform); - overlappingPairIds.add(pairId); - colliderEntities1.add(collider1); - colliderEntities2.add(collider2); - contactPoints.add(List(mMemoryAllocator)); - isColliding.add(false); - - // Add a collision info for the two collision shapes into the overlapping pair (if not present yet) - LastFrameCollisionInfo* lastFrameInfo = mOverlappingPairs.addLastFrameInfoIfNecessary(pairIndex, shape1->getId(), shape2->getId()); - lastFrameCollisionInfos.add(lastFrameInfo); } // Initialize the containers using cached capacity void SphereVsCapsuleNarrowPhaseInfoBatch::reserveMemory() { - overlappingPairIds.reserve(mCachedCapacity); - colliderEntities1.reserve(mCachedCapacity); - colliderEntities2.reserve(mCachedCapacity); - shape1ToWorldTransforms.reserve(mCachedCapacity); - shape2ToWorldTransforms.reserve(mCachedCapacity); - lastFrameCollisionInfos.reserve(mCachedCapacity); - isColliding.reserve(mCachedCapacity); - contactPoints.reserve(mCachedCapacity); + NarrowPhaseInfoBatch::reserveMemory(); isSpheresShape1.reserve(mCachedCapacity); sphereRadiuses.reserve(mCachedCapacity); @@ -93,16 +79,7 @@ void SphereVsCapsuleNarrowPhaseInfoBatch::clear() { // allocated in the next frame at a possibly different location in memory (remember that the // location of the allocated memory of a single frame allocator might change between two frames) - mCachedCapacity = overlappingPairIds.size(); - - overlappingPairIds.clear(true); - colliderEntities1.clear(true); - colliderEntities2.clear(true); - shape1ToWorldTransforms.clear(true); - shape2ToWorldTransforms.clear(true); - lastFrameCollisionInfos.clear(true); - isColliding.clear(true); - contactPoints.clear(true); + NarrowPhaseInfoBatch::clear(); isSpheresShape1.clear(true); sphereRadiuses.clear(true); diff --git a/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp b/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp index 64ee7f87..9c677fa4 100644 --- a/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp +++ b/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp @@ -36,7 +36,7 @@ using namespace reactphysics3d; // This technique is based on the "Robust Contact Creation for Physics Simulations" presentation // by Dirk Gregorius. bool SphereVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { + bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { // First, we run the GJK algorithm GJKAlgorithm gjkAlgorithm; @@ -50,7 +50,7 @@ bool SphereVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narr #endif List gjkResults(memoryAllocator); - gjkAlgorithm.testCollision(narrowPhaseInfoBatch, batchStartIndex, batchNbItems, gjkResults, reportContacts); + gjkAlgorithm.testCollision(narrowPhaseInfoBatch, batchStartIndex, batchNbItems, gjkResults); assert(gjkResults.size() == batchNbItems); // For each item in the batch @@ -88,7 +88,7 @@ bool SphereVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narr #endif - isCollisionFound |= satAlgorithm.testCollisionSphereVsConvexPolyhedron(narrowPhaseInfoBatch, batchIndex, 1, reportContacts); + isCollisionFound |= satAlgorithm.testCollisionSphereVsConvexPolyhedron(narrowPhaseInfoBatch, batchIndex, 1); lastFrameCollisionInfo->wasUsingGJK = false; lastFrameCollisionInfo->wasUsingSAT = true; diff --git a/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp b/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp index 4af07480..9334a618 100755 --- a/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp +++ b/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp @@ -31,8 +31,7 @@ // We want to use the ReactPhysics3D namespace using namespace reactphysics3d; -bool SphereVsSphereAlgorithm::testCollision(SphereVsSphereNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, MemoryAllocator& memoryAllocator) { +bool SphereVsSphereAlgorithm::testCollision(SphereVsSphereNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; @@ -59,10 +58,11 @@ bool SphereVsSphereAlgorithm::testCollision(SphereVsSphereNarrowPhaseInfoBatch& // If the sphere collision shapes intersect if (squaredDistanceBetweenCenters < sumRadiusesProducts) { - if (reportContacts) { + // If we need to report contacts + if (narrowPhaseInfoBatch.reportContacts[batchIndex]) { - Transform transform1Inverse = transform1.getInverse(); - Transform transform2Inverse = transform2.getInverse(); + const Transform transform1Inverse = transform1.getInverse(); + const Transform transform2Inverse = transform2.getInverse(); decimal penetrationDepth = sumRadiuses - std::sqrt(squaredDistanceBetweenCenters); Vector3 intersectionOnBody1; diff --git a/src/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.cpp b/src/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.cpp index 76914285..f476af97 100644 --- a/src/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.cpp +++ b/src/collision/narrowphase/SphereVsSphereNarrowPhaseInfoBatch.cpp @@ -37,7 +37,10 @@ SphereVsSphereNarrowPhaseInfoBatch::SphereVsSphereNarrowPhaseInfoBatch(MemoryAll // Add shapes to be tested during narrow-phase collision detection into the batch void SphereVsSphereNarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2, - const Transform& shape1Transform, const Transform& shape2Transform) { + const Transform& shape1Transform, const Transform& shape2Transform, bool needToReportContacts, MemoryAllocator &shapeAllocator) { + + NarrowPhaseInfoBatch::addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, + shape2Transform, needToReportContacts, shapeAllocator); assert(shape1->getType() == CollisionShapeType::SPHERE); assert(shape2->getType() == CollisionShapeType::SPHERE); @@ -47,30 +50,12 @@ void SphereVsSphereNarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint6 sphere1Radiuses.add(sphere1->getRadius()); sphere2Radiuses.add(sphere2->getRadius()); - colliderEntities1.add(collider1); - colliderEntities2.add(collider2); - shape1ToWorldTransforms.add(shape1Transform); - shape2ToWorldTransforms.add(shape2Transform); - overlappingPairIds.add(pairId); - contactPoints.add(List(mMemoryAllocator)); - isColliding.add(false); - - // Add a collision info for the two collision shapes into the overlapping pair (if not present yet) - LastFrameCollisionInfo* lastFrameInfo = mOverlappingPairs.addLastFrameInfoIfNecessary(pairIndex, shape1->getId(), shape2->getId()); - lastFrameCollisionInfos.add(lastFrameInfo); } // Initialize the containers using cached capacity void SphereVsSphereNarrowPhaseInfoBatch::reserveMemory() { - overlappingPairIds.reserve(mCachedCapacity); - colliderEntities1.reserve(mCachedCapacity); - colliderEntities2.reserve(mCachedCapacity); - shape1ToWorldTransforms.reserve(mCachedCapacity); - shape2ToWorldTransforms.reserve(mCachedCapacity); - lastFrameCollisionInfos.reserve(mCachedCapacity); - isColliding.reserve(mCachedCapacity); - contactPoints.reserve(mCachedCapacity); + NarrowPhaseInfoBatch::reserveMemory(); sphere1Radiuses.reserve(mCachedCapacity); sphere2Radiuses.reserve(mCachedCapacity); @@ -84,16 +69,7 @@ void SphereVsSphereNarrowPhaseInfoBatch::clear() { // allocated in the next frame at a possibly different location in memory (remember that the // location of the allocated memory of a single frame allocator might change between two frames) - mCachedCapacity = overlappingPairIds.size(); - - overlappingPairIds.clear(true); - colliderEntities1.clear(true); - colliderEntities2.clear(true); - shape1ToWorldTransforms.clear(true); - shape2ToWorldTransforms.clear(true); - lastFrameCollisionInfos.clear(true); - isColliding.clear(true); - contactPoints.clear(true); + NarrowPhaseInfoBatch::clear(); sphere1Radiuses.clear(true); sphere2Radiuses.clear(true); diff --git a/src/components/ColliderComponents.cpp b/src/components/ColliderComponents.cpp index 33a47503..0d879f83 100644 --- a/src/components/ColliderComponents.cpp +++ b/src/components/ColliderComponents.cpp @@ -37,7 +37,8 @@ using namespace reactphysics3d; ColliderComponents::ColliderComponents(MemoryAllocator& allocator) :Components(allocator, sizeof(Entity) + sizeof(Entity) + sizeof(Collider*) + sizeof(int32) + sizeof(Transform) + sizeof(CollisionShape*) + sizeof(unsigned short) + - sizeof(unsigned short) + sizeof(Transform) + sizeof(List) + sizeof(bool)) { + sizeof(unsigned short) + sizeof(Transform) + sizeof(List) + sizeof(bool) + + sizeof(bool)) { // Allocate memory for the components data allocate(INIT_NB_ALLOCATED_COMPONENTS); @@ -67,6 +68,7 @@ void ColliderComponents::allocate(uint32 nbComponentsToAllocate) { Transform* newLocalToWorldTransforms = reinterpret_cast(newCollideWithMaskBits + nbComponentsToAllocate); List* newOverlappingPairs = reinterpret_cast*>(newLocalToWorldTransforms + nbComponentsToAllocate); bool* hasCollisionShapeChangedSize = reinterpret_cast(newOverlappingPairs + nbComponentsToAllocate); + bool* isTrigger = reinterpret_cast(hasCollisionShapeChangedSize + nbComponentsToAllocate); // If there was already components before if (mNbComponents > 0) { @@ -83,6 +85,7 @@ void ColliderComponents::allocate(uint32 nbComponentsToAllocate) { memcpy(newLocalToWorldTransforms, mLocalToWorldTransforms, mNbComponents * sizeof(Transform)); memcpy(newOverlappingPairs, mOverlappingPairs, mNbComponents * sizeof(List)); memcpy(hasCollisionShapeChangedSize, mHasCollisionShapeChangedSize, mNbComponents * sizeof(bool)); + memcpy(isTrigger, mIsTrigger, mNbComponents * sizeof(bool)); // Deallocate previous memory mMemoryAllocator.release(mBuffer, mNbAllocatedComponents * mComponentDataSize); @@ -101,6 +104,7 @@ void ColliderComponents::allocate(uint32 nbComponentsToAllocate) { mLocalToWorldTransforms = newLocalToWorldTransforms; mOverlappingPairs = newOverlappingPairs; mHasCollisionShapeChangedSize = hasCollisionShapeChangedSize; + mIsTrigger = isTrigger; mNbAllocatedComponents = nbComponentsToAllocate; } @@ -123,6 +127,7 @@ void ColliderComponents::addComponent(Entity colliderEntity, bool isSleeping, co new (mLocalToWorldTransforms + index) Transform(component.localToWorldTransform); new (mOverlappingPairs + index) List(mMemoryAllocator); mHasCollisionShapeChangedSize[index] = false; + mIsTrigger[index] = false; // Map the entity with the new component lookup index mMapEntityToComponentIndex.add(Pair(colliderEntity, index)); @@ -150,6 +155,7 @@ void ColliderComponents::moveComponentToIndex(uint32 srcIndex, uint32 destIndex) new (mLocalToWorldTransforms + destIndex) Transform(mLocalToWorldTransforms[srcIndex]); new (mOverlappingPairs + destIndex) List(mOverlappingPairs[srcIndex]); mHasCollisionShapeChangedSize[destIndex] = mHasCollisionShapeChangedSize[srcIndex]; + mIsTrigger[destIndex] = mIsTrigger[srcIndex]; // Destroy the source component destroyComponent(srcIndex); @@ -177,6 +183,7 @@ void ColliderComponents::swapComponents(uint32 index1, uint32 index2) { Transform localToWorldTransform1 = mLocalToWorldTransforms[index1]; List overlappingPairs = mOverlappingPairs[index1]; bool hasCollisionShapeChangedSize = mHasCollisionShapeChangedSize[index1]; + bool isTrigger = mIsTrigger[index1]; // Destroy component 1 destroyComponent(index1); @@ -195,6 +202,7 @@ void ColliderComponents::swapComponents(uint32 index1, uint32 index2) { new (mLocalToWorldTransforms + index2) Transform(localToWorldTransform1); new (mOverlappingPairs + index2) List(overlappingPairs); mHasCollisionShapeChangedSize[index2] = hasCollisionShapeChangedSize; + mIsTrigger[index2] = isTrigger; // Update the entity to component index mapping mMapEntityToComponentIndex.add(Pair(colliderEntity1, index2)); diff --git a/src/engine/OverlappingPairs.cpp b/src/engine/OverlappingPairs.cpp index f9f60460..9580a833 100644 --- a/src/engine/OverlappingPairs.cpp +++ b/src/engine/OverlappingPairs.cpp @@ -40,7 +40,7 @@ OverlappingPairs::OverlappingPairs(MemoryAllocator& persistentMemoryAllocator, M mNbPairs(0), mConcavePairsStartIndex(0), mPairDataSize(sizeof(uint64) + sizeof(int32) + sizeof(int32) + sizeof(Entity) + sizeof(Entity) + sizeof(Map) + sizeof(bool) + sizeof(bool) + sizeof(NarrowPhaseAlgorithmType) + - sizeof(bool)), + sizeof(bool) + sizeof(bool) + sizeof(bool)), mNbAllocatedPairs(0), mBuffer(nullptr), mMapPairIdToPairIndex(persistentMemoryAllocator), mColliderComponents(colliderComponents), mCollisionBodyComponents(collisionBodyComponents), @@ -211,6 +211,8 @@ void OverlappingPairs::allocate(uint64 nbPairsToAllocate) { bool* newIsActive = reinterpret_cast(newNeedToTestOverlap + nbPairsToAllocate); NarrowPhaseAlgorithmType* newNarrowPhaseAlgorithmType = reinterpret_cast(newIsActive + nbPairsToAllocate); bool* newIsShape1Convex = reinterpret_cast(newNarrowPhaseAlgorithmType + nbPairsToAllocate); + bool* wereCollidingInPreviousFrame = reinterpret_cast(newIsShape1Convex + nbPairsToAllocate); + bool* areCollidingInCurrentFrame = reinterpret_cast(wereCollidingInPreviousFrame + nbPairsToAllocate); // If there was already pairs before if (mNbPairs > 0) { @@ -226,6 +228,8 @@ void OverlappingPairs::allocate(uint64 nbPairsToAllocate) { memcpy(newIsActive, mIsActive, mNbPairs * sizeof(bool)); memcpy(newNarrowPhaseAlgorithmType, mNarrowPhaseAlgorithmType, mNbPairs * sizeof(NarrowPhaseAlgorithmType)); memcpy(newIsShape1Convex, mIsShape1Convex, mNbPairs * sizeof(bool)); + memcpy(wereCollidingInPreviousFrame, mCollidingInPreviousFrame, mNbPairs * sizeof(bool)); + memcpy(areCollidingInCurrentFrame, mCollidingInCurrentFrame, mNbPairs * sizeof(bool)); // Deallocate previous memory mPersistentAllocator.release(mBuffer, mNbAllocatedPairs * mPairDataSize); @@ -242,6 +246,8 @@ void OverlappingPairs::allocate(uint64 nbPairsToAllocate) { mIsActive = newIsActive; mNarrowPhaseAlgorithmType = newNarrowPhaseAlgorithmType; mIsShape1Convex = newIsShape1Convex; + mCollidingInPreviousFrame = wereCollidingInPreviousFrame; + mCollidingInCurrentFrame = areCollidingInCurrentFrame; mNbAllocatedPairs = nbPairsToAllocate; } @@ -298,6 +304,8 @@ uint64 OverlappingPairs::addPair(Collider* shape1, Collider* shape2) { new (mIsActive + index) bool(true); new (mNarrowPhaseAlgorithmType + index) NarrowPhaseAlgorithmType(algorithmType); new (mIsShape1Convex + index) bool(isShape1Convex); + new (mCollidingInPreviousFrame + index) bool(false); + new (mCollidingInCurrentFrame + index) bool(false); // Map the entity with the new component lookup index mMapPairIdToPairIndex.add(Pair(pairId, index)); @@ -335,6 +343,8 @@ void OverlappingPairs::movePairToIndex(uint64 srcIndex, uint64 destIndex) { mIsActive[destIndex] = mIsActive[srcIndex]; new (mNarrowPhaseAlgorithmType + destIndex) NarrowPhaseAlgorithmType(mNarrowPhaseAlgorithmType[srcIndex]); mIsShape1Convex[destIndex] = mIsShape1Convex[srcIndex]; + mCollidingInPreviousFrame[destIndex] = mCollidingInPreviousFrame[srcIndex]; + mCollidingInCurrentFrame[destIndex] = mCollidingInCurrentFrame[srcIndex]; // Destroy the source pair destroyPair(srcIndex); @@ -361,6 +371,8 @@ void OverlappingPairs::swapPairs(uint64 index1, uint64 index2) { bool isActive = mIsActive[index1]; NarrowPhaseAlgorithmType narrowPhaseAlgorithmType = mNarrowPhaseAlgorithmType[index1]; bool isShape1Convex = mIsShape1Convex[index1]; + bool wereCollidingInPreviousFrame = mCollidingInPreviousFrame[index1]; + bool areCollidingInCurrentFrame = mCollidingInCurrentFrame[index1]; // Destroy pair 1 destroyPair(index1); @@ -378,6 +390,8 @@ void OverlappingPairs::swapPairs(uint64 index1, uint64 index2) { mIsActive[index2] = isActive; new (mNarrowPhaseAlgorithmType + index2) NarrowPhaseAlgorithmType(narrowPhaseAlgorithmType); mIsShape1Convex[index2] = isShape1Convex; + mCollidingInPreviousFrame[index2] = wereCollidingInPreviousFrame; + mCollidingInCurrentFrame[index2] = areCollidingInCurrentFrame; // Update the pairID to pair index mapping mMapPairIdToPairIndex.add(Pair(pairId, index2)); @@ -476,7 +490,7 @@ void OverlappingPairs::clearObsoleteLastFrameCollisionInfos() { RP3D_PROFILE("OverlappingPairs::clearObsoleteLastFrameCollisionInfos()", mProfiler); - // For each convex vs convex overlapping pair + // For each overlapping pair for (uint64 i=0; i < mNbPairs; i++) { // For each collision info @@ -501,3 +515,13 @@ void OverlappingPairs::clearObsoleteLastFrameCollisionInfos() { } } } + +// Set the collidingInPreviousFrame value with the collidinginCurrentFrame value for each pair +void OverlappingPairs::updateCollidingInPreviousFrame() { + + // For each overlapping pair + for (uint64 i=0; i < mNbPairs; i++) { + + mCollidingInPreviousFrame[i] = mCollidingInCurrentFrame[i]; + } +} diff --git a/src/engine/PhysicsWorld.cpp b/src/engine/PhysicsWorld.cpp index 4cc9d062..9c73abe3 100644 --- a/src/engine/PhysicsWorld.cpp +++ b/src/engine/PhysicsWorld.cpp @@ -338,7 +338,7 @@ void PhysicsWorld::update(decimal timeStep) { createIslands(); // Report the contacts to the user - mCollisionDetection.reportContacts(); + mCollisionDetection.reportContactsAndTriggers(); // Disable the joints for pair of sleeping bodies disableJointsOfSleepingBodies(); @@ -718,9 +718,6 @@ void PhysicsWorld::addJointToBodies(Entity body1, Entity body2, Entity joint) { /// it). Then, we create an island with this group of connected bodies. void PhysicsWorld::createIslands() { - // list of contact pairs involving a non-rigid body - List nonRigidBodiesContactPairs(mMemoryManager.getSingleFrameAllocator()); - RP3D_PROFILE("PhysicsWorld::createIslands()", mProfiler); // Reset all the isAlreadyInIsland variables of bodies and joints @@ -783,14 +780,15 @@ void PhysicsWorld::createIslands() { for (uint p=0; p < contactPairs.size(); p++) { ContactPair& pair = (*mCollisionDetection.mCurrentContactPairs)[contactPairs[p]]; - assert(pair.potentialContactManifoldsIndices.size() > 0); // Check if the current contact pair has already been added into an island if (pair.isAlreadyInIsland) continue; - // If the colliding body is a RigidBody (and not a CollisionBody instead) - if (mRigidBodyComponents.hasComponent(pair.body1Entity) && mRigidBodyComponents.hasComponent(pair.body2Entity)) { + // If the colliding body is a RigidBody (and not a CollisionBody) and is not a trigger + if (mRigidBodyComponents.hasComponent(pair.body1Entity) && mRigidBodyComponents.hasComponent(pair.body2Entity) + && !mCollidersComponents.getIsTrigger(pair.collider1Entity) && !mCollidersComponents.getIsTrigger(pair.collider2Entity)) { + assert(pair.potentialContactManifoldsIndices.size() > 0); nbTotalManifolds += pair.potentialContactManifoldsIndices.size(); // Add the contact manifold into the island @@ -809,7 +807,6 @@ void PhysicsWorld::createIslands() { else { // Add the contact pair index in the list of contact pairs that won't be part of islands - nonRigidBodiesContactPairs.add(pair.contactPairIndex); pair.isAlreadyInIsland = true; } } diff --git a/src/systems/CollisionDetectionSystem.cpp b/src/systems/CollisionDetectionSystem.cpp index 1b3ac912..2d104d37 100644 --- a/src/systems/CollisionDetectionSystem.cpp +++ b/src/systems/CollisionDetectionSystem.cpp @@ -61,10 +61,10 @@ CollisionDetectionSystem::CollisionDetectionSystem(PhysicsWorld* world, Collider mNarrowPhaseInput(mMemoryManager.getSingleFrameAllocator(), mOverlappingPairs), mPotentialContactPoints(mMemoryManager.getSingleFrameAllocator()), mPotentialContactManifolds(mMemoryManager.getSingleFrameAllocator()), mContactPairs1(mMemoryManager.getPoolAllocator()), mContactPairs2(mMemoryManager.getPoolAllocator()), mPreviousContactPairs(&mContactPairs1), mCurrentContactPairs(&mContactPairs2), - mMapPairIdToContactPairIndex1(mMemoryManager.getPoolAllocator()), mMapPairIdToContactPairIndex2(mMemoryManager.getPoolAllocator()), - mPreviousMapPairIdToContactPairIndex(&mMapPairIdToContactPairIndex1), mCurrentMapPairIdToContactPairIndex(&mMapPairIdToContactPairIndex2), - mContactManifolds1(mMemoryManager.getPoolAllocator()), mContactManifolds2(mMemoryManager.getPoolAllocator()), - mPreviousContactManifolds(&mContactManifolds1), mCurrentContactManifolds(&mContactManifolds2), + mLostContactPairs(mMemoryManager.getSingleFrameAllocator()), mMapPairIdToContactPairIndex1(mMemoryManager.getPoolAllocator()), + mMapPairIdToContactPairIndex2(mMemoryManager.getPoolAllocator()), mPreviousMapPairIdToContactPairIndex(&mMapPairIdToContactPairIndex1), + mCurrentMapPairIdToContactPairIndex(&mMapPairIdToContactPairIndex2), mContactManifolds1(mMemoryManager.getPoolAllocator()), + mContactManifolds2(mMemoryManager.getPoolAllocator()), mPreviousContactManifolds(&mContactManifolds1), mCurrentContactManifolds(&mContactManifolds2), mContactPoints1(mMemoryManager.getPoolAllocator()), mContactPoints2(mMemoryManager.getPoolAllocator()), mPreviousContactPoints(&mContactPoints1), mCurrentContactPoints(&mContactPoints2), mMapBodyToContactPairs(mMemoryManager.getSingleFrameAllocator()) { @@ -88,7 +88,7 @@ void CollisionDetectionSystem::computeCollisionDetection() { computeBroadPhase(); // Compute the middle-phase collision detection - computeMiddlePhase(mNarrowPhaseInput); + computeMiddlePhase(mNarrowPhaseInput, true); // Compute the narrow-phase collision detection computeNarrowPhase(); @@ -117,7 +117,6 @@ void CollisionDetectionSystem::removeNonOverlappingPairs() { RP3D_PROFILE("CollisionDetectionSystem::removeNonOverlappingPairs()", mProfiler); - // For each possible convex vs convex pair of bodies for (uint64 i=0; i < mOverlappingPairs.getNbPairs(); i++) { // Check if we need to test overlap. If so, test if the two shapes are still overlapping. @@ -128,6 +127,14 @@ void CollisionDetectionSystem::removeNonOverlappingPairs() { mOverlappingPairs.mNeedToTestOverlap[i] = false; } else { + + // If the two colliders of the pair were colliding in the previous frame + if (mOverlappingPairs.mCollidingInPreviousFrame[i]) { + + // Create a new lost contact pair + addLostContactPair(i); + } + mOverlappingPairs.removePair(mOverlappingPairs.mPairIds[i]); i--; } @@ -135,6 +142,25 @@ void CollisionDetectionSystem::removeNonOverlappingPairs() { } } +// Add a lost contact pair (pair of colliders that are not in contact anymore) +void CollisionDetectionSystem::addLostContactPair(uint64 overlappingPairIndex) { + + const Entity collider1Entity = mOverlappingPairs.mColliders1[overlappingPairIndex]; + const Entity collider2Entity = mOverlappingPairs.mColliders2[overlappingPairIndex]; + + const Entity body1Entity = mCollidersComponents.getBody(collider1Entity); + const Entity body2Entity = mCollidersComponents.getBody(collider2Entity); + + const bool isCollider1Trigger = mCollidersComponents.getIsTrigger(collider1Entity); + const bool isCollider2Trigger = mCollidersComponents.getIsTrigger(collider2Entity); + const bool isTrigger = isCollider1Trigger || isCollider2Trigger; + + // Create a lost contact pair + ContactPair lostContactPair(mOverlappingPairs.mPairIds[overlappingPairIndex], body1Entity, body2Entity, collider1Entity, collider2Entity, mLostContactPairs.size(), + true, isTrigger, mMemoryManager.getHeapAllocator()); + mLostContactPairs.add(lostContactPair); +} + // Take a list of overlapping nodes in the broad-phase and create new overlapping pairs if necessary void CollisionDetectionSystem::updateOverlappingPairs(const List>& overlappingNodes) { @@ -204,7 +230,7 @@ void CollisionDetectionSystem::updateOverlappingPairs(const List& convexPairs, List& concavePairs, NarrowPhaseInput& narrowPhaseInput) { +void CollisionDetectionSystem::computeMiddlePhaseCollisionSnapshot(List& convexPairs, List& concavePairs, NarrowPhaseInput& narrowPhaseInput, + bool reportContacts) { RP3D_PROFILE("CollisionDetectionSystem::computeMiddlePhase()", mProfiler); @@ -299,7 +334,7 @@ void CollisionDetectionSystem::computeMiddlePhaseCollisionSnapshot(List& narrowPhaseInput.addNarrowPhaseTest(pairId, pairIndex, collider1Entity, collider2Entity, collisionShape1, collisionShape2, mCollidersComponents.mLocalToWorldTransforms[collider1Index], mCollidersComponents.mLocalToWorldTransforms[collider2Index], - algorithmType, mMemoryManager.getSingleFrameAllocator()); + algorithmType, reportContacts, mMemoryManager.getSingleFrameAllocator()); } @@ -365,6 +400,10 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(uint64 pairInde assert(triangleVertices.size() % 3 == 0); assert(triangleVerticesNormals.size() % 3 == 0); + const bool isCollider1Trigger = mCollidersComponents.mIsTrigger[collider1Index]; + const bool isCollider2Trigger = mCollidersComponents.mIsTrigger[collider2Index]; + const bool reportContacts = !isCollider1Trigger && !isCollider2Trigger; + // For each overlapping triangle for (uint i=0; i < shapeIds.size(); i++) { @@ -384,13 +423,13 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(uint64 pairInde narrowPhaseInput.addNarrowPhaseTest(mOverlappingPairs.mPairIds[pairIndex], pairIndex, collider1, collider2, isShape1Convex ? convexShape : triangleShape, isShape1Convex ? triangleShape : convexShape, shape1LocalToWorldTransform, shape2LocalToWorldTransform, - mOverlappingPairs.mNarrowPhaseAlgorithmType[pairIndex], allocator); + mOverlappingPairs.mNarrowPhaseAlgorithmType[pairIndex], reportContacts, allocator); } } // Execute the narrow-phase collision detection algorithm on batches -bool CollisionDetectionSystem::testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool reportContacts, - bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& allocator) { +bool CollisionDetectionSystem::testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, + bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& allocator) { bool contactFound = false; @@ -402,32 +441,32 @@ bool CollisionDetectionSystem::testNarrowPhaseCollision(NarrowPhaseInput& narrow CapsuleVsConvexPolyhedronAlgorithm* capsuleVsConvexPolyAlgo = mCollisionDispatch.getCapsuleVsConvexPolyhedronAlgorithm(); ConvexPolyhedronVsConvexPolyhedronAlgorithm* convexPolyVsConvexPolyAlgo = mCollisionDispatch.getConvexPolyhedronVsConvexPolyhedronAlgorithm(); - // get the narrow-phase batches to test for collision - SphereVsSphereNarrowPhaseInfoBatch& sphereVsSphereBatch = narrowPhaseInput.getSphereVsSphereBatch(); - SphereVsCapsuleNarrowPhaseInfoBatch& sphereVsCapsuleBatch = narrowPhaseInput.getSphereVsCapsuleBatch(); - CapsuleVsCapsuleNarrowPhaseInfoBatch& capsuleVsCapsuleBatch = narrowPhaseInput.getCapsuleVsCapsuleBatch(); - NarrowPhaseInfoBatch& sphereVsConvexPolyhedronBatch = narrowPhaseInput.getSphereVsConvexPolyhedronBatch(); - NarrowPhaseInfoBatch& capsuleVsConvexPolyhedronBatch = narrowPhaseInput.getCapsuleVsConvexPolyhedronBatch(); - NarrowPhaseInfoBatch& convexPolyhedronVsConvexPolyhedronBatch = narrowPhaseInput.getConvexPolyhedronVsConvexPolyhedronBatch(); + // get the narrow-phase batches to test for collision for contacts + SphereVsSphereNarrowPhaseInfoBatch& sphereVsSphereBatchContacts = narrowPhaseInput.getSphereVsSphereBatch(); + SphereVsCapsuleNarrowPhaseInfoBatch& sphereVsCapsuleBatchContacts = narrowPhaseInput.getSphereVsCapsuleBatch(); + CapsuleVsCapsuleNarrowPhaseInfoBatch& capsuleVsCapsuleBatchContacts = narrowPhaseInput.getCapsuleVsCapsuleBatch(); + NarrowPhaseInfoBatch& sphereVsConvexPolyhedronBatchContacts = narrowPhaseInput.getSphereVsConvexPolyhedronBatch(); + NarrowPhaseInfoBatch& capsuleVsConvexPolyhedronBatchContacts = narrowPhaseInput.getCapsuleVsConvexPolyhedronBatch(); + NarrowPhaseInfoBatch& convexPolyhedronVsConvexPolyhedronBatchContacts = narrowPhaseInput.getConvexPolyhedronVsConvexPolyhedronBatch(); - // Compute the narrow-phase collision detection for each kind of collision shapes - if (sphereVsSphereBatch.getNbObjects() > 0) { - contactFound |= sphereVsSphereAlgo->testCollision(sphereVsSphereBatch, 0, sphereVsSphereBatch.getNbObjects(), reportContacts, allocator); + // Compute the narrow-phase collision detection for each kind of collision shapes (for contacts) + if (sphereVsSphereBatchContacts.getNbObjects() > 0) { + contactFound |= sphereVsSphereAlgo->testCollision(sphereVsSphereBatchContacts, 0, sphereVsSphereBatchContacts.getNbObjects(), allocator); } - if (sphereVsCapsuleBatch.getNbObjects() > 0) { - contactFound |= sphereVsCapsuleAlgo->testCollision(sphereVsCapsuleBatch, 0, sphereVsCapsuleBatch.getNbObjects(), reportContacts, allocator); + if (sphereVsCapsuleBatchContacts.getNbObjects() > 0) { + contactFound |= sphereVsCapsuleAlgo->testCollision(sphereVsCapsuleBatchContacts, 0, sphereVsCapsuleBatchContacts.getNbObjects(), allocator); } - if (capsuleVsCapsuleBatch.getNbObjects() > 0) { - contactFound |= capsuleVsCapsuleAlgo->testCollision(capsuleVsCapsuleBatch, 0, capsuleVsCapsuleBatch.getNbObjects(), reportContacts, allocator); + if (capsuleVsCapsuleBatchContacts.getNbObjects() > 0) { + contactFound |= capsuleVsCapsuleAlgo->testCollision(capsuleVsCapsuleBatchContacts, 0, capsuleVsCapsuleBatchContacts.getNbObjects(), allocator); } - if (sphereVsConvexPolyhedronBatch.getNbObjects() > 0) { - contactFound |= sphereVsConvexPolyAlgo->testCollision(sphereVsConvexPolyhedronBatch, 0, sphereVsConvexPolyhedronBatch.getNbObjects(), reportContacts, clipWithPreviousAxisIfStillColliding, allocator); + if (sphereVsConvexPolyhedronBatchContacts.getNbObjects() > 0) { + contactFound |= sphereVsConvexPolyAlgo->testCollision(sphereVsConvexPolyhedronBatchContacts, 0, sphereVsConvexPolyhedronBatchContacts.getNbObjects(), clipWithPreviousAxisIfStillColliding, allocator); } - if (capsuleVsConvexPolyhedronBatch.getNbObjects() > 0) { - contactFound |= capsuleVsConvexPolyAlgo->testCollision(capsuleVsConvexPolyhedronBatch, 0, capsuleVsConvexPolyhedronBatch.getNbObjects(), reportContacts, clipWithPreviousAxisIfStillColliding, allocator); + if (capsuleVsConvexPolyhedronBatchContacts.getNbObjects() > 0) { + contactFound |= capsuleVsConvexPolyAlgo->testCollision(capsuleVsConvexPolyhedronBatchContacts, 0, capsuleVsConvexPolyhedronBatchContacts.getNbObjects(), clipWithPreviousAxisIfStillColliding, allocator); } - if (convexPolyhedronVsConvexPolyhedronBatch.getNbObjects() > 0) { - contactFound |= convexPolyVsConvexPolyAlgo->testCollision(convexPolyhedronVsConvexPolyhedronBatch, 0, convexPolyhedronVsConvexPolyhedronBatch.getNbObjects(), reportContacts, clipWithPreviousAxisIfStillColliding, allocator); + if (convexPolyhedronVsConvexPolyhedronBatchContacts.getNbObjects() > 0) { + contactFound |= convexPolyVsConvexPolyAlgo->testCollision(convexPolyhedronVsConvexPolyhedronBatchContacts, 0, convexPolyhedronVsConvexPolyhedronBatchContacts.getNbObjects(), clipWithPreviousAxisIfStillColliding, allocator); } return contactFound; @@ -478,7 +517,7 @@ void CollisionDetectionSystem::computeNarrowPhase() { swapPreviousAndCurrentContacts(); // Test the narrow-phase collision detection on the batches to be tested - testNarrowPhaseCollision(mNarrowPhaseInput, true, true, allocator); + testNarrowPhaseCollision(mNarrowPhaseInput, true, allocator); // Process all the potential contacts after narrow-phase collision processAllPotentialContacts(mNarrowPhaseInput, true, mPotentialContactPoints, mCurrentMapPairIdToContactPairIndex, @@ -505,15 +544,16 @@ bool CollisionDetectionSystem::computeNarrowPhaseOverlapSnapshot(NarrowPhaseInpu MemoryAllocator& allocator = mMemoryManager.getPoolAllocator(); // Test the narrow-phase collision detection on the batches to be tested - bool collisionFound = testNarrowPhaseCollision(narrowPhaseInput, false, false, allocator); + bool collisionFound = testNarrowPhaseCollision(narrowPhaseInput, false, allocator); if (collisionFound && callback != nullptr) { - // Compute the overlapping bodies - List> overlapBodies(allocator); - computeSnapshotContactPairs(narrowPhaseInput, overlapBodies); + // Compute the overlapping colliders + List contactPairs(allocator); + List lostContactPairs(allocator); // Always empty in this case (snapshot) + computeOverlapSnapshotContactPairs(narrowPhaseInput, contactPairs); - // Report overlapping bodies - OverlapCallback::CallbackData callbackData(overlapBodies, *mWorld); + // Report overlapping colliders + OverlapCallback::CallbackData callbackData(contactPairs, lostContactPairs, *mWorld); (*callback).onOverlap(callbackData); } @@ -521,7 +561,9 @@ bool CollisionDetectionSystem::computeNarrowPhaseOverlapSnapshot(NarrowPhaseInpu } // Process the potential overlapping bodies for the testOverlap() methods -void CollisionDetectionSystem::computeSnapshotContactPairs(NarrowPhaseInput& narrowPhaseInput, List>& overlapPairs) const { +void CollisionDetectionSystem::computeOverlapSnapshotContactPairs(NarrowPhaseInput& narrowPhaseInput, List& contactPairs) const { + + Set setOverlapContactPairId(mMemoryManager.getHeapAllocator()); // get the narrow-phase batches to test for collision NarrowPhaseInfoBatch& sphereVsSphereBatch = narrowPhaseInput.getSphereVsSphereBatch(); @@ -532,12 +574,12 @@ void CollisionDetectionSystem::computeSnapshotContactPairs(NarrowPhaseInput& nar NarrowPhaseInfoBatch& convexPolyhedronVsConvexPolyhedronBatch = narrowPhaseInput.getConvexPolyhedronVsConvexPolyhedronBatch(); // Process the potential contacts - computeSnapshotContactPairs(sphereVsSphereBatch, overlapPairs); - computeSnapshotContactPairs(sphereVsCapsuleBatch, overlapPairs); - computeSnapshotContactPairs(capsuleVsCapsuleBatch, overlapPairs); - computeSnapshotContactPairs(sphereVsConvexPolyhedronBatch, overlapPairs); - computeSnapshotContactPairs(capsuleVsConvexPolyhedronBatch, overlapPairs); - computeSnapshotContactPairs(convexPolyhedronVsConvexPolyhedronBatch, overlapPairs); + computeOverlapSnapshotContactPairs(sphereVsSphereBatch, contactPairs, setOverlapContactPairId); + computeOverlapSnapshotContactPairs(sphereVsCapsuleBatch, contactPairs, setOverlapContactPairId); + computeOverlapSnapshotContactPairs(capsuleVsCapsuleBatch, contactPairs, setOverlapContactPairId); + computeOverlapSnapshotContactPairs(sphereVsConvexPolyhedronBatch, contactPairs, setOverlapContactPairId); + computeOverlapSnapshotContactPairs(capsuleVsConvexPolyhedronBatch, contactPairs, setOverlapContactPairId); + computeOverlapSnapshotContactPairs(convexPolyhedronVsConvexPolyhedronBatch, contactPairs, setOverlapContactPairId); } // Notify that the overlapping pairs where a given collider is involved need to be tested for overlap @@ -554,19 +596,38 @@ void CollisionDetectionSystem::notifyOverlappingPairsToTestOverlap(Collider* col } // Convert the potential overlapping bodies for the testOverlap() methods -void CollisionDetectionSystem::computeSnapshotContactPairs(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, List>& overlapPairs) const { +void CollisionDetectionSystem::computeOverlapSnapshotContactPairs(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, List& contactPairs, + Set& setOverlapContactPairId) const { RP3D_PROFILE("CollisionDetectionSystem::computeSnapshotContactPairs()", mProfiler); // For each narrow phase info object for(uint i=0; i < narrowPhaseInfoBatch.getNbObjects(); i++) { - // If there is a contact + // If there is a collision if (narrowPhaseInfoBatch.isColliding[i]) { - // Add the pair of bodies in contact into the list - overlapPairs.add(Pair(mCollidersComponents.getBody(narrowPhaseInfoBatch.colliderEntities1[i]), - mCollidersComponents.getBody(narrowPhaseInfoBatch.colliderEntities2[i]))); + // If the contact pair does not already exist + if (!setOverlapContactPairId.contains(narrowPhaseInfoBatch.overlappingPairIds[i])) { + + const Entity collider1Entity = narrowPhaseInfoBatch.colliderEntities1[i]; + const Entity collider2Entity = narrowPhaseInfoBatch.colliderEntities2[i]; + + const uint32 collider1Index = mCollidersComponents.getEntityIndex(collider1Entity); + const uint32 collider2Index = mCollidersComponents.getEntityIndex(collider2Entity); + + const Entity body1Entity = mCollidersComponents.mBodiesEntities[collider1Index]; + const Entity body2Entity = mCollidersComponents.mBodiesEntities[collider2Index]; + + const bool isTrigger = mCollidersComponents.mIsTrigger[collider1Index] || mCollidersComponents.mIsTrigger[collider2Index]; + + // Create a new contact pair + ContactPair contactPair(narrowPhaseInfoBatch.overlappingPairIds[i], body1Entity, body2Entity, collider1Entity, collider2Entity, + contactPairs.size(), isTrigger, false, mMemoryManager.getHeapAllocator()); + contactPairs.add(contactPair); + + setOverlapContactPairId.add(narrowPhaseInfoBatch.overlappingPairIds[i]); + } } narrowPhaseInfoBatch.resetContactPoints(i); @@ -582,7 +643,7 @@ bool CollisionDetectionSystem::computeNarrowPhaseCollisionSnapshot(NarrowPhaseIn MemoryAllocator& allocator = mMemoryManager.getHeapAllocator(); // Test the narrow-phase collision detection on the batches to be tested - bool collisionFound = testNarrowPhaseCollision(narrowPhaseInput, true, false, allocator); + bool collisionFound = testNarrowPhaseCollision(narrowPhaseInput, false, allocator); // If collision has been found, create contacts if (collisionFound) { @@ -591,6 +652,7 @@ bool CollisionDetectionSystem::computeNarrowPhaseCollisionSnapshot(NarrowPhaseIn List potentialContactManifolds(allocator); Map mapPairIdToContactPairIndex(allocator); List contactPairs(allocator); + List lostContactPairs(allocator); // Not used during collision snapshots List contactManifolds(allocator); List contactPoints(allocator); Map> mapBodyToContactPairs(allocator); @@ -607,7 +669,7 @@ bool CollisionDetectionSystem::computeNarrowPhaseCollisionSnapshot(NarrowPhaseIn potentialContactPoints); // Report the contacts to the user - reportContacts(callback, &contactPairs, &contactManifolds, &contactPoints); + reportContacts(callback, &contactPairs, &contactManifolds, &contactPoints, lostContactPairs); } return collisionFound; @@ -654,7 +716,6 @@ void CollisionDetectionSystem::createContacts() { for (uint p=0; p < (*mCurrentContactPairs).size(); p++) { ContactPair& contactPair = (*mCurrentContactPairs)[p]; - assert(contactPair.potentialContactManifoldsIndices.size() > 0); contactPair.contactManifoldsIndex = mCurrentContactManifolds->size(); contactPair.nbContactManifolds = contactPair.potentialContactManifoldsIndices.size(); @@ -696,16 +757,38 @@ void CollisionDetectionSystem::createContacts() { // Initialize the current contacts with the contacts from the previous frame (for warmstarting) initContactsWithPreviousOnes(); - // Report contacts to the user - if (mWorld->mEventListener != nullptr) { - reportContacts(*(mWorld->mEventListener), mCurrentContactPairs, mCurrentContactManifolds, mCurrentContactPoints); - } + // Compute the lost contacts (contact pairs that were colliding in previous frame but not in this one) + computeLostContactPairs(); + + mPreviousContactPoints->clear(); + mPreviousContactManifolds->clear(); + mPreviousContactPairs->clear(); + mPreviousMapPairIdToContactPairIndex->clear(); // Reset the potential contacts mPotentialContactPoints.clear(true); mPotentialContactManifolds.clear(true); } +// Compute the lost contact pairs (contact pairs in contact in the previous frame but not in the current one) +void CollisionDetectionSystem::computeLostContactPairs() { + + // For each overlapping pair + for (uint i=0; i < mOverlappingPairs.getNbPairs(); i++) { + + // If the two colliders of the pair were colliding in the previous frame but not in the current one + if (mOverlappingPairs.mCollidingInPreviousFrame[i] && !mOverlappingPairs.mCollidingInCurrentFrame[i]) { + + // If both bodies still exist + if (mCollidersComponents.hasComponent(mOverlappingPairs.mColliders1[i]) && mCollidersComponents.hasComponent(mOverlappingPairs.mColliders2[i])) { + + // Create a lost contact pair + addLostContactPair(i); + } + } + } +} + // Create the actual contact manifolds and contacts points for testCollision() methods void CollisionDetectionSystem::createSnapshotContacts(List& contactPairs, List& contactManifolds, @@ -784,7 +867,7 @@ void CollisionDetectionSystem::initContactsWithPreviousOnes() { const uint contactManifoldsIndex = currentContactPair.contactManifoldsIndex; const uint nbContactManifolds = currentContactPair.nbContactManifolds; - // For each current contact manifold of the contact pair + // For each current contact manifold of the current contact pair for (uint m=contactManifoldsIndex; m < contactManifoldsIndex + nbContactManifolds; m++) { assert(m < mCurrentContactManifolds->size()); @@ -823,7 +906,7 @@ void CollisionDetectionSystem::initContactsWithPreviousOnes() { const uint contactPointsIndex = currentContactPair.contactPointsIndex; const uint nbTotalContactPoints = currentContactPair.nbToTalContactPoints; - // For each current contact point of the contact pair + // For each current contact point of the current contact pair for (uint c=contactPointsIndex; c < contactPointsIndex + nbTotalContactPoints; c++) { assert(c < mCurrentContactPoints->size()); @@ -850,11 +933,6 @@ void CollisionDetectionSystem::initContactsWithPreviousOnes() { } } } - - mPreviousContactPoints->clear(); - mPreviousContactManifolds->clear(); - mPreviousContactPairs->clear(); - mPreviousMapPairIdToContactPairIndex->clear(); } // Remove a body from the collision detection @@ -897,11 +975,11 @@ void CollisionDetectionSystem::raycast(RaycastCallback* raycastCallback, // Convert the potential contact into actual contacts void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, bool updateLastFrameInfo, - List& potentialContactPoints, - Map* mapPairIdToContactPairIndex, - List& potentialContactManifolds, - List* contactPairs, - Map>& mapBodyToContactPairs) { + List& potentialContactPoints, + Map* mapPairIdToContactPairIndex, + List& potentialContactManifolds, + List* contactPairs, + Map>& mapBodyToContactPairs) { RP3D_PROFILE("CollisionDetectionSystem::processPotentialContacts()", mProfiler); @@ -915,32 +993,82 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na narrowPhaseInfoBatch.lastFrameCollisionInfos[i]->isValid = true; } - // Add the potential contacts - for (uint j=0; j < narrowPhaseInfoBatch.contactPoints[i].size(); j++) { + const uint64 pairId = narrowPhaseInfoBatch.overlappingPairIds[i]; + const uint64 pairIndex = mOverlappingPairs.mMapPairIdToPairIndex[pairId]; - assert(narrowPhaseInfoBatch.isColliding[i]); + // If the two colliders are colliding + if (narrowPhaseInfoBatch.isColliding[i]) { - const ContactPointInfo& contactPoint = *(narrowPhaseInfoBatch.contactPoints[i][j]); + mOverlappingPairs.mCollidingInCurrentFrame[pairIndex] = true; - // Add the contact point to the list of potential contact points - const uint contactPointIndex = static_cast(potentialContactPoints.size()); - - potentialContactPoints.add(contactPoint); - - bool similarManifoldFound = false; - - // If there is already a contact pair for this overlapping pair - const uint64 pairId = narrowPhaseInfoBatch.overlappingPairIds[i]; + // If there is not already a contact pair for this overlapping pair auto it = mapPairIdToContactPairIndex->find(pairId); ContactPair* pairContact = nullptr; - if (it != mapPairIdToContactPairIndex->end()) { + if (it == mapPairIdToContactPairIndex->end()) { + + // Create a new ContactPair + + const Entity collider1Entity = narrowPhaseInfoBatch.colliderEntities1[i]; + const Entity collider2Entity = narrowPhaseInfoBatch.colliderEntities2[i]; + + const uint32 collider1Index = mCollidersComponents.getEntityIndex(collider1Entity); + const uint32 collider2Index = mCollidersComponents.getEntityIndex(collider2Entity); + + const Entity body1Entity = mCollidersComponents.mBodiesEntities[collider1Index]; + const Entity body2Entity = mCollidersComponents.mBodiesEntities[collider2Index]; + + const bool isTrigger = mCollidersComponents.mIsTrigger[collider1Index] || mCollidersComponents.mIsTrigger[collider2Index]; + + assert(!mWorld->mCollisionBodyComponents.getIsEntityDisabled(body1Entity) || !mWorld->mCollisionBodyComponents.getIsEntityDisabled(body2Entity)); + + const uint newContactPairIndex = contactPairs->size(); + ContactPair overlappingPairContact(pairId, body1Entity, body2Entity, + collider1Entity, collider2Entity, + newContactPairIndex, mOverlappingPairs.getCollidingInPreviousFrame(pairId), isTrigger, mMemoryManager.getHeapAllocator()); + contactPairs->add(overlappingPairContact); + pairContact = &((*contactPairs)[newContactPairIndex]); + mapPairIdToContactPairIndex->add(Pair(pairId, newContactPairIndex)); + + auto itbodyContactPairs = mapBodyToContactPairs.find(body1Entity); + if (itbodyContactPairs != mapBodyToContactPairs.end()) { + itbodyContactPairs->second.add(newContactPairIndex); + } + else { + List contactPairs(mMemoryManager.getPoolAllocator(), 1); + contactPairs.add(newContactPairIndex); + mapBodyToContactPairs.add(Pair>(body1Entity, contactPairs)); + } + itbodyContactPairs = mapBodyToContactPairs.find(body2Entity); + if (itbodyContactPairs != mapBodyToContactPairs.end()) { + itbodyContactPairs->second.add(newContactPairIndex); + } + else { + List contactPairs(mMemoryManager.getPoolAllocator(), 1); + contactPairs.add(newContactPairIndex); + mapBodyToContactPairs.add(Pair>(body2Entity, contactPairs)); + } + } + else { // If a ContactPair already exists for this overlapping pair, we use this one assert(it->first == pairId); const uint pairContactIndex = it->second; pairContact = &((*contactPairs)[pairContactIndex]); + } - assert(pairContact->potentialContactManifoldsIndices.size() > 0); + assert(pairContact != nullptr); + + // Add the potential contacts + for (uint j=0; j < narrowPhaseInfoBatch.contactPoints[i].size(); j++) { + + const ContactPointInfo& contactPoint = *(narrowPhaseInfoBatch.contactPoints[i][j]); + + // Add the contact point to the list of potential contact points + const uint contactPointIndex = static_cast(potentialContactPoints.size()); + + potentialContactPoints.add(contactPoint); + + bool similarManifoldFound = false; // For each contact manifold of the overlapping pair for (uint m=0; m < pairContact->potentialContactManifoldsIndices.size(); m++) { @@ -964,71 +1092,32 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na break; } } - } - // If we have not found a manifold with a similar contact normal for the contact point - if (!similarManifoldFound) { + // If we have not found a manifold with a similar contact normal for the contact point + if (!similarManifoldFound) { - // Create a new contact manifold for the overlapping pair - ContactManifoldInfo contactManifoldInfo(pairId, mMemoryManager.getPoolAllocator()); + // Create a new contact manifold for the overlapping pair + ContactManifoldInfo contactManifoldInfo(pairId, mMemoryManager.getPoolAllocator()); - // Add the contact point to the manifold - contactManifoldInfo.potentialContactPointsIndices.add(contactPointIndex); + // Add the contact point to the manifold + contactManifoldInfo.potentialContactPointsIndices.add(contactPointIndex); - // If the overlapping pair contact does not exists yet - if (pairContact == nullptr) { + assert(pairContact != nullptr); - const Entity collider1Entity = narrowPhaseInfoBatch.colliderEntities1[i]; - const Entity collider2Entity = narrowPhaseInfoBatch.colliderEntities2[i]; + // Add the potential contact manifold + uint contactManifoldIndex = static_cast(potentialContactManifolds.size()); + potentialContactManifolds.add(contactManifoldInfo); - const Entity body1Entity = mCollidersComponents.getBody(collider1Entity); - const Entity body2Entity = mCollidersComponents.getBody(collider2Entity); - - assert(!mWorld->mCollisionBodyComponents.getIsEntityDisabled(body1Entity) || !mWorld->mCollisionBodyComponents.getIsEntityDisabled(body2Entity)); - - const uint newContactPairIndex = contactPairs->size(); - ContactPair overlappingPairContact(pairId, body1Entity, body2Entity, - collider1Entity, collider2Entity, - newContactPairIndex, mMemoryManager.getHeapAllocator()); - contactPairs->add(overlappingPairContact); - pairContact = &((*contactPairs)[newContactPairIndex]); - mapPairIdToContactPairIndex->add(Pair(pairId, newContactPairIndex)); - - auto itbodyContactPairs = mapBodyToContactPairs.find(body1Entity); - if (itbodyContactPairs != mapBodyToContactPairs.end()) { - itbodyContactPairs->second.add(newContactPairIndex); - } - else { - List contactPairs(mMemoryManager.getPoolAllocator(), 1); - contactPairs.add(newContactPairIndex); - mapBodyToContactPairs.add(Pair>(body1Entity, contactPairs)); - } - itbodyContactPairs = mapBodyToContactPairs.find(body2Entity); - if (itbodyContactPairs != mapBodyToContactPairs.end()) { - itbodyContactPairs->second.add(newContactPairIndex); - } - else { - List contactPairs(mMemoryManager.getPoolAllocator(), 1); - contactPairs.add(newContactPairIndex); - mapBodyToContactPairs.add(Pair>(body2Entity, contactPairs)); - } + // Add the contact manifold to the overlapping pair contact + assert(pairContact->pairId == contactManifoldInfo.pairId); + pairContact->potentialContactManifoldsIndices.add(contactManifoldIndex); } - assert(pairContact != nullptr); - - // Add the potential contact manifold - uint contactManifoldIndex = static_cast(potentialContactManifolds.size()); - potentialContactManifolds.add(contactManifoldInfo); - - // Add the contact manifold to the overlapping pair contact - assert(pairContact->pairId == contactManifoldInfo.pairId); - pairContact->potentialContactManifoldsIndices.add(contactManifoldIndex); + assert(pairContact->potentialContactManifoldsIndices.size() > 0); } - assert(pairContact->potentialContactManifoldsIndices.size() > 0); + narrowPhaseInfoBatch.resetContactPoints(i); } - - narrowPhaseInfoBatch.resetContactPoints(i); } } @@ -1044,8 +1133,6 @@ void CollisionDetectionSystem::reducePotentialContactManifolds(List ContactPair& contactPair = (*contactPairs)[i]; - assert(contactPair.potentialContactManifoldsIndices.size() > 0); - // While there are too many manifolds in the contact pair while(contactPair.potentialContactManifoldsIndices.size() > mWorld->mConfig.nbMaxContactManifolds) { @@ -1296,31 +1383,51 @@ void CollisionDetectionSystem::reduceContactPoints(ContactManifoldInfo& manifold manifold.potentialContactPointsIndices.add(pointsToKeepIndices[3]); } -// Report contacts -void CollisionDetectionSystem::reportContacts() { +// Report contacts and triggers +void CollisionDetectionSystem::reportContactsAndTriggers() { - if (mWorld->mEventListener != nullptr) { - reportContacts(*(mWorld->mEventListener), mCurrentContactPairs, mCurrentContactManifolds, - mCurrentContactPoints); - } + if (mWorld->mEventListener != nullptr) { + + reportContacts(*(mWorld->mEventListener), mCurrentContactPairs, mCurrentContactManifolds, mCurrentContactPoints, mLostContactPairs); + reportTriggers(*(mWorld->mEventListener), mCurrentContactPairs, mLostContactPairs); + } + + mOverlappingPairs.updateCollidingInPreviousFrame(); + + mLostContactPairs.clear(true); } // Report all contacts void CollisionDetectionSystem::reportContacts(CollisionCallback& callback, List* contactPairs, - List* manifolds, List* contactPoints) { + List* manifolds, List* contactPoints, List& lostContactPairs) { RP3D_PROFILE("CollisionDetectionSystem::reportContacts()", mProfiler); // If there are contacts - if (contactPairs->size() > 0) { + if (contactPairs->size() + lostContactPairs.size() > 0) { - CollisionCallback::CallbackData callbackData(contactPairs, manifolds, contactPoints, *mWorld); + CollisionCallback::CallbackData callbackData(contactPairs, manifolds, contactPoints, lostContactPairs, *mWorld); // Call the callback method to report the contacts callback.onContact(callbackData); } } +// Report all triggers +void CollisionDetectionSystem::reportTriggers(EventListener& eventListener, List* contactPairs, List& lostContactPairs) { + + RP3D_PROFILE("CollisionDetectionSystem::reportTriggers()", mProfiler); + + // If there are contacts + if (contactPairs->size() + lostContactPairs.size() > 0) { + + OverlapCallback::CallbackData callbackData(*contactPairs, lostContactPairs, *mWorld); + + // Call the callback method to report the overlapping shapes + eventListener.onTrigger(callbackData); + } +} + // Return true if two bodies overlap (collide) bool CollisionDetectionSystem::testOverlap(CollisionBody* body1, CollisionBody* body2) { @@ -1337,7 +1444,7 @@ bool CollisionDetectionSystem::testOverlap(CollisionBody* body1, CollisionBody* if (convexPairs.size() > 0 || concavePairs.size() > 0) { // Compute the middle-phase collision detection - computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput); + computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput, false); // Compute the narrow-phase collision detection return computeNarrowPhaseOverlapSnapshot(narrowPhaseInput, nullptr); @@ -1355,7 +1462,7 @@ void CollisionDetectionSystem::testOverlap(OverlapCallback& callback) { computeBroadPhase(); // Compute the middle-phase collision detection - computeMiddlePhase(narrowPhaseInput); + computeMiddlePhase(narrowPhaseInput, false); // Compute the narrow-phase collision detection and report overlapping shapes computeNarrowPhaseOverlapSnapshot(narrowPhaseInput, &callback); @@ -1377,7 +1484,7 @@ void CollisionDetectionSystem::testOverlap(CollisionBody* body, OverlapCallback& if (convexPairs.size() > 0 || concavePairs.size() > 0) { // Compute the middle-phase collision detection - computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput); + computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput, false); // Compute the narrow-phase collision detection computeNarrowPhaseOverlapSnapshot(narrowPhaseInput, &callback); @@ -1400,7 +1507,7 @@ void CollisionDetectionSystem::testCollision(CollisionBody* body1, CollisionBody if (convexPairs.size() > 0 || concavePairs.size() > 0) { // Compute the middle-phase collision detection - computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput); + computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput, true); // Compute the narrow-phase collision detection and report contacts computeNarrowPhaseCollisionSnapshot(narrowPhaseInput, callback); @@ -1423,7 +1530,7 @@ void CollisionDetectionSystem::testCollision(CollisionBody* body, CollisionCallb if (convexPairs.size() > 0 || concavePairs.size() > 0) { // Compute the middle-phase collision detection - computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput); + computeMiddlePhaseCollisionSnapshot(convexPairs, concavePairs, narrowPhaseInput, true); // Compute the narrow-phase collision detection and report contacts computeNarrowPhaseCollisionSnapshot(narrowPhaseInput, callback); @@ -1439,7 +1546,7 @@ void CollisionDetectionSystem::testCollision(CollisionCallback& callback) { computeBroadPhase(); // Compute the middle-phase collision detection - computeMiddlePhase(narrowPhaseInput); + computeMiddlePhase(narrowPhaseInput, true); // Compute the narrow-phase collision detection and report contacts computeNarrowPhaseCollisionSnapshot(narrowPhaseInput, callback); diff --git a/testbed/src/Scene.cpp b/testbed/src/Scene.cpp index 5d0dfcce..c3883d04 100644 --- a/testbed/src/Scene.cpp +++ b/testbed/src/Scene.cpp @@ -207,3 +207,7 @@ void Scene::onContact(const rp3d::CollisionCallback::CallbackData& callbackData) } } } + +void Scene::onTrigger(const rp3d::OverlapCallback::CallbackData& callbackData) { + +} diff --git a/testbed/src/Scene.h b/testbed/src/Scene.h index 9ae7b203..a2d920c8 100644 --- a/testbed/src/Scene.h +++ b/testbed/src/Scene.h @@ -238,6 +238,8 @@ class Scene : public rp3d::EventListener { /// Called when some contacts occur virtual void onContact(const rp3d::CollisionCallback::CallbackData& callbackData) override; + + virtual void onTrigger(const rp3d::OverlapCallback::CallbackData& callbackData) override; }; // Called when a keyboard event occurs