/******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * * Copyright (c) 2010-2022 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * * In no event will the authors be held liable for any damages arising from the * * use of this software. * * * * Permission is granted to anyone to use this software for any purpose, * * including commercial applications, and to alter it and redistribute it * * freely, subject to the following restrictions: * * * * 1. The origin of this software must not be misrepresented; you must not claim * * that you wrote the original software. If you use this software in a * * product, an acknowledgment in the product documentation would be * * appreciated but is not required. * * * * 2. Altered source versions must be plainly marked as such, and must not be * * misrepresented as being the original software. * * * * 3. This notice may not be removed or altered from any source distribution. * * * ********************************************************************************/ #ifndef REACTPHYSICS3D_COLLISION_DETECTION_SYSTEM_H #define REACTPHYSICS3D_COLLISION_DETECTION_SYSTEM_H // Libraries #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// ReactPhysics3D namespace namespace reactphysics3d { // Declarations class PhysicsWorld; class CollisionCallback; class OverlapCallback; class RaycastCallback; class ContactPoint; class MemoryManager; class EventListener; class CollisionDispatch; // Class CollisionDetectionSystem /** * This class computes the collision detection algorithms. We first * perform a broad-phase algorithm to know which pairs of bodies can * collide and then we run a narrow-phase algorithm to compute the * collision contacts between bodies. */ class CollisionDetectionSystem { private : using OverlappingPairMap = Map, OverlappingPair*>; // -------------------- Constants -------------------- // /// Maximum number of contact points in a reduced contact manifold static const int8 MAX_CONTACT_POINTS_IN_MANIFOLD = 4; // -------------------- Attributes -------------------- // /// Memory manager MemoryManager& mMemoryManager; /// Reference the collider components ColliderComponents& mCollidersComponents; /// Reference to the rigid bodies components RigidBodyComponents& mRigidBodyComponents; /// Collision Detection Dispatch configuration CollisionDispatch mCollisionDispatch; /// Pointer to the physics world PhysicsWorld* mWorld; /// Set of pair of bodies that cannot collide between each other Set mNoCollisionPairs; /// Broad-phase overlapping pairs OverlappingPairs mOverlappingPairs; /// Overlapping nodes during broad-phase computation Array> mBroadPhaseOverlappingNodes; /// Broad-phase system BroadPhaseSystem mBroadPhaseSystem; /// Map a broad-phase id with the corresponding entity of the collider Map mMapBroadPhaseIdToColliderEntity; /// Narrow-phase collision detection input NarrowPhaseInput mNarrowPhaseInput; /// Array of the potential contact points Array mPotentialContactPoints; /// Array of the potential contact manifolds Array mPotentialContactManifolds; /// First array of narrow-phase pair contacts Array mContactPairs1; /// Second array of narrow-phase pair contacts Array mContactPairs2; /// Pointer to the array of contact pairs of the previous frame (either mContactPairs1 or mContactPairs2) Array* mPreviousContactPairs; /// Pointer to the array of contact pairs of the current frame (either mContactPairs1 or mContactPairs2) Array* mCurrentContactPairs; /// Array of lost contact pairs (contact pairs in contact in previous frame but not in the current one) Array mLostContactPairs; /// Pointer to the map of overlappingPairId to the index of contact pair of the previous frame /// (either mMapPairIdToContactPairIndex1 or mMapPairIdToContactPairIndex2) Map mPreviousMapPairIdToContactPairIndex; /// First array with the contact manifolds Array mContactManifolds1; /// Second array with the contact manifolds Array mContactManifolds2; /// Pointer to the array of contact manifolds from the previous frame (either mContactManifolds1 or mContactManifolds2) Array* mPreviousContactManifolds; /// Pointer to the array of contact manifolds from the current frame (either mContactManifolds1 or mContactManifolds2) Array* mCurrentContactManifolds; /// Second array of contact points (contact points from either the current frame of the previous frame) Array mContactPoints1; /// Second array of contact points (contact points from either the current frame of the previous frame) Array mContactPoints2; /// Pointer to the contact points of the previous frame (either mContactPoints1 or mContactPoints2) Array* mPreviousContactPoints; /// Pointer to the contact points of the current frame (either mContactPoints1 or mContactPoints2) Array* mCurrentContactPoints; /// Array with the indices of all the contact pairs that have at least one CollisionBody Array mCollisionBodyContactPairsIndices; /// Number of potential contact manifolds in the previous frame uint32 mNbPreviousPotentialContactManifolds; /// Number of potential contact points in the previous frame uint32 mNbPreviousPotentialContactPoints; /// Reference to the half-edge structure of the triangle polyhedron HalfEdgeStructure& mTriangleHalfEdgeStructure; #ifdef IS_RP3D_PROFILING_ENABLED /// Pointer to the profiler Profiler* mProfiler; #endif // -------------------- Methods -------------------- // /// Compute the broad-phase collision detection void computeBroadPhase(); /// Compute the middle-phase collision detection void computeMiddlePhase(NarrowPhaseInput& narrowPhaseInput, bool needToReportContacts); // Compute the middle-phase collision detection void computeMiddlePhaseCollisionSnapshot(Array& convexPairs, Array& concavePairs, NarrowPhaseInput& narrowPhaseInput, bool reportContacts); /// Compute the narrow-phase collision detection void computeNarrowPhase(); /// Compute the narrow-phase collision detection for the testOverlap() methods. bool computeNarrowPhaseOverlapSnapshot(NarrowPhaseInput& narrowPhaseInput, OverlapCallback* callback); /// Compute the narrow-phase collision detection for the testCollision() methods bool computeNarrowPhaseCollisionSnapshot(NarrowPhaseInput& narrowPhaseInput, CollisionCallback& callback); /// Process the potential contacts after narrow-phase collision detection void computeOverlapSnapshotContactPairs(NarrowPhaseInput& narrowPhaseInput, Array& contactPairs) const; /// Convert the potential contact into actual contacts void computeOverlapSnapshotContactPairs(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, Array& contactPairs, Set& setOverlapContactPairId) const; /// Take an array of overlapping nodes in the broad-phase and create new overlapping pairs if necessary void updateOverlappingPairs(const Array >& overlappingNodes); /// 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(OverlappingPairs::OverlappingPair& overlappingPair); /// Execute the narrow-phase collision detection algorithm on batches bool testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& allocator); /// Compute the concave vs convex middle-phase algorithm for a given pair of bodies void computeConvexVsConcaveMiddlePhase(OverlappingPairs::ConcaveOverlappingPair& overlappingPair, MemoryAllocator& allocator, NarrowPhaseInput& narrowPhaseInput, bool reportContacts); /// Swap the previous and current contacts arrays void swapPreviousAndCurrentContacts(); /// Convert the potential contact into actual contacts void processPotentialContacts(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, bool updateLastFrameInfo, Array& potentialContactPoints, Array& potentialContactManifolds, Map& mapPairIdToContactPairIndex, Array* contactPairs); /// Process the potential contacts after narrow-phase collision detection void processAllPotentialContacts(NarrowPhaseInput& narrowPhaseInput, bool updateLastFrameInfo, Array& potentialContactPoints, Array& potentialContactManifolds, Array* contactPairs); /// Reduce the potential contact manifolds and contact points of the overlapping pair contacts void reducePotentialContactManifolds(Array* contactPairs, Array& potentialContactManifolds, const Array& potentialContactPoints) const; /// Create the actual contact manifolds and contacts points (from potential contacts) for a given contact pair void createContacts(); /// Add the contact pairs to the corresponding bodies void addContactPairsToBodies(); /// Compute the map from contact pairs ids to contact pair for the next frame void computeMapPreviousContactPairs(); /// 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(Array& contactPairs, Array &contactManifolds, Array& contactPoints, Array& potentialContactManifolds, Array& potentialContactPoints); /// Initialize the current contacts with the contacts from the previous frame (for warmstarting) void initContactsWithPreviousOnes(); /// Reduce the number of contact points of a potential contact manifold void reduceContactPoints(ContactManifoldInfo& manifold, const Transform& shape1ToWorldTransform, const Array& potentialContactPoints) const; /// Report contacts void reportContacts(CollisionCallback& callback, Array* contactPairs, Array* manifolds, Array* contactPoints, Array& lostContactPairs); /// Report all triggers void reportTriggers(EventListener& eventListener, Array* contactPairs, Array& lostContactPairs); /// Report all contacts for debug rendering void reportDebugRenderingContacts(Array* contactPairs, Array* manifolds, Array* contactPoints, Array& lostContactPairs); /// Return the largest depth of all the contact points of a potential manifold decimal computePotentialManifoldLargestContactDepth(const ContactManifoldInfo& manifold, const Array& potentialContactPoints) const; /// Process the potential contacts where one collion is a concave shape void processSmoothMeshContacts(OverlappingPair* pair); /// Filter the overlapping pairs to keep only the pairs where a given body is involved void filterOverlappingPairs(Entity bodyEntity, Array& convexPairs, Array& concavePairs) const; /// Filter the overlapping pairs to keep only the pairs where two given bodies are involved void filterOverlappingPairs(Entity body1Entity, Entity body2Entity, Array& convexPairs, Array& concavePairs) const; /// Remove an element in an array (and replace it by the last one in the array) void removeItemAtInArray(uint array[], uint8 index, uint8& arraySize) const; /// Remove the duplicated contact points in a given contact manifold void removeDuplicatedContactPointsInManifold(ContactManifoldInfo& manifold, const Array& potentialContactPoints) const; public : // -------------------- Methods -------------------- // /// Constructor CollisionDetectionSystem(PhysicsWorld* world, ColliderComponents& collidersComponents, TransformComponents& transformComponents, CollisionBodyComponents& collisionBodyComponents, RigidBodyComponents& rigidBodyComponents, MemoryManager& memoryManager, HalfEdgeStructure& triangleHalfEdgeStructure); /// Destructor ~CollisionDetectionSystem() = default; /// Deleted copy-constructor CollisionDetectionSystem(const CollisionDetectionSystem& collisionDetection) = delete; /// Deleted assignment operator CollisionDetectionSystem& operator=(const CollisionDetectionSystem& collisionDetection) = delete; /// Set the collision dispatch configuration CollisionDispatch& getCollisionDispatch(); /// Add a collider to the collision detection void addCollider(Collider* collider, const AABB& aabb); /// Remove a collider from the collision detection void removeCollider(Collider* collider); /// Update a collider (that has moved for instance) void updateCollider(Entity colliderEntity); /// Update all the enabled colliders void updateColliders(); /// Add a pair of bodies that cannot collide with each other void addNoCollisionPair(Entity body1Entity, Entity body2Entity); /// Remove a pair of bodies that cannot collide with each other void removeNoCollisionPair(Entity body1Entity, Entity body2Entity); /// Ask for a collision shape to be tested again during broad-phase. void askForBroadPhaseCollisionCheck(Collider* collider); /// Notify that the overlapping pairs where a given collider is involved need to be tested for overlap void notifyOverlappingPairsToTestOverlap(Collider* collider); /// Report contacts and triggers void reportContactsAndTriggers(); /// Compute the collision detection void computeCollisionDetection(); /// Ray casting method void raycast(RaycastCallback* raycastCallback, const Ray& ray, unsigned short raycastWithCategoryMaskBits) const; /// Return true if two bodies (collide) overlap bool testOverlap(CollisionBody* body1, CollisionBody* body2); /// Report all the bodies that overlap (collide) with the body in parameter void testOverlap(CollisionBody* body, OverlapCallback& callback); /// Report all the bodies that overlap (collide) in the world void testOverlap(OverlapCallback& overlapCallback); /// Test collision and report contacts between two bodies. void testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback); /// Test collision and report all the contacts involving the body in parameter void testCollision(CollisionBody* body, CollisionCallback& callback); /// Test collision and report contacts between each colliding bodies in the world void testCollision(CollisionCallback& callback); /// Return a reference to the memory manager MemoryManager& getMemoryManager() const; /// Return a pointer to the world PhysicsWorld* getWorld(); /// Return the world event listener EventListener* getWorldEventListener(); #ifdef IS_RP3D_PROFILING_ENABLED /// Set the profiler void setProfiler(Profiler* profiler); #endif /// Return the world-space AABB of a given collider const AABB getWorldAABB(const Collider* collider) const; // -------------------- Friendship -------------------- // friend class PhysicsWorld; friend class ConvexMeshShape; friend class RigidBody; friend class DebugRenderer; }; // Return a reference to the collision dispatch configuration RP3D_FORCE_INLINE CollisionDispatch& CollisionDetectionSystem::getCollisionDispatch() { return mCollisionDispatch; } // Add a body to the collision detection RP3D_FORCE_INLINE void CollisionDetectionSystem::addCollider(Collider* collider, const AABB& aabb) { // Add the body to the broad-phase mBroadPhaseSystem.addCollider(collider, aabb); int broadPhaseId = mCollidersComponents.getBroadPhaseId(collider->getEntity()); assert(!mMapBroadPhaseIdToColliderEntity.containsKey(broadPhaseId)); // Add the mapping between the collider broad-phase id and its entity mMapBroadPhaseIdToColliderEntity.add(Pair(broadPhaseId, collider->getEntity())); } // Remove a pair of bodies that cannot collide with each other RP3D_FORCE_INLINE void CollisionDetectionSystem::removeNoCollisionPair(Entity body1Entity, Entity body2Entity) { mNoCollisionPairs.remove(OverlappingPairs::computeBodiesIndexPair(body1Entity, body2Entity)); } // Ask for a collision shape to be tested again during broad-phase. /// We simply put the shape in the array of collision shape that have moved in the /// previous frame so that it is tested for collision again in the broad-phase. RP3D_FORCE_INLINE void CollisionDetectionSystem::askForBroadPhaseCollisionCheck(Collider* collider) { if (collider->getBroadPhaseId() != -1) { mBroadPhaseSystem.addMovedCollider(collider->getBroadPhaseId(), collider); } } // Return a pointer to the world RP3D_FORCE_INLINE PhysicsWorld* CollisionDetectionSystem::getWorld() { return mWorld; } // Return a reference to the memory manager RP3D_FORCE_INLINE MemoryManager& CollisionDetectionSystem::getMemoryManager() const { return mMemoryManager; } // Update a collider (that has moved for instance) RP3D_FORCE_INLINE void CollisionDetectionSystem::updateCollider(Entity colliderEntity) { // Update the collider component mBroadPhaseSystem.updateCollider(colliderEntity); } // Update all the enabled colliders RP3D_FORCE_INLINE void CollisionDetectionSystem::updateColliders() { mBroadPhaseSystem.updateColliders(); } #ifdef IS_RP3D_PROFILING_ENABLED // Set the profiler RP3D_FORCE_INLINE void CollisionDetectionSystem::setProfiler(Profiler* profiler) { mProfiler = profiler; mBroadPhaseSystem.setProfiler(profiler); mCollisionDispatch.setProfiler(profiler); mOverlappingPairs.setProfiler(profiler); } #endif } #endif