diff --git a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h
index bcad2c5b..73b44cb3 100644
--- a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h
+++ b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInfoBatch.h
@@ -59,7 +59,6 @@ struct NarrowPhaseInfoBatch {
         Entity colliderEntity2;
 
         /// Collision info of the previous frame
-        // TODO OPTI : Do we really need to use a pointer here ? why not flat object instead ?
         LastFrameCollisionInfo* lastFrameCollisionInfo;
 
         /// Memory allocator for the collision shape (Used to release TriangleShape memory in destructor)
@@ -124,9 +123,9 @@ struct NarrowPhaseInfoBatch {
         ~NarrowPhaseInfoBatch();
 
         /// 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,
+        void addNarrowPhaseInfo(uint64 pairId, Entity collider1, Entity collider2, CollisionShape* shape1,
                                                       CollisionShape* shape2, const Transform& shape1Transform, const Transform& shape2Transform,
-                                                      bool needToReportContacts, MemoryAllocator& shapeAllocator);
+                                                      bool needToReportContacts, LastFrameCollisionInfo* lastFrameInfo, MemoryAllocator& shapeAllocator);
 
         /// Return the number of objects in the batch
         uint getNbObjects() const;
@@ -151,13 +150,9 @@ RP3D_FORCE_INLINE uint NarrowPhaseInfoBatch::getNbObjects() const {
 }
 
 // Add shapes to be tested during narrow-phase collision detection into the batch
-RP3D_FORCE_INLINE void NarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1,
+RP3D_FORCE_INLINE void NarrowPhaseInfoBatch::addNarrowPhaseInfo(uint64 pairId, Entity collider1, Entity collider2, CollisionShape* shape1,
                                               CollisionShape* shape2, const Transform& shape1Transform, const Transform& shape2Transform,
-                                              bool needToReportContacts, MemoryAllocator& shapeAllocator) {
-
-    // Add a collision info for the two collision shapes into the overlapping pair (if not present yet)
-    // TODO OPTI : Can we better manage this
-    LastFrameCollisionInfo* lastFrameInfo = mOverlappingPairs.addLastFrameInfoIfNecessary(pairIndex, shape1->getId(), shape2->getId());
+                                              bool needToReportContacts, LastFrameCollisionInfo* lastFrameInfo, MemoryAllocator& shapeAllocator) {
 
     // Create a meta data object
     narrowPhaseInfos.emplace(pairId, collider1, collider2, lastFrameInfo, shapeAllocator, shape1Transform, shape2Transform, shape1, shape2, needToReportContacts);
diff --git a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h
index 639117a7..9f64574a 100644
--- a/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h
+++ b/include/reactphysics3d/collision/narrowphase/NarrowPhaseInput.h
@@ -66,10 +66,10 @@ class NarrowPhaseInput {
         NarrowPhaseInput(MemoryAllocator& allocator, OverlappingPairs& overlappingPairs);
 
         /// 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,
+        void addNarrowPhaseTest(uint64 pairId, Entity collider1, Entity collider2, CollisionShape* shape1,
                         CollisionShape* shape2, const Transform& shape1Transform,
                         const Transform& shape2Transform, NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, bool reportContacts,
-                        MemoryAllocator& shapeAllocator);
+                        LastFrameCollisionInfo* lastFrameInfo, MemoryAllocator& shapeAllocator);
 
         /// Get a reference to the sphere vs sphere batch
         NarrowPhaseInfoBatch& getSphereVsSphereBatch();
@@ -128,28 +128,29 @@ RP3D_FORCE_INLINE NarrowPhaseInfoBatch &NarrowPhaseInput::getConvexPolyhedronVsC
 }
 
 // Add shapes to be tested during narrow-phase collision detection into the batch
-RP3D_FORCE_INLINE void NarrowPhaseInput::addNarrowPhaseTest(uint64 pairId, uint64 pairIndex, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2,
+RP3D_FORCE_INLINE void NarrowPhaseInput::addNarrowPhaseTest(uint64 pairId, Entity collider1, Entity collider2, CollisionShape* shape1, CollisionShape* shape2,
                                           const Transform& shape1Transform, const Transform& shape2Transform,
-                                          NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, bool reportContacts, MemoryAllocator& shapeAllocator) {
+                                          NarrowPhaseAlgorithmType narrowPhaseAlgorithmType, bool reportContacts, LastFrameCollisionInfo* lastFrameInfo,
+                                          MemoryAllocator& shapeAllocator) {
 
     switch (narrowPhaseAlgorithmType) {
         case NarrowPhaseAlgorithmType::SphereVsSphere:
-            mSphereVsSphereBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator);
+            mSphereVsSphereBatch.addNarrowPhaseInfo(pairId, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, lastFrameInfo, shapeAllocator);
             break;
         case NarrowPhaseAlgorithmType::SphereVsCapsule:
-            mSphereVsCapsuleBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator);
+            mSphereVsCapsuleBatch.addNarrowPhaseInfo(pairId, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, lastFrameInfo, shapeAllocator);
             break;
         case NarrowPhaseAlgorithmType::CapsuleVsCapsule:
-            mCapsuleVsCapsuleBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator);
+            mCapsuleVsCapsuleBatch.addNarrowPhaseInfo(pairId, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, lastFrameInfo, shapeAllocator);
             break;
         case NarrowPhaseAlgorithmType::SphereVsConvexPolyhedron:
-            mSphereVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator);
+            mSphereVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, lastFrameInfo, shapeAllocator);
             break;
         case NarrowPhaseAlgorithmType::CapsuleVsConvexPolyhedron:
-            mCapsuleVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator);
+            mCapsuleVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, lastFrameInfo, shapeAllocator);
             break;
         case NarrowPhaseAlgorithmType::ConvexPolyhedronVsConvexPolyhedron:
-            mConvexPolyhedronVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, pairIndex, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, shapeAllocator);
+            mConvexPolyhedronVsConvexPolyhedronBatch.addNarrowPhaseInfo(pairId, collider1, collider2, shape1, shape2, shape1Transform, shape2Transform, reportContacts, lastFrameInfo, shapeAllocator);
             break;
         case NarrowPhaseAlgorithmType::None:
             // Must never happen
diff --git a/include/reactphysics3d/engine/OverlappingPairs.h b/include/reactphysics3d/engine/OverlappingPairs.h
index 206d43bc..e00dcd54 100644
--- a/include/reactphysics3d/engine/OverlappingPairs.h
+++ b/include/reactphysics3d/engine/OverlappingPairs.h
@@ -106,77 +106,199 @@ struct LastFrameCollisionInfo {
  */
 class OverlappingPairs {
 
+    public:
+
+        struct OverlappingPair {
+
+            /// Ids of the convex vs convex pairs
+            uint64 pairID;
+
+            /// Broad-phase Id of the first shape
+            // TODO OPTI : Is this used ?
+            int32 broadPhaseId1;
+
+            /// Broad-phase Id of the second shape
+            // TODO OPTI : Is this used ?
+            int32 broadPhaseId2;
+
+            /// Entity of the first collider of the convex vs convex pairs
+            Entity collider1;
+
+            /// Entity of the second collider of the convex vs convex pairs
+            Entity collider2;
+
+            /// True if we need to test if the convex vs convex overlapping pairs of shapes still overlap
+            bool needToTestOverlap;
+
+            /// Pointer to the narrow-phase algorithm
+            NarrowPhaseAlgorithmType narrowPhaseAlgorithmType;
+
+            /// True if the colliders of the overlapping pair were colliding in the previous frame
+            bool collidingInPreviousFrame;
+
+            /// True if the colliders of the overlapping pair are colliding in the current frame
+            bool collidingInCurrentFrame;
+
+            /// Constructor
+            OverlappingPair(uint64 pairId, int32 broadPhaseId1, int32 broadPhaseId2, Entity collider1, Entity collider2,
+                            NarrowPhaseAlgorithmType narrowPhaseAlgorithmType)
+               : pairID(pairId), broadPhaseId1(broadPhaseId1), broadPhaseId2(broadPhaseId2), collider1(collider1) , collider2(collider2),
+                 needToTestOverlap(false), narrowPhaseAlgorithmType(narrowPhaseAlgorithmType), collidingInPreviousFrame(false),
+                 collidingInCurrentFrame(false) {
+
+            }
+
+            /// Destructor
+            virtual ~OverlappingPair() = default;
+        };
+
+        // Overlapping pair between two convex colliders
+        struct ConvexOverlappingPair : public OverlappingPair {
+
+            /// Temporal coherence collision data for each overlapping collision shapes of this pair.
+            /// Temporal coherence data store collision information about the last frame.
+            /// If two convex shapes overlap, we have a single collision data but if one shape is concave,
+            /// we might have collision data for several overlapping triangles.
+            LastFrameCollisionInfo lastFrameCollisionInfo;
+
+            /// Constructor
+            ConvexOverlappingPair(uint64 pairId, int32 broadPhaseId1, int32 broadPhaseId2, Entity collider1, Entity collider2,
+                            NarrowPhaseAlgorithmType narrowPhaseAlgorithmType)
+              : OverlappingPair(pairId, broadPhaseId1, broadPhaseId2, collider1, collider2, narrowPhaseAlgorithmType) {
+
+            }
+        };
+
+        // Overlapping pair between two a convex collider and a concave collider
+        struct ConcaveOverlappingPair : public OverlappingPair {
+
+            private:
+
+                MemoryAllocator& mPoolAllocator;
+
+            public:
+
+                /// True if the first shape of the pair is convex
+                bool isShape1Convex;
+
+                /// Temporal coherence collision data for each overlapping collision shapes of this pair.
+                /// Temporal coherence data store collision information about the last frame.
+                /// If two convex shapes overlap, we have a single collision data but if one shape is concave,
+                /// we might have collision data for several overlapping triangles. The key in the map is the
+                /// shape Ids of the two collision shapes.
+                Map<uint64, LastFrameCollisionInfo*> lastFrameCollisionInfos;
+
+                /// Constructor
+                ConcaveOverlappingPair(uint64 pairId, int32 broadPhaseId1, int32 broadPhaseId2, Entity collider1, Entity collider2,
+                                NarrowPhaseAlgorithmType narrowPhaseAlgorithmType,
+                                bool isShape1Convex, MemoryAllocator& poolAllocator, MemoryAllocator& heapAllocator)
+                  : OverlappingPair(pairId, broadPhaseId1, broadPhaseId2, collider1, collider2, narrowPhaseAlgorithmType), mPoolAllocator(poolAllocator),
+                    isShape1Convex(isShape1Convex), lastFrameCollisionInfos(heapAllocator, 16) {
+
+                }
+
+                // Destroy all the LastFrameCollisionInfo objects
+                void destroyLastFrameCollisionInfos() {
+
+                    for (auto it = lastFrameCollisionInfos.begin(); it != lastFrameCollisionInfos.end(); ++it) {
+
+                        // Call the destructor
+                        it->second->LastFrameCollisionInfo::~LastFrameCollisionInfo();
+
+                        // Release memory
+                        mPoolAllocator.release(it->second, sizeof(LastFrameCollisionInfo));
+                    }
+
+                    lastFrameCollisionInfos.clear();
+                }
+
+                // Add a new last frame collision info if it does not exist for the given shapes already
+                LastFrameCollisionInfo* addLastFrameInfoIfNecessary(uint32 shapeId1, uint32 shapeId2) {
+
+                    uint32 maxShapeId = shapeId1;
+                    uint32 minShapeId = shapeId2;
+                    if (shapeId1 < shapeId2) {
+                       maxShapeId = shapeId2;
+                       minShapeId = shapeId1;
+                    }
+
+                    // Try to get the corresponding last frame collision info
+                    const uint64 shapesId = pairNumbers(maxShapeId, minShapeId);
+
+                    // If there is no collision info for those two shapes already
+                    auto it = lastFrameCollisionInfos.find(shapesId);
+                    if (it == lastFrameCollisionInfos.end()) {
+
+                        LastFrameCollisionInfo* lastFrameInfo = new (mPoolAllocator.allocate(sizeof(LastFrameCollisionInfo))) LastFrameCollisionInfo();
+
+                        // Add it into the map of collision infos
+                        lastFrameCollisionInfos.add(Pair<uint64, LastFrameCollisionInfo*>(shapesId, lastFrameInfo));
+
+                        return lastFrameInfo;
+                    }
+                    else {
+
+                       // The existing collision info is not obsolete
+                       it->second->isObsolete = false;
+
+                       return it->second;
+                    }
+                }
+
+                /// Clear the obsolete LastFrameCollisionInfo objects
+                void clearObsoleteLastFrameInfos() {
+
+                    // For each last frame collision info
+                    for (auto it = lastFrameCollisionInfos.begin(); it != lastFrameCollisionInfos.end(); ) {
+
+                        // If the collision info is obsolete
+                        if (it->second->isObsolete) {
+
+                            // Call the destructor
+                            it->second->LastFrameCollisionInfo::~LastFrameCollisionInfo();
+
+                            // Release memory
+                            mPoolAllocator.release(it->second, sizeof(LastFrameCollisionInfo));
+
+                            it = lastFrameCollisionInfos.remove(it);
+                        }
+                        else {  // If the collision info is not obsolete
+
+                            // Do not delete it but mark it as obsolete
+                            it->second->isObsolete = true;
+
+                            ++it;
+                        }
+                    }
+                }
+
+            /// Destructor
+            virtual ~ConcaveOverlappingPair() {
+
+            }
+        };
+
     private:
 
-        // -------------------- Constants -------------------- //
-
-
-        /// Number of pairs to allocated at the beginning
-        const uint32 INIT_NB_ALLOCATED_PAIRS = 10;
-
         // -------------------- Attributes -------------------- //
 
-        /// Persistent memory allocator
-        MemoryAllocator& mPersistentAllocator;
+        /// Pool memory allocator
+        MemoryAllocator& mPoolAllocator;
 
-        /// Memory allocator used to allocated memory for the ContactManifoldInfo and ContactPointInfo
-        // TODO OPTI : Do we need to keep this ?
-        MemoryAllocator& mTempMemoryAllocator;
+        /// Heap memory allocator
+        MemoryAllocator& mHeapAllocator;
 
-        /// Current number of components
-        uint64 mNbPairs;
+        /// List of convex vs convex overlapping pairs
+        List<ConvexOverlappingPair> mConvexPairs;
 
-        /// Index in the array of the first convex vs concave pair
-        uint64 mConcavePairsStartIndex;
-
-        /// Size (in bytes) of a single pair
-        size_t mPairDataSize;
-
-        /// Number of allocated pairs
-        uint64 mNbAllocatedPairs;
-
-        /// Allocated memory for all the data of the pairs
-        void* mBuffer;
+        /// List of convex vs concave overlapping pairs
+        List<ConcaveOverlappingPair> mConcavePairs;
 
         /// Map a pair id to the internal array index
-        Map<uint64, uint64> mMapPairIdToPairIndex;
+        Map<uint64, uint64> mMapConvexPairIdToPairIndex;
 
-        /// Ids of the convex vs convex pairs
-        uint64* mPairIds;
-
-        /// Array with the broad-phase Ids of the first shape
-        int32* mPairBroadPhaseId1;
-
-        /// Array with the broad-phase Ids of the second shape
-        int32* mPairBroadPhaseId2;
-
-        /// Array of Entity of the first collider of the convex vs convex pairs
-        Entity* mColliders1;
-
-        /// Array of Entity of the second collider of the convex vs convex pairs
-        Entity* mColliders2;
-
-        /// Temporal coherence collision data for each overlapping collision shapes of this pair.
-        /// Temporal coherence data store collision information about the last frame.
-        /// If two convex shapes overlap, we have a single collision data but if one shape is concave,
-        /// we might have collision data for several overlapping triangles. The key in the map is the
-        /// shape Ids of the two collision shapes.
-        Map<uint64, LastFrameCollisionInfo*>* mLastFrameCollisionInfos;
-
-        /// True if we need to test if the convex vs convex overlapping pairs of shapes still overlap
-        bool* mNeedToTestOverlap;
-
-        /// Array with the pointer to the narrow-phase algorithm for each overlapping pair
-        NarrowPhaseAlgorithmType* mNarrowPhaseAlgorithmType;
-
-        /// 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;
+        /// Map a pair id to the internal array index
+        Map<uint64, uint64> mMapConcavePairIdToPairIndex;
 
         /// Reference to the colliders components
         ColliderComponents& mColliderComponents;
@@ -202,15 +324,6 @@ class OverlappingPairs {
 
         // -------------------- Methods -------------------- //
 
-        /// Allocate memory for a given number of pairs
-        void allocate(uint64 nbPairsToAllocate);
-
-        /// Compute the index where we need to insert the new pair
-        uint64 prepareAddPair(bool isConvexVsConvex);
-
-        /// Destroy a pair at a given index
-        void destroyPair(uint64 index);
-
         // Move a pair from a source to a destination index in the pairs array
         void movePairToIndex(uint64 srcIndex, uint64 destIndex);
 
@@ -222,8 +335,8 @@ class OverlappingPairs {
         // -------------------- Methods -------------------- //
 
         /// Constructor
-        OverlappingPairs(MemoryAllocator& persistentMemoryAllocator, MemoryAllocator& temporaryMemoryAllocator,
-                         ColliderComponents& colliderComponents, CollisionBodyComponents& collisionBodyComponents,
+        OverlappingPairs(MemoryManager& memoryManager,  ColliderComponents& colliderComponents,
+                         CollisionBodyComponents& collisionBodyComponents,
                          RigidBodyComponents& rigidBodyComponents, Set<bodypair>& noCollisionPairs,
                          CollisionDispatch& collisionDispatch);
 
@@ -237,40 +350,13 @@ class OverlappingPairs {
         OverlappingPairs& operator=(const OverlappingPairs& pair) = delete;
 
         /// Add an overlapping pair
-        uint64 addPair(Collider* shape1, Collider* shape2);
+        uint64 addPair(uint32 collider1Index, uint32 collider2Index, bool isConvexVsConvex);
 
         /// Remove a component at a given index
         void removePair(uint64 pairId);
 
-        /// Return the number of pairs
-        uint64 getNbPairs() const;
-
-        /// Return the number of convex vs convex pairs
-        uint64 getNbConvexVsConvexPairs() const;
-
-        /// Return the number of convex vs concave pairs
-        uint64 getNbConvexVsConcavePairs() const;
-
-        /// Return the starting index of the convex vs concave pairs
-        uint64 getConvexVsConcavePairsStartIndex() const;
-
-        /// Return the entity of the first collider
-        Entity getCollider1(uint64 pairId) const;
-
-        /// Return the entity of the second collider
-        Entity getCollider2(uint64 pairId) const;
-
-        /// Return the index of a given overlapping pair in the internal array
-        uint64 getPairIndex(uint64 pairId) const;
-
-        /// Return the last frame collision info
-        LastFrameCollisionInfo* getLastFrameCollisionInfo(uint64, uint64 shapesId);
-
-        /// Return a reference to the temporary memory allocator
-        MemoryAllocator& getTemporaryAllocator();
-
-        /// Add a new last frame collision info if it does not exist for the given shapes already
-        LastFrameCollisionInfo* addLastFrameInfoIfNecessary(uint64 pairIndex, uint32 shapeId1, uint32 shapeId2);
+        /// Remove a pair
+        void removePair(uint64 pairIndex, bool isConvexVsConvex);
 
         /// Delete all the obsolete last frame collision info
         void clearObsoleteLastFrameCollisionInfos();
@@ -284,11 +370,8 @@ class OverlappingPairs {
         /// 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);
+        /// Return a reference to an overlapping pair
+        OverlappingPair* getOverlappingPair(uint64 pairId);
 
 #ifdef IS_RP3D_PROFILING_ENABLED
 
@@ -303,41 +386,6 @@ class OverlappingPairs {
         friend class CollisionDetectionSystem;
 };
 
-// Return the entity of the first collider
-RP3D_FORCE_INLINE Entity OverlappingPairs::getCollider1(uint64 pairId) const {
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-    assert(mMapPairIdToPairIndex[pairId] < mNbPairs);
-    return mColliders1[mMapPairIdToPairIndex[pairId]];
-}
-
-// Return the entity of the second collider
-RP3D_FORCE_INLINE Entity OverlappingPairs::getCollider2(uint64 pairId) const {
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-    assert(mMapPairIdToPairIndex[pairId] < mNbPairs);
-    return mColliders2[mMapPairIdToPairIndex[pairId]];
-}
-
-// Return the index of a given overlapping pair in the internal array
-RP3D_FORCE_INLINE uint64 OverlappingPairs::getPairIndex(uint64 pairId) const {
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-    return mMapPairIdToPairIndex[pairId];
-}
-
-// Return the last frame collision info for a given shape id or nullptr if none is found
-RP3D_FORCE_INLINE LastFrameCollisionInfo* OverlappingPairs::getLastFrameCollisionInfo(uint64 pairId, uint64 shapesId) {
-
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-    const uint64 index = mMapPairIdToPairIndex[pairId];
-    assert(index < mNbPairs);
-
-    Map<uint64, LastFrameCollisionInfo*>::Iterator it = mLastFrameCollisionInfos[index].find(shapesId);
-    if (it != mLastFrameCollisionInfos[index].end()) {
-        return it->second;
-    }
-
-    return nullptr;
-}
-
 // Return the pair of bodies index
 RP3D_FORCE_INLINE bodypair OverlappingPairs::computeBodiesIndexPair(Entity body1Entity, Entity body2Entity) {
 
@@ -349,47 +397,33 @@ RP3D_FORCE_INLINE bodypair OverlappingPairs::computeBodiesIndexPair(Entity body1
     return indexPair;
 }
 
-// Return the number of pairs
-RP3D_FORCE_INLINE uint64 OverlappingPairs::getNbPairs() const {
-    return mNbPairs;
-}
-
-// Return the number of convex vs convex pairs
-RP3D_FORCE_INLINE uint64 OverlappingPairs::getNbConvexVsConvexPairs() const {
-   return mConcavePairsStartIndex;
-}
-
-// Return the number of convex vs concave pairs
-RP3D_FORCE_INLINE uint64 OverlappingPairs::getNbConvexVsConcavePairs() const {
-   return mNbPairs - mConcavePairsStartIndex;
-}
-
-// Return the starting index of the convex vs concave pairs
-RP3D_FORCE_INLINE uint64 OverlappingPairs::getConvexVsConcavePairsStartIndex() const {
-   return mConcavePairsStartIndex;
-}
-
-// Return a reference to the temporary memory allocator
-RP3D_FORCE_INLINE MemoryAllocator& OverlappingPairs::getTemporaryAllocator() {
-    return mTempMemoryAllocator;
-}
-
 // Set if we need to test a given pair for overlap
 RP3D_FORCE_INLINE void OverlappingPairs::setNeedToTestOverlap(uint64 pairId, bool needToTestOverlap) {
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-    mNeedToTestOverlap[mMapPairIdToPairIndex[pairId]] = needToTestOverlap;
+
+    assert(mMapConvexPairIdToPairIndex.containsKey(pairId) || mMapConcavePairIdToPairIndex.containsKey(pairId));
+
+    auto it = mMapConvexPairIdToPairIndex.find(pairId);
+    if (it != mMapConvexPairIdToPairIndex.end()) {
+        mConvexPairs[it->second].needToTestOverlap = needToTestOverlap;
+    }
+    else {
+        mConcavePairs[mMapConcavePairIdToPairIndex[pairId]].needToTestOverlap = needToTestOverlap;
+    }
 }
 
-// Return true if the two colliders of the pair were already colliding the previous frame
-RP3D_FORCE_INLINE bool OverlappingPairs::getCollidingInPreviousFrame(uint64 pairId) const {
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-    return mCollidingInPreviousFrame[mMapPairIdToPairIndex[pairId]];
-}
+// Return a reference to an overlapping pair
+RP3D_FORCE_INLINE OverlappingPairs::OverlappingPair* OverlappingPairs::getOverlappingPair(uint64 pairId) {
 
-// Set to true if the two colliders of the pair were already colliding the previous frame
-RP3D_FORCE_INLINE void OverlappingPairs::setCollidingInPreviousFrame(uint64 pairId, bool wereCollidingInPreviousFrame) {
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-    mCollidingInPreviousFrame[mMapPairIdToPairIndex[pairId]] = wereCollidingInPreviousFrame;
+    auto it = mMapConvexPairIdToPairIndex.find(pairId);
+    if (it != mMapConvexPairIdToPairIndex.end()) {
+        return &(mConvexPairs[it->second]);
+    }
+    it = mMapConcavePairIdToPairIndex.find(pairId);
+    if (it != mMapConcavePairIdToPairIndex.end()) {
+        return &(mConcavePairs[it->second]);
+    }
+
+    return nullptr;
 }
 
 #ifdef IS_RP3D_PROFILING_ENABLED
diff --git a/include/reactphysics3d/systems/CollisionDetectionSystem.h b/include/reactphysics3d/systems/CollisionDetectionSystem.h
index 683df3cc..5cb28461 100644
--- a/include/reactphysics3d/systems/CollisionDetectionSystem.h
+++ b/include/reactphysics3d/systems/CollisionDetectionSystem.h
@@ -198,13 +198,13 @@ class CollisionDetectionSystem {
         void removeNonOverlappingPairs();
 
         /// Add a lost contact pair (pair of colliders that are not in contact anymore)
-        void addLostContactPair(uint64 overlappingPairIndex);
+        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(uint64 pairIndex, MemoryAllocator& allocator,
+        void computeConvexVsConcaveMiddlePhase(OverlappingPairs::ConcaveOverlappingPair& overlappingPair, MemoryAllocator& allocator,
                                                NarrowPhaseInput& narrowPhaseInput);
 
         /// Swap the previous and current contacts lists
diff --git a/src/collision/broadphase/DynamicAABBTree.cpp b/src/collision/broadphase/DynamicAABBTree.cpp
index a5a7ad21..655e3a1e 100644
--- a/src/collision/broadphase/DynamicAABBTree.cpp
+++ b/src/collision/broadphase/DynamicAABBTree.cpp
@@ -640,6 +640,9 @@ void DynamicAABBTree::reportAllShapesOverlappingWithAABB(const AABB& aabb, List<
         // Get the next node ID to visit
         const int32 nodeIDToVisit = stack.pop();
 
+        assert(nodeIDToVisit >= 0);
+        assert(nodeIDToVisit < mNbAllocatedNodes);
+
         // Skip it if it is a null node
         if (nodeIDToVisit == TreeNode::NULL_TREE_NODE) continue;
 
diff --git a/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp b/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp
index d0266741..dcde890e 100644
--- a/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp
+++ b/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp
@@ -52,11 +52,12 @@ void NarrowPhaseInfoBatch::reserveMemory() {
 // Clear all the objects in the batch
 void NarrowPhaseInfoBatch::clear() {
 
-    // TODO OPTI : Better manage this
     for (uint i=0; i < narrowPhaseInfos.size(); i++) {
 
         assert(narrowPhaseInfos[i].nbContactPoints == 0);
 
+        // TODO OPTI : Better manage this
+
         // Release the memory of the TriangleShape (this memory was allocated in the
         // MiddlePhaseTriangleCallback::testTriangle() method)
         if (narrowPhaseInfos[i].collisionShape1->getName() == CollisionShapeName::TRIANGLE) {
diff --git a/src/collision/shapes/ConcaveMeshShape.cpp b/src/collision/shapes/ConcaveMeshShape.cpp
index c395b0ec..f977b752 100644
--- a/src/collision/shapes/ConcaveMeshShape.cpp
+++ b/src/collision/shapes/ConcaveMeshShape.cpp
@@ -139,7 +139,7 @@ void ConcaveMeshShape::computeOverlappingTriangles(const AABB& localAABB, List<V
     aabb.applyScale(Vector3(decimal(1.0) / mScale.x, decimal(1.0) / mScale.y, decimal(1.0) / mScale.z));
 
     // Compute the nodes of the internal AABB tree that are overlapping with the AABB
-    List<int> overlappingNodes(allocator);
+    List<int> overlappingNodes(allocator, 64);
     mDynamicAABBTree.reportAllShapesOverlappingWithAABB(aabb, overlappingNodes);
 
     const uint nbOverlappingNodes = overlappingNodes.size();
diff --git a/src/collision/shapes/HeightFieldShape.cpp b/src/collision/shapes/HeightFieldShape.cpp
index 7c2e99cd..dd056ca3 100644
--- a/src/collision/shapes/HeightFieldShape.cpp
+++ b/src/collision/shapes/HeightFieldShape.cpp
@@ -236,9 +236,9 @@ bool HeightFieldShape::raycast(const Ray& ray, RaycastInfo& raycastInfo, Collide
     const AABB rayAABB(Vector3::min(ray.point1, rayEnd), Vector3::max(ray.point1, rayEnd));
 
     // Compute the triangles overlapping with the ray AABB
-    List<Vector3> triangleVertices(allocator);
-    List<Vector3> triangleVerticesNormals(allocator);
-    List<uint> shapeIds(allocator);
+    List<Vector3> triangleVertices(allocator, 64);
+    List<Vector3> triangleVerticesNormals(allocator, 64);
+    List<uint> shapeIds(allocator, 64);
     computeOverlappingTriangles(rayAABB, triangleVertices, triangleVerticesNormals, shapeIds, allocator);
 
     assert(triangleVertices.size() == triangleVerticesNormals.size());
diff --git a/src/engine/OverlappingPairs.cpp b/src/engine/OverlappingPairs.cpp
index 543a32ef..c0ad16ef 100644
--- a/src/engine/OverlappingPairs.cpp
+++ b/src/engine/OverlappingPairs.cpp
@@ -30,459 +30,194 @@
 #include <reactphysics3d/collision/ContactPointInfo.h>
 #include <reactphysics3d/collision/narrowphase/NarrowPhaseAlgorithm.h>
 #include <reactphysics3d/collision/narrowphase/CollisionDispatch.h>
+#include <reactphysics3d/memory/MemoryManager.h>
 
 using namespace reactphysics3d;
 
 // Constructor
-OverlappingPairs::OverlappingPairs(MemoryAllocator& persistentMemoryAllocator, MemoryAllocator& temporaryMemoryAllocator, ColliderComponents &colliderComponents,
+OverlappingPairs::OverlappingPairs(MemoryManager& memoryManager, ColliderComponents& colliderComponents,
                                    CollisionBodyComponents& collisionBodyComponents, RigidBodyComponents& rigidBodyComponents, Set<bodypair> &noCollisionPairs, CollisionDispatch &collisionDispatch)
-                : mPersistentAllocator(persistentMemoryAllocator), mTempMemoryAllocator(temporaryMemoryAllocator),
-                  mNbPairs(0), mConcavePairsStartIndex(0), mPairDataSize(sizeof(uint64) + sizeof(int32) + sizeof(int32) + sizeof(Entity) +
-                                                                         sizeof(Entity) + sizeof(Map<uint64, LastFrameCollisionInfo*>) +
-                                                                         sizeof(bool) + sizeof(NarrowPhaseAlgorithmType) +
-                                                                         sizeof(bool) + sizeof(bool) + sizeof(bool)),
-                  mNbAllocatedPairs(0), mBuffer(nullptr),
-                  mMapPairIdToPairIndex(persistentMemoryAllocator),
+                : mPoolAllocator(memoryManager.getPoolAllocator()), mHeapAllocator(memoryManager.getHeapAllocator()), mConvexPairs(memoryManager.getHeapAllocator()),
+                  mConcavePairs(memoryManager.getHeapAllocator()), mMapConvexPairIdToPairIndex(memoryManager.getHeapAllocator()), mMapConcavePairIdToPairIndex(memoryManager.getHeapAllocator()),
                   mColliderComponents(colliderComponents), mCollisionBodyComponents(collisionBodyComponents),
                   mRigidBodyComponents(rigidBodyComponents), mNoCollisionPairs(noCollisionPairs), mCollisionDispatch(collisionDispatch) {
     
-    // Allocate memory for the components data
-    allocate(INIT_NB_ALLOCATED_PAIRS);
 }
 
 // Destructor
 OverlappingPairs::~OverlappingPairs() {
 
-    // If there are allocated pairs
-    if (mNbAllocatedPairs > 0) {
+    // Destroy the convex pairs
+    while (mConvexPairs.size() > 0) {
 
-        // Destroy all the remaining pairs
-        for (uint32 i = 0; i < mNbPairs; i++) {
-
-            // Remove all the remaining last frame collision info
-            for (auto it = mLastFrameCollisionInfos[i].begin(); it != mLastFrameCollisionInfos[i].end(); ++it) {
-
-                // Call the constructor
-                it->second->~LastFrameCollisionInfo();
-
-                // Release memory
-                mPersistentAllocator.release(it->second, sizeof(LastFrameCollisionInfo));
-            }
-
-            // Remove the involved overlapping pair to the two colliders
-            assert(mColliderComponents.getOverlappingPairs(mColliders1[i]).find(mPairIds[i]) != mColliderComponents.getOverlappingPairs(mColliders1[i]).end());
-            assert(mColliderComponents.getOverlappingPairs(mColliders2[i]).find(mPairIds[i]) != mColliderComponents.getOverlappingPairs(mColliders2[i]).end());
-            mColliderComponents.getOverlappingPairs(mColliders1[i]).remove(mPairIds[i]);
-            mColliderComponents.getOverlappingPairs(mColliders2[i]).remove(mPairIds[i]);
-
-            destroyPair(i);
-        }
-
-        // Size for the data of a single pair (in bytes)
-        const size_t totalSizeBytes = mNbAllocatedPairs * mPairDataSize;
-
-        // Release the allocated memory
-        mPersistentAllocator.release(mBuffer, totalSizeBytes);
-    }
-}
-
-// Compute the index where we need to insert the new pair
-uint64 OverlappingPairs::prepareAddPair(bool isConvexVsConvex) {
-
-    // If we need to allocate more components
-    if (mNbPairs == mNbAllocatedPairs) {
-        allocate(mNbAllocatedPairs * 2);
+        removePair(mConvexPairs.size() - 1, true);
     }
 
-    uint64 index;
+    // Destroy the concave pairs
+    while (mConcavePairs.size() > 0) {
 
-    // If the pair to add is not convex vs convex or there are no concave pairs yet
-    if (!isConvexVsConvex) {
-
-        // Add the component at the end of the array
-        index = mNbPairs;
+        removePair(mConcavePairs.size() - 1, false);
     }
-    // If the pair to add is convex vs convex
-    else {
-
-        // If there already are convex vs concave pairs
-        if (mConcavePairsStartIndex != mNbPairs) {
-
-            // Move the first convex vs concave pair to the end of the array
-            movePairToIndex(mConcavePairsStartIndex, mNbPairs);
-        }
-
-        index = mConcavePairsStartIndex;
-
-        mConcavePairsStartIndex++;
-    }
-
-    return index;
 }
 
 // Remove a component at a given index
 void OverlappingPairs::removePair(uint64 pairId) {
 
-    RP3D_PROFILE("OverlappingPairs::removePair()", mProfiler);
+    assert(mMapConvexPairIdToPairIndex.containsKey(pairId) || mMapConcavePairIdToPairIndex.containsKey(pairId));
 
-    assert(mMapPairIdToPairIndex.containsKey(pairId));
-
-    uint64 index = mMapPairIdToPairIndex[pairId];
-    assert(index < mNbPairs);
-
-    // We want to keep the arrays tightly packed. Therefore, when a pair is removed,
-    // we replace it with the last element of the array. But we need to make sure that convex
-    // and concave pairs stay grouped together.
-
-    // Remove all the remaining last frame collision info
-    for (auto it = mLastFrameCollisionInfos[index].begin(); it != mLastFrameCollisionInfos[index].end(); ++it) {
-
-        // Call the constructor
-        it->second->~LastFrameCollisionInfo();
-
-        // Release memory
-        mPersistentAllocator.release(it->second, sizeof(LastFrameCollisionInfo));
+    auto it = mMapConvexPairIdToPairIndex.find(pairId);
+    if (it != mMapConvexPairIdToPairIndex.end()) {
+        removePair(it->second, true);
     }
-
-    // Remove the involved overlapping pair to the two colliders
-    assert(mColliderComponents.getOverlappingPairs(mColliders1[index]).find(pairId) != mColliderComponents.getOverlappingPairs(mColliders1[index]).end());
-    assert(mColliderComponents.getOverlappingPairs(mColliders2[index]).find(pairId) != mColliderComponents.getOverlappingPairs(mColliders2[index]).end());
-    mColliderComponents.getOverlappingPairs(mColliders1[index]).remove(pairId);
-    mColliderComponents.getOverlappingPairs(mColliders2[index]).remove(pairId);
-
-    // Destroy the pair
-    destroyPair(index);
-
-    // If the pair to remove is convex vs concave
-    if (index >= mConcavePairsStartIndex) {
-
-        // If the pair is not the last one
-        if (index != mNbPairs - 1) {
-
-            // We replace it by the last convex vs concave pair
-            movePairToIndex(mNbPairs - 1, index);
-        }
+    else {
+        removePair(mMapConcavePairIdToPairIndex[pairId], false);
     }
-    else {   // If the pair to remove is convex vs convex
-
-        // If it not the last convex vs convex pair
-        if (index != mConcavePairsStartIndex - 1) {
-
-            // We replace it by the last convex vs convex pair
-            movePairToIndex(mConcavePairsStartIndex - 1, index);
-        }
-
-        // If there are convex vs concave pairs at the end
-        if (mConcavePairsStartIndex != mNbPairs) {
-
-            // We replace the last convex vs convex pair by the last convex vs concave pair
-            movePairToIndex(mNbPairs - 1, mConcavePairsStartIndex - 1);
-        }
-
-        mConcavePairsStartIndex--;
-    }
-
-    mNbPairs--;
-
-    assert(mConcavePairsStartIndex <= mNbPairs);
-    assert(mNbPairs == static_cast<uint32>(mMapPairIdToPairIndex.size()));
 }
 
-// Allocate memory for a given number of pairs
-void OverlappingPairs::allocate(uint64 nbPairsToAllocate) {
+// Remove a component at a given index
+void OverlappingPairs::removePair(uint64 pairIndex, bool isConvexVsConvex) {
 
-    assert(nbPairsToAllocate > mNbAllocatedPairs);
+    RP3D_PROFILE("OverlappingPairs::removePair()", mProfiler);
 
-    // Size for the data of a single component (in bytes)
-    const size_t totalSizeBytes = nbPairsToAllocate * mPairDataSize;
+    if (isConvexVsConvex) {
 
-    // Allocate memory
-    void* newBuffer = mPersistentAllocator.allocate(totalSizeBytes);
-    assert(newBuffer != nullptr);
+        const uint nbConvexPairs = mConvexPairs.size();
 
-    // New pointers to components data
-    uint64* newPairIds = static_cast<uint64*>(newBuffer);
-    int32* newPairBroadPhaseId1 = reinterpret_cast<int32*>(newPairIds + nbPairsToAllocate);
-    int32* newPairBroadPhaseId2 = reinterpret_cast<int32*>(newPairBroadPhaseId1 + nbPairsToAllocate);
-    Entity* newColliders1 = reinterpret_cast<Entity*>(newPairBroadPhaseId2 + nbPairsToAllocate);
-    Entity* newColliders2 = reinterpret_cast<Entity*>(newColliders1 + nbPairsToAllocate);
-    Map<uint64, LastFrameCollisionInfo*>* newLastFrameCollisionInfos = reinterpret_cast<Map<uint64, LastFrameCollisionInfo*>*>(newColliders2 + nbPairsToAllocate);
-    bool* newNeedToTestOverlap = reinterpret_cast<bool*>(newLastFrameCollisionInfos + nbPairsToAllocate);
-    NarrowPhaseAlgorithmType* newNarrowPhaseAlgorithmType = reinterpret_cast<NarrowPhaseAlgorithmType*>(newNeedToTestOverlap + nbPairsToAllocate);
-    bool* newIsShape1Convex = reinterpret_cast<bool*>(newNarrowPhaseAlgorithmType + nbPairsToAllocate);
-    bool* wereCollidingInPreviousFrame = reinterpret_cast<bool*>(newIsShape1Convex + nbPairsToAllocate);
-    bool* areCollidingInCurrentFrame = reinterpret_cast<bool*>(wereCollidingInPreviousFrame + nbPairsToAllocate);
+        assert(pairIndex < nbConvexPairs);
 
-    // If there was already pairs before
-    if (mNbPairs > 0) {
+        // Remove the involved overlapping pair from the two colliders
+        assert(mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).find(mConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).end());
+        assert(mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).find(mConvexPairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).end());
+        mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider1).remove(mConvexPairs[pairIndex].pairID);
+        mColliderComponents.getOverlappingPairs(mConvexPairs[pairIndex].collider2).remove(mConvexPairs[pairIndex].pairID);
 
-        // Copy component data from the previous buffer to the new one
-        memcpy(newPairIds, mPairIds, mNbPairs * sizeof(uint64));
-        memcpy(newPairBroadPhaseId1, mPairBroadPhaseId1, mNbPairs * sizeof(int32));
-        memcpy(newPairBroadPhaseId2, mPairBroadPhaseId2, mNbPairs * sizeof(int32));
-        memcpy(newColliders1, mColliders1, mNbPairs * sizeof(Entity));
-        memcpy(newColliders2, mColliders2, mNbPairs * sizeof(Entity));
-        memcpy(newLastFrameCollisionInfos, mLastFrameCollisionInfos, mNbPairs * sizeof(Map<uint64, LastFrameCollisionInfo*>));
-        memcpy(newNeedToTestOverlap, mNeedToTestOverlap, 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));
+        assert(mMapConvexPairIdToPairIndex[mConvexPairs[pairIndex].pairID] == pairIndex);
+        mMapConvexPairIdToPairIndex.remove(mConvexPairs[pairIndex].pairID);
 
-        // Deallocate previous memory
-        mPersistentAllocator.release(mBuffer, mNbAllocatedPairs * mPairDataSize);
+        // Change the mapping between the pairId and the index in the convex pairs array if we swap the last item with the one to remove
+        if (mConvexPairs.size() > 1 && pairIndex < (nbConvexPairs - 1)) {
+
+            mMapConvexPairIdToPairIndex[mConvexPairs[nbConvexPairs - 1].pairID] = pairIndex;
+        }
+
+        // We want to keep the arrays tightly packed. Therefore, when a pair is removed,
+        // we replace it with the last element of the array.
+        mConvexPairs.removeAtAndReplaceByLast(pairIndex);
     }
+    else {
 
-    mBuffer = newBuffer;
-    mPairIds = newPairIds;
-    mPairBroadPhaseId1 = newPairBroadPhaseId1;
-    mPairBroadPhaseId2 = newPairBroadPhaseId2;
-    mColliders1 = newColliders1;
-    mColliders2 = newColliders2;
-    mLastFrameCollisionInfos = newLastFrameCollisionInfos;
-    mNeedToTestOverlap = newNeedToTestOverlap;
-    mNarrowPhaseAlgorithmType = newNarrowPhaseAlgorithmType;
-    mIsShape1Convex = newIsShape1Convex;
-    mCollidingInPreviousFrame = wereCollidingInPreviousFrame;
-    mCollidingInCurrentFrame = areCollidingInCurrentFrame;
+        const uint nbConcavePairs = mConcavePairs.size();
 
-    mNbAllocatedPairs = nbPairsToAllocate;
+        assert(pairIndex < nbConcavePairs);
+
+        // Remove the involved overlapping pair to the two colliders
+        assert(mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).find(mConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).end());
+        assert(mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).find(mConcavePairs[pairIndex].pairID) != mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).end());
+        mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider1).remove(mConcavePairs[pairIndex].pairID);
+        mColliderComponents.getOverlappingPairs(mConcavePairs[pairIndex].collider2).remove(mConcavePairs[pairIndex].pairID);
+
+        assert(mMapConcavePairIdToPairIndex[mConcavePairs[pairIndex].pairID] == pairIndex);
+        mMapConcavePairIdToPairIndex.remove(mConcavePairs[pairIndex].pairID);
+
+        // Destroy all the LastFrameCollisionInfo objects
+        mConcavePairs[pairIndex].destroyLastFrameCollisionInfos();
+
+        // Change the mapping between the pairId and the index in the convex pairs array if we swap the last item with the one to remove
+        if (mConcavePairs.size() > 1 && pairIndex < (nbConcavePairs - 1)) {
+
+            mMapConcavePairIdToPairIndex[mConcavePairs[nbConcavePairs - 1].pairID] = pairIndex;
+        }
+
+        // We want to keep the arrays tightly packed. Therefore, when a pair is removed,
+        // we replace it with the last element of the array.
+        mConcavePairs.removeAtAndReplaceByLast(pairIndex);
+    }
 }
 
 // Add an overlapping pair
-uint64 OverlappingPairs::addPair(Collider* shape1, Collider* shape2) {
+uint64 OverlappingPairs::addPair(uint32 collider1Index, uint32 collider2Index, bool isConvexVsConvex) {
 
     RP3D_PROFILE("OverlappingPairs::addPair()", mProfiler);
 
-    const Entity collider1 = shape1->getEntity();
-    const Entity collider2 = shape2->getEntity();
-
-    const uint collider1Index = mColliderComponents.getEntityIndex(collider1);
-    const uint collider2Index = mColliderComponents.getEntityIndex(collider2);
+    assert(mColliderComponents.mBroadPhaseIds[collider1Index] >= 0 && mColliderComponents.mBroadPhaseIds[collider2Index] >= 0);
 
     const CollisionShape* collisionShape1 = mColliderComponents.mCollisionShapes[collider1Index];
     const CollisionShape* collisionShape2 = mColliderComponents.mCollisionShapes[collider2Index];
 
-    const bool isShape1Convex = collisionShape1->isConvex();
-    const bool isShape2Convex = collisionShape2->isConvex();
-    const bool isConvexVsConvex = isShape1Convex && isShape2Convex;
+    const Entity collider1Entity = mColliderComponents.mCollidersEntities[collider1Index];
+    const Entity collider2Entity = mColliderComponents.mCollidersEntities[collider2Index];
 
-    // Prepare to add new pair (allocate memory if necessary and compute insertion index)
-    uint64 index = prepareAddPair(isConvexVsConvex);
-
-    const uint32 broadPhase1Id = static_cast<uint32>(shape1->getBroadPhaseId());
-    const uint32 broadPhase2Id = static_cast<uint32>(shape2->getBroadPhaseId());
+    const uint32 broadPhase1Id = static_cast<uint32>(mColliderComponents.mBroadPhaseIds[collider1Index]);
+    const uint32 broadPhase2Id = static_cast<uint32>(mColliderComponents.mBroadPhaseIds[collider2Index]);
 
     // Compute a unique id for the overlapping pair
     const uint64 pairId = pairNumbers(std::max(broadPhase1Id, broadPhase2Id), std::min(broadPhase1Id, broadPhase2Id));
 
-    assert(!mMapPairIdToPairIndex.containsKey(pairId));
-
     // Select the narrow phase algorithm to use according to the two collision shapes
-    NarrowPhaseAlgorithmType algorithmType;
     if (isConvexVsConvex) {
 
-        algorithmType = mCollisionDispatch.selectNarrowPhaseAlgorithm(collisionShape1->getType(), collisionShape2->getType());
+        assert(!mMapConvexPairIdToPairIndex.containsKey(pairId));
+        NarrowPhaseAlgorithmType algorithmType = mCollisionDispatch.selectNarrowPhaseAlgorithm(collisionShape1->getType(), collisionShape2->getType());
+
+        // Map the entity with the new component lookup index
+        mMapConvexPairIdToPairIndex.add(Pair<uint64, uint64>(pairId, mConvexPairs.size()));
+
+        // Create and add a new convex pair
+        mConvexPairs.emplace(pairId, broadPhase1Id, broadPhase2Id, collider1Entity, collider2Entity, algorithmType);
     }
     else {
 
-        algorithmType = mCollisionDispatch.selectNarrowPhaseAlgorithm(isShape1Convex ? collisionShape1->getType() : collisionShape2->getType(),
+        const bool isShape1Convex = collisionShape1->isConvex();
+
+        assert(!mMapConcavePairIdToPairIndex.containsKey(pairId));
+        NarrowPhaseAlgorithmType algorithmType = mCollisionDispatch.selectNarrowPhaseAlgorithm(isShape1Convex ? collisionShape1->getType() : collisionShape2->getType(),
                                                                       CollisionShapeType::CONVEX_POLYHEDRON);
+        // Map the entity with the new component lookup index
+        mMapConcavePairIdToPairIndex.add(Pair<uint64, uint64>(pairId, mConcavePairs.size()));
+
+        // Create and add a new concave pair
+        mConcavePairs.emplace(pairId, broadPhase1Id, broadPhase2Id, collider1Entity, collider2Entity, algorithmType,
+                              isShape1Convex, mPoolAllocator, mHeapAllocator);
     }
 
-    // Insert the new component data
-    new (mPairIds + index) uint64(pairId);
-    new (mPairBroadPhaseId1 + index) int32(shape1->getBroadPhaseId());
-    new (mPairBroadPhaseId2 + index) int32(shape2->getBroadPhaseId());
-    new (mColliders1 + index) Entity(shape1->getEntity());
-    new (mColliders2 + index) Entity(shape2->getEntity());
-    new (mLastFrameCollisionInfos + index) Map<uint64, LastFrameCollisionInfo*>(mPersistentAllocator);
-    new (mNeedToTestOverlap + index) bool(false);
-    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<uint64, uint64>(pairId, index));
-
     // Add the involved overlapping pair to the two colliders
     assert(mColliderComponents.mOverlappingPairs[collider1Index].find(pairId) == mColliderComponents.mOverlappingPairs[collider1Index].end());
     assert(mColliderComponents.mOverlappingPairs[collider2Index].find(pairId) == mColliderComponents.mOverlappingPairs[collider2Index].end());
     mColliderComponents.mOverlappingPairs[collider1Index].add(pairId);
     mColliderComponents.mOverlappingPairs[collider2Index].add(pairId);
 
-    mNbPairs++;
-
-    assert(mConcavePairsStartIndex <= mNbPairs);
-    assert(mNbPairs == static_cast<uint64>(mMapPairIdToPairIndex.size()));
-
     return pairId;
 }
 
-// Move a pair from a source to a destination index in the pairs array
-// The destination location must contain a constructed object
-void OverlappingPairs::movePairToIndex(uint64 srcIndex, uint64 destIndex) {
-
-    const uint64 pairId = mPairIds[srcIndex];
-
-    // Copy the data of the source pair to the destination location
-    mPairIds[destIndex] = mPairIds[srcIndex];
-    mPairBroadPhaseId1[destIndex] = mPairBroadPhaseId1[srcIndex];
-    mPairBroadPhaseId2[destIndex] = mPairBroadPhaseId2[srcIndex];
-    new (mColliders1 + destIndex) Entity(mColliders1[srcIndex]);
-    new (mColliders2 + destIndex) Entity(mColliders2[srcIndex]);
-    new (mLastFrameCollisionInfos + destIndex) Map<uint64, LastFrameCollisionInfo*>(mLastFrameCollisionInfos[srcIndex]);
-    mNeedToTestOverlap[destIndex] = mNeedToTestOverlap[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);
-
-    assert(!mMapPairIdToPairIndex.containsKey(pairId));
-
-    // Update the pairId to pair index mapping
-    mMapPairIdToPairIndex.add(Pair<uint64, uint64>(pairId, destIndex));
-
-    assert(mMapPairIdToPairIndex[mPairIds[destIndex]] == destIndex);
-}
-
-// Swap two pairs in the array
-void OverlappingPairs::swapPairs(uint64 index1, uint64 index2) {
-
-    // Copy pair 1 data
-    uint64 pairId = mPairIds[index1];
-    int32 pairBroadPhaseId1 = mPairBroadPhaseId1[index1];
-    int32 pairBroadPhaseId2 = mPairBroadPhaseId2[index1];
-    Entity collider1 = mColliders1[index1];
-    Entity collider2 = mColliders2[index1];
-    Map<uint64, LastFrameCollisionInfo*> lastFrameCollisionInfo(mLastFrameCollisionInfos[index1]);
-    bool needTestOverlap = mNeedToTestOverlap[index1];
-    NarrowPhaseAlgorithmType narrowPhaseAlgorithmType = mNarrowPhaseAlgorithmType[index1];
-    bool isShape1Convex = mIsShape1Convex[index1];
-    bool wereCollidingInPreviousFrame = mCollidingInPreviousFrame[index1];
-    bool areCollidingInCurrentFrame = mCollidingInCurrentFrame[index1];
-
-    // Destroy pair 1
-    destroyPair(index1);
-
-    movePairToIndex(index2, index1);
-
-    // Reconstruct pair 1 at pair 2 location
-    mPairIds[index2] = pairId;
-    mPairBroadPhaseId1[index2] = pairBroadPhaseId1;
-    mPairBroadPhaseId2[index2] = pairBroadPhaseId2;
-    new (mColliders1 + index2) Entity(collider1);
-    new (mColliders2 + index2) Entity(collider2);
-    new (mLastFrameCollisionInfos + index2) Map<uint64, LastFrameCollisionInfo*>(lastFrameCollisionInfo);
-    mNeedToTestOverlap[index2] = needTestOverlap;
-    new (mNarrowPhaseAlgorithmType + index2) NarrowPhaseAlgorithmType(narrowPhaseAlgorithmType);
-    mIsShape1Convex[index2] = isShape1Convex;
-    mCollidingInPreviousFrame[index2] = wereCollidingInPreviousFrame;
-    mCollidingInCurrentFrame[index2] = areCollidingInCurrentFrame;
-
-    // Update the pairID to pair index mapping
-    mMapPairIdToPairIndex.add(Pair<uint64, uint64>(pairId, index2));
-
-    assert(mMapPairIdToPairIndex[mPairIds[index1]] == index1);
-    assert(mMapPairIdToPairIndex[mPairIds[index2]] == index2);
-    assert(mNbPairs == static_cast<uint64>(mMapPairIdToPairIndex.size()));
-}
-
-// Destroy a pair at a given index
-void OverlappingPairs::destroyPair(uint64 index) {
-
-    assert(index < mNbPairs);
-
-    assert(mMapPairIdToPairIndex[mPairIds[index]] == index);
-
-    mMapPairIdToPairIndex.remove(mPairIds[index]);
-
-    mColliders1[index].~Entity();
-    mColliders2[index].~Entity();
-    mLastFrameCollisionInfos[index].~Map<uint64, LastFrameCollisionInfo*>();
-    mNarrowPhaseAlgorithmType[index].~NarrowPhaseAlgorithmType();
-}
-
-// Add a new last frame collision info if it does not exist for the given shapes already
-LastFrameCollisionInfo* OverlappingPairs::addLastFrameInfoIfNecessary(uint64 pairIndex, uint32 shapeId1, uint32 shapeId2) {
-
-    RP3D_PROFILE("OverlappingPairs::addLastFrameInfoIfNecessary()", mProfiler);
-
-    assert(pairIndex < mNbPairs);
-
-    uint32 maxShapeId = shapeId1;
-    uint32 minShapeId = shapeId2;
-    if (shapeId1 < shapeId2) {
-       maxShapeId = shapeId2;
-       minShapeId = shapeId1;
-    }
-
-    // Try to get the corresponding last frame collision info
-    const uint64 shapesId = pairNumbers(maxShapeId, minShapeId);
-
-    // If there is no collision info for those two shapes already
-    auto it = mLastFrameCollisionInfos[pairIndex].find(shapesId);
-    if (it == mLastFrameCollisionInfos[pairIndex].end()) {
-
-        // Create a new collision info
-        LastFrameCollisionInfo* collisionInfo = new (mPersistentAllocator.allocate(sizeof(LastFrameCollisionInfo)))
-                                                LastFrameCollisionInfo();
-
-        // Add it into the map of collision infos
-        mLastFrameCollisionInfos[pairIndex].add(Pair<uint64, LastFrameCollisionInfo*>(shapesId, collisionInfo));
-
-        return collisionInfo;
-    }
-    else {
-
-       // The existing collision info is not obsolete
-       it->second->isObsolete = false;
-
-       return it->second;
-    }
-}
-
 // Delete all the obsolete last frame collision info
 void OverlappingPairs::clearObsoleteLastFrameCollisionInfos() {
 
     RP3D_PROFILE("OverlappingPairs::clearObsoleteLastFrameCollisionInfos()", mProfiler);
 
-    // For each overlapping pair
-    for (uint64 i=0; i < mNbPairs; i++) {
+    // For each concave overlapping pair
+    const uint64 nbConcavePairs = mConcavePairs.size();
+    for (uint64 i=0; i < nbConcavePairs; i++) {
 
-        // For each collision info
-        for (auto it = mLastFrameCollisionInfos[i].begin(); it != mLastFrameCollisionInfos[i].end(); ) {
-
-            // If the collision info is obsolete
-            if (it->second->isObsolete) {
-
-                // Delete it
-                it->second->~LastFrameCollisionInfo();
-                mPersistentAllocator.release(it->second, sizeof(LastFrameCollisionInfo));
-
-                it = mLastFrameCollisionInfos[i].remove(it);
-            }
-            else {  // If the collision info is not obsolete
-
-                // Do not delete it but mark it as obsolete
-                it->second->isObsolete = true;
-
-                ++it;
-            }
-        }
+        mConcavePairs[i].clearObsoleteLastFrameInfos();
     }
 }
 
 // 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++) {
+    RP3D_PROFILE("OverlappingPairs::updateCollidingInPreviousFrame()", mProfiler);
 
-        mCollidingInPreviousFrame[i] = mCollidingInCurrentFrame[i];
+    // For each convex overlapping pair
+    const uint64 nbConvexPairs = mConvexPairs.size();
+    for (uint64 i=0; i < nbConvexPairs; i++) {
+
+        mConvexPairs[i].collidingInPreviousFrame = mConvexPairs[i].collidingInCurrentFrame;
+    }
+
+    // For each concave overlapping pair
+    const uint64 nbConcavePairs = mConcavePairs.size();
+    for (uint64 i=0; i < nbConcavePairs; i++) {
+
+        mConcavePairs[i].collidingInPreviousFrame = mConcavePairs[i].collidingInCurrentFrame;
     }
 }
diff --git a/src/systems/BroadPhaseSystem.cpp b/src/systems/BroadPhaseSystem.cpp
index 53a2993e..8c1326a6 100644
--- a/src/systems/BroadPhaseSystem.cpp
+++ b/src/systems/BroadPhaseSystem.cpp
@@ -37,9 +37,9 @@ using namespace reactphysics3d;
 // Constructor
 BroadPhaseSystem::BroadPhaseSystem(CollisionDetectionSystem& collisionDetection, ColliderComponents& collidersComponents,
                                    TransformComponents& transformComponents, RigidBodyComponents& rigidBodyComponents)
-                    :mDynamicAABBTree(collisionDetection.getMemoryManager().getPoolAllocator(), DYNAMIC_TREE_FAT_AABB_INFLATE_PERCENTAGE),
+                    :mDynamicAABBTree(collisionDetection.getMemoryManager().getHeapAllocator(), DYNAMIC_TREE_FAT_AABB_INFLATE_PERCENTAGE),
                      mCollidersComponents(collidersComponents), mTransformsComponents(transformComponents),
-                     mRigidBodyComponents(rigidBodyComponents), mMovedShapes(collisionDetection.getMemoryManager().getPoolAllocator()),
+                     mRigidBodyComponents(rigidBodyComponents), mMovedShapes(collisionDetection.getMemoryManager().getHeapAllocator()),
                      mCollisionDetection(collisionDetection) {
 
 #ifdef IS_RP3D_PROFILING_ENABLED
@@ -211,7 +211,7 @@ void BroadPhaseSystem::computeOverlappingPairs(MemoryManager& memoryManager, Lis
     RP3D_PROFILE("BroadPhaseSystem::computeOverlappingPairs()", mProfiler);
 
     // Get the list of the colliders that have moved or have been created in the last frame
-    List<int> shapesToTest = mMovedShapes.toList(memoryManager.getPoolAllocator());
+    List<int> shapesToTest = mMovedShapes.toList(memoryManager.getHeapAllocator());
 
     // Ask the dynamic AABB tree to report all collision shapes that overlap with the shapes to test
     mDynamicAABBTree.reportAllShapesOverlappingWithShapes(shapesToTest, 0, shapesToTest.size(), overlappingNodes);
diff --git a/src/systems/CollisionDetectionSystem.cpp b/src/systems/CollisionDetectionSystem.cpp
index 4315cdb5..ab706182 100644
--- a/src/systems/CollisionDetectionSystem.cpp
+++ b/src/systems/CollisionDetectionSystem.cpp
@@ -55,8 +55,8 @@ CollisionDetectionSystem::CollisionDetectionSystem(PhysicsWorld* world, Collider
                    : mMemoryManager(memoryManager), mCollidersComponents(collidersComponents), mRigidBodyComponents(rigidBodyComponents),
                      mCollisionDispatch(mMemoryManager.getPoolAllocator()), mWorld(world),
                      mNoCollisionPairs(mMemoryManager.getPoolAllocator()),
-                     mOverlappingPairs(mMemoryManager.getPoolAllocator(), mMemoryManager.getSingleFrameAllocator(), mCollidersComponents,
-                                       collisionBodyComponents, rigidBodyComponents, mNoCollisionPairs, mCollisionDispatch),
+                     mOverlappingPairs(mMemoryManager, mCollidersComponents, collisionBodyComponents, rigidBodyComponents,
+                                       mNoCollisionPairs, mCollisionDispatch),
                      mBroadPhaseSystem(*this, mCollidersComponents, transformComponents, rigidBodyComponents),
                      mMapBroadPhaseIdToColliderEntity(memoryManager.getPoolAllocator()),
                      mNarrowPhaseInput(mMemoryManager.getSingleFrameAllocator(), mOverlappingPairs), mPotentialContactPoints(mMemoryManager.getSingleFrameAllocator()),
@@ -102,7 +102,7 @@ void CollisionDetectionSystem::computeBroadPhase() {
     // Ask the broad-phase to compute all the shapes overlapping with the shapes that
     // have moved or have been added in the last frame. This call can only add new
     // overlapping pairs in the collision detection.
-    List<Pair<int32, int32>> overlappingNodes(mMemoryManager.getPoolAllocator(), 32);
+    List<Pair<int32, int32>> overlappingNodes(mMemoryManager.getHeapAllocator(), 32);
     mBroadPhaseSystem.computeOverlappingPairs(mMemoryManager, overlappingNodes);
 
     // Create new overlapping pairs if necessary
@@ -117,25 +117,55 @@ void CollisionDetectionSystem::removeNonOverlappingPairs() {
 
     RP3D_PROFILE("CollisionDetectionSystem::removeNonOverlappingPairs()", mProfiler);
 
-    for (uint64 i=0; i < mOverlappingPairs.getNbPairs(); i++) {
+    // For each convex pairs
+    for (uint64 i=0; i < mOverlappingPairs.mConvexPairs.size(); i++) {
+
+        OverlappingPairs::ConvexOverlappingPair& overlappingPair = mOverlappingPairs.mConvexPairs[i];
 
         // Check if we need to test overlap. If so, test if the two shapes are still overlapping.
         // Otherwise, we destroy the overlapping pair
-        if (mOverlappingPairs.mNeedToTestOverlap[i]) {
+        if (overlappingPair.needToTestOverlap) {
 
-            if(mBroadPhaseSystem.testOverlappingShapes(mOverlappingPairs.mPairBroadPhaseId1[i], mOverlappingPairs.mPairBroadPhaseId2[i])) {
-                mOverlappingPairs.mNeedToTestOverlap[i] = false;
+            if(mBroadPhaseSystem.testOverlappingShapes(overlappingPair.broadPhaseId1, overlappingPair.broadPhaseId2)) {
+                overlappingPair.needToTestOverlap = false;
             }
             else {
 
                 // If the two colliders of the pair were colliding in the previous frame
-                if (mOverlappingPairs.mCollidingInPreviousFrame[i]) {
+                if (overlappingPair.collidingInPreviousFrame) {
 
                     // Create a new lost contact pair
-                    addLostContactPair(i);
+                    addLostContactPair(overlappingPair);
                 }
 
-                mOverlappingPairs.removePair(mOverlappingPairs.mPairIds[i]);
+                mOverlappingPairs.removePair(i, true);
+                i--;
+            }
+        }
+    }
+
+    // For each concave pairs
+    for (uint64 i=0; i < mOverlappingPairs.mConcavePairs.size(); i++) {
+
+        OverlappingPairs::ConcaveOverlappingPair& overlappingPair = mOverlappingPairs.mConcavePairs[i];
+
+        // Check if we need to test overlap. If so, test if the two shapes are still overlapping.
+        // Otherwise, we destroy the overlapping pair
+        if (overlappingPair.needToTestOverlap) {
+
+            if(mBroadPhaseSystem.testOverlappingShapes(overlappingPair.broadPhaseId1, overlappingPair.broadPhaseId2)) {
+                overlappingPair.needToTestOverlap = false;
+            }
+            else {
+
+                // If the two colliders of the pair were colliding in the previous frame
+                if (overlappingPair.collidingInPreviousFrame) {
+
+                    // Create a new lost contact pair
+                    addLostContactPair(overlappingPair);
+                }
+
+                mOverlappingPairs.removePair(i, false);
                 i--;
             }
         }
@@ -143,20 +173,20 @@ void CollisionDetectionSystem::removeNonOverlappingPairs() {
 }
 
 // Add a lost contact pair (pair of colliders that are not in contact anymore)
-void CollisionDetectionSystem::addLostContactPair(uint64 overlappingPairIndex) {
+void CollisionDetectionSystem::addLostContactPair(OverlappingPairs::OverlappingPair& overlappingPair) {
 
-    const Entity collider1Entity = mOverlappingPairs.mColliders1[overlappingPairIndex];
-    const Entity collider2Entity = mOverlappingPairs.mColliders2[overlappingPairIndex];
+    const uint32 collider1Index = mCollidersComponents.getEntityIndex(overlappingPair.collider1);
+    const uint32 collider2Index = mCollidersComponents.getEntityIndex(overlappingPair.collider2);
 
-    const Entity body1Entity = mCollidersComponents.getBody(collider1Entity);
-    const Entity body2Entity = mCollidersComponents.getBody(collider2Entity);
+    const Entity body1Entity = mCollidersComponents.mBodiesEntities[collider1Index];
+    const Entity body2Entity = mCollidersComponents.mBodiesEntities[collider2Index];
 
-    const bool isCollider1Trigger = mCollidersComponents.getIsTrigger(collider1Entity);
-    const bool isCollider2Trigger = mCollidersComponents.getIsTrigger(collider2Entity);
+    const bool isCollider1Trigger = mCollidersComponents.mIsTrigger[collider1Index];
+    const bool isCollider2Trigger = mCollidersComponents.mIsTrigger[collider2Index];
     const bool isTrigger = isCollider1Trigger || isCollider2Trigger;
 
     // Create a lost contact pair
-    ContactPair lostContactPair(mOverlappingPairs.mPairIds[overlappingPairIndex], body1Entity, body2Entity, collider1Entity, collider2Entity, mLostContactPairs.size(),
+    ContactPair lostContactPair(overlappingPair.pairID, body1Entity, body2Entity, overlappingPair.collider1, overlappingPair.collider2, mLostContactPairs.size(),
                                 true, isTrigger);
     mLostContactPairs.add(lostContactPair);
 }
@@ -219,8 +249,8 @@ void CollisionDetectionSystem::updateOverlappingPairs(const List<Pair<int32, int
                         const uint64 pairId = pairNumbers(std::max(nodePair.first, nodePair.second), std::min(nodePair.first, nodePair.second));
 
                         // Check if the overlapping pair already exists
-                        auto it = mOverlappingPairs.mMapPairIdToPairIndex.find(pairId);
-                        if (it == mOverlappingPairs.mMapPairIdToPairIndex.end()) {
+                        OverlappingPairs::OverlappingPair* overlappingPair = mOverlappingPairs.getOverlappingPair(pairId);
+                        if (overlappingPair == nullptr) {
 
                             const unsigned short shape1CollideWithMaskBits = mCollidersComponents.mCollideWithMaskBits[collider1Index];
                             const unsigned short shape2CollideWithMaskBits = mCollidersComponents.mCollideWithMaskBits[collider2Index];
@@ -236,17 +266,19 @@ void CollisionDetectionSystem::updateOverlappingPairs(const List<Pair<int32, int
                                 Collider* shape2 = mCollidersComponents.mColliders[collider2Index];
 
                                 // Check that at least one collision shape is convex
-                                if (shape1->getCollisionShape()->isConvex() || shape2->getCollisionShape()->isConvex()) {
+                                const bool isShape1Convex = shape1->getCollisionShape()->isConvex();
+                                const bool isShape2Convex = shape2->getCollisionShape()->isConvex();
+                                if (isShape1Convex || isShape2Convex) {
 
                                     // Add the new overlapping pair
-                                    mOverlappingPairs.addPair(shape1, shape2);
+                                    mOverlappingPairs.addPair(collider1Index, collider2Index, isShape1Convex && isShape2Convex);
                                 }
                             }
                         }
                         else {
 
                             // We do not need to test the pair for overlap because it has just been reported that they still overlap
-                            mOverlappingPairs.mNeedToTestOverlap[it->second] = false;
+                            overlappingPair->needToTestOverlap = false;
                         }
                     }
                 }
@@ -267,16 +299,18 @@ void CollisionDetectionSystem::computeMiddlePhase(NarrowPhaseInput& narrowPhaseI
     mOverlappingPairs.clearObsoleteLastFrameCollisionInfos();
 
     // For each possible convex vs convex pair of bodies
-    const uint64 nbConvexVsConvexPairs = mOverlappingPairs.getNbConvexVsConvexPairs();
+    const uint64 nbConvexVsConvexPairs = mOverlappingPairs.mConvexPairs.size();
     for (uint64 i=0; i < nbConvexVsConvexPairs; i++) {
 
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders1[i]) != -1);
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders2[i]) != -1);
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders1[i]) != mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders2[i]));
+        OverlappingPairs::ConvexOverlappingPair& overlappingPair = mOverlappingPairs.mConvexPairs[i];
+
+        assert(mCollidersComponents.getBroadPhaseId(overlappingPair.collider1) != -1);
+        assert(mCollidersComponents.getBroadPhaseId(overlappingPair.collider2) != -1);
+        assert(mCollidersComponents.getBroadPhaseId(overlappingPair.collider1) != mCollidersComponents.getBroadPhaseId(overlappingPair.collider2));
 
 
-        const Entity collider1Entity = mOverlappingPairs.mColliders1[i];
-        const Entity collider2Entity = mOverlappingPairs.mColliders2[i];
+        const Entity collider1Entity = overlappingPair.collider1;
+        const Entity collider2Entity = overlappingPair.collider2;
 
         const uint collider1Index = mCollidersComponents.getEntityIndex(collider1Entity);
         const uint collider2Index = mCollidersComponents.getEntityIndex(collider2Entity);
@@ -284,7 +318,7 @@ void CollisionDetectionSystem::computeMiddlePhase(NarrowPhaseInput& narrowPhaseI
         CollisionShape* collisionShape1 = mCollidersComponents.mCollisionShapes[collider1Index];
         CollisionShape* collisionShape2 = mCollidersComponents.mCollisionShapes[collider2Index];
 
-        NarrowPhaseAlgorithmType algorithmType = mOverlappingPairs.mNarrowPhaseAlgorithmType[i];
+        NarrowPhaseAlgorithmType algorithmType = overlappingPair.narrowPhaseAlgorithmType;
 
         const bool isCollider1Trigger = mCollidersComponents.mIsTrigger[collider1Index];
         const bool isCollider2Trigger = mCollidersComponents.mIsTrigger[collider2Index];
@@ -292,32 +326,34 @@ void CollisionDetectionSystem::computeMiddlePhase(NarrowPhaseInput& narrowPhaseI
 
         // No middle-phase is necessary, simply create a narrow phase info
         // for the narrow-phase collision detection
-        narrowPhaseInput.addNarrowPhaseTest(mOverlappingPairs.mPairIds[i], i, collider1Entity, collider2Entity, collisionShape1, collisionShape2,
-                                                  mCollidersComponents.mLocalToWorldTransforms[collider1Index],
-                                                  mCollidersComponents.mLocalToWorldTransforms[collider2Index],
-                                                  algorithmType, reportContacts, mMemoryManager.getSingleFrameAllocator());
+        narrowPhaseInput.addNarrowPhaseTest(overlappingPair.pairID, collider1Entity, collider2Entity, collisionShape1, collisionShape2,
+                                            mCollidersComponents.mLocalToWorldTransforms[collider1Index],
+                                            mCollidersComponents.mLocalToWorldTransforms[collider2Index],
+                                            algorithmType, reportContacts, &overlappingPair.lastFrameCollisionInfo,
+                                            mMemoryManager.getSingleFrameAllocator());
 
-        mOverlappingPairs.mCollidingInCurrentFrame[i] = false;
+        overlappingPair.collidingInCurrentFrame = false;
     }
 
     // For each possible convex vs concave pair of bodies
-    const uint64 convexVsConcaveStartIndex = mOverlappingPairs.getConvexVsConcavePairsStartIndex();
-    const uint64 nbConvexVsConcavePairs = mOverlappingPairs.getNbConvexVsConcavePairs();
-    for (uint64 i=convexVsConcaveStartIndex; i < convexVsConcaveStartIndex + nbConvexVsConcavePairs; i++) {
+    const uint64 nbConcavePairs = mOverlappingPairs.mConcavePairs.size();
+    for (uint64 i=0; i < nbConcavePairs; i++) {
 
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders1[i]) != -1);
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders2[i]) != -1);
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders1[i]) != mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders2[i]));
+        OverlappingPairs::ConcaveOverlappingPair& overlappingPair = mOverlappingPairs.mConcavePairs[i];
 
-        computeConvexVsConcaveMiddlePhase(i, mMemoryManager.getSingleFrameAllocator(), narrowPhaseInput);
+        assert(mCollidersComponents.getBroadPhaseId(overlappingPair.collider1) != -1);
+        assert(mCollidersComponents.getBroadPhaseId(overlappingPair.collider2) != -1);
+        assert(mCollidersComponents.getBroadPhaseId(overlappingPair.collider1) != mCollidersComponents.getBroadPhaseId(overlappingPair.collider2));
 
-        mOverlappingPairs.mCollidingInCurrentFrame[i] = false;
+        computeConvexVsConcaveMiddlePhase(overlappingPair, mMemoryManager.getSingleFrameAllocator(), narrowPhaseInput);
+
+        overlappingPair.collidingInCurrentFrame = false;
     }
 }
 
 // Compute the middle-phase collision detection
-void CollisionDetectionSystem::computeMiddlePhaseCollisionSnapshot(List<uint64>& convexPairs, List<uint64>& concavePairs, NarrowPhaseInput& narrowPhaseInput,
-                                                                   bool reportContacts) {
+void CollisionDetectionSystem::computeMiddlePhaseCollisionSnapshot(List<uint64>& convexPairs, List<uint64>& concavePairs,
+                                                                   NarrowPhaseInput& narrowPhaseInput, bool reportContacts) {
 
     RP3D_PROFILE("CollisionDetectionSystem::computeMiddlePhase()", mProfiler);
 
@@ -333,11 +369,11 @@ void CollisionDetectionSystem::computeMiddlePhaseCollisionSnapshot(List<uint64>&
 
         const uint64 pairId = convexPairs[p];
 
-        const uint64 pairIndex = mOverlappingPairs.mMapPairIdToPairIndex[pairId];
-        assert(pairIndex < mOverlappingPairs.getNbPairs());
+        const uint64 pairIndex = mOverlappingPairs.mMapConvexPairIdToPairIndex[pairId];
+        assert(pairIndex < mOverlappingPairs.mConvexPairs.size());
 
-        const Entity collider1Entity = mOverlappingPairs.mColliders1[pairIndex];
-        const Entity collider2Entity = mOverlappingPairs.mColliders2[pairIndex];
+        const Entity collider1Entity = mOverlappingPairs.mConvexPairs[pairIndex].collider1;
+        const Entity collider2Entity = mOverlappingPairs.mConvexPairs[pairIndex].collider2;
 
         const uint collider1Index = mCollidersComponents.getEntityIndex(collider1Entity);
         const uint collider2Index = mCollidersComponents.getEntityIndex(collider2Entity);
@@ -349,14 +385,14 @@ void CollisionDetectionSystem::computeMiddlePhaseCollisionSnapshot(List<uint64>&
         CollisionShape* collisionShape1 = mCollidersComponents.mCollisionShapes[collider1Index];
         CollisionShape* collisionShape2 = mCollidersComponents.mCollisionShapes[collider2Index];
 
-        NarrowPhaseAlgorithmType algorithmType = mOverlappingPairs.mNarrowPhaseAlgorithmType[pairIndex];
+        NarrowPhaseAlgorithmType algorithmType = mOverlappingPairs.mConvexPairs[pairIndex].narrowPhaseAlgorithmType;
 
         // No middle-phase is necessary, simply create a narrow phase info
         // for the narrow-phase collision detection
-        narrowPhaseInput.addNarrowPhaseTest(pairId, pairIndex, collider1Entity, collider2Entity, collisionShape1, collisionShape2,
+        narrowPhaseInput.addNarrowPhaseTest(pairId, collider1Entity, collider2Entity, collisionShape1, collisionShape2,
                                                   mCollidersComponents.mLocalToWorldTransforms[collider1Index],
                                                   mCollidersComponents.mLocalToWorldTransforms[collider2Index],
-                                                  algorithmType, reportContacts, mMemoryManager.getSingleFrameAllocator());
+                                                  algorithmType, reportContacts, &mOverlappingPairs.mConvexPairs[pairIndex].lastFrameCollisionInfo, mMemoryManager.getSingleFrameAllocator());
 
     }
 
@@ -365,23 +401,23 @@ void CollisionDetectionSystem::computeMiddlePhaseCollisionSnapshot(List<uint64>&
     for (uint p=0; p < nbConcavePairs; p++) {
 
         const uint64 pairId = concavePairs[p];
-        const uint64 pairIndex = mOverlappingPairs.mMapPairIdToPairIndex[pairId];
+        const uint64 pairIndex = mOverlappingPairs.mMapConcavePairIdToPairIndex[pairId];
 
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders1[pairIndex]) != -1);
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders2[pairIndex]) != -1);
-        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders1[pairIndex]) != mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mColliders2[pairIndex]));
+        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mConcavePairs[pairIndex].collider1) != -1);
+        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mConcavePairs[pairIndex].collider2) != -1);
+        assert(mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mConcavePairs[pairIndex].collider1) != mCollidersComponents.getBroadPhaseId(mOverlappingPairs.mConcavePairs[pairIndex].collider2));
 
-        computeConvexVsConcaveMiddlePhase(pairIndex, mMemoryManager.getSingleFrameAllocator(), narrowPhaseInput);
+        computeConvexVsConcaveMiddlePhase(mOverlappingPairs.mConcavePairs[pairIndex], mMemoryManager.getSingleFrameAllocator(), narrowPhaseInput);
     }
 }
 
 // Compute the concave vs convex middle-phase algorithm for a given pair of bodies
-void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(uint64 pairIndex, MemoryAllocator& allocator, NarrowPhaseInput& narrowPhaseInput) {
+void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(OverlappingPairs::ConcaveOverlappingPair& overlappingPair, MemoryAllocator& allocator, NarrowPhaseInput& narrowPhaseInput) {
 
     RP3D_PROFILE("CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase()", mProfiler);
 
-    const Entity collider1 = mOverlappingPairs.mColliders1[pairIndex];
-    const Entity collider2 = mOverlappingPairs.mColliders2[pairIndex];
+    const Entity collider1 = overlappingPair.collider1;
+    const Entity collider2 = overlappingPair.collider2;
 
     const uint collider1Index = mCollidersComponents.getEntityIndex(collider1);
     const uint collider2Index = mCollidersComponents.getEntityIndex(collider2);
@@ -394,8 +430,7 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(uint64 pairInde
     // Collision shape 1 is convex, collision shape 2 is concave
     ConvexShape* convexShape;
     ConcaveShape* concaveShape;
-    const bool isShape1Convex = mOverlappingPairs.mIsShape1Convex[pairIndex];
-    if (isShape1Convex) {
+    if (overlappingPair.isShape1Convex) {
         convexShape = static_cast<ConvexShape*>(mCollidersComponents.mCollisionShapes[collider1Index]);
         concaveShape = static_cast<ConcaveShape*>(mCollidersComponents.mCollisionShapes[collider2Index]);
         convexToConcaveTransform = shape2LocalToWorldTransform.getInverse() * shape1LocalToWorldTransform;
@@ -408,16 +443,16 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(uint64 pairInde
 
     assert(convexShape->isConvex());
     assert(!concaveShape->isConvex());
-    assert(mOverlappingPairs.mNarrowPhaseAlgorithmType[pairIndex] != NarrowPhaseAlgorithmType::None);
+    assert(overlappingPair.narrowPhaseAlgorithmType != NarrowPhaseAlgorithmType::None);
 
     // Compute the convex shape AABB in the local-space of the convex shape
     AABB aabb;
     convexShape->computeAABB(aabb, convexToConcaveTransform);
 
     // Compute the concave shape triangles that are overlapping with the convex mesh AABB
-    List<Vector3> triangleVertices(allocator);
-    List<Vector3> triangleVerticesNormals(allocator);
-    List<uint> shapeIds(allocator);
+    List<Vector3> triangleVertices(allocator, 64);
+    List<Vector3> triangleVerticesNormals(allocator, 64);
+    List<uint> shapeIds(allocator, 64);
     concaveShape->computeOverlappingTriangles(aabb, triangleVertices, triangleVerticesNormals, shapeIds, allocator);
 
     assert(triangleVertices.size() == triangleVerticesNormals.size());
@@ -429,10 +464,20 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(uint64 pairInde
     const bool isCollider2Trigger = mCollidersComponents.mIsTrigger[collider2Index];
     const bool reportContacts = !isCollider1Trigger && !isCollider2Trigger;
 
+    CollisionShape* shape1;
+    CollisionShape* shape2;
+
+    if (overlappingPair.isShape1Convex) {
+        shape1 = convexShape;
+    }
+    else {
+        shape2 = convexShape;
+    }
+
     // For each overlapping triangle
     const uint nbShapeIds = shapeIds.size();
-    for (uint i=0; i < nbShapeIds; i++)
-    {
+    for (uint i=0; i < nbShapeIds; i++) {
+
         // Create a triangle collision shape (the allocated memory for the TriangleShape will be released in the
         // destructor of the corresponding NarrowPhaseInfo.
         TriangleShape* triangleShape = new (allocator.allocate(sizeof(TriangleShape)))
@@ -446,11 +491,20 @@ void CollisionDetectionSystem::computeConvexVsConcaveMiddlePhase(uint64 pairInde
 
     #endif
 
+        if (overlappingPair.isShape1Convex) {
+            shape2 = triangleShape;
+        }
+        else {
+            shape1 = triangleShape;
+        }
+
+        // Add a collision info for the two collision shapes into the overlapping pair (if not present yet)
+        LastFrameCollisionInfo* lastFrameInfo = overlappingPair.addLastFrameInfoIfNecessary(shape1->getId(), shape2->getId());
+
         // Create a narrow phase info for the narrow-phase collision detection
-        narrowPhaseInput.addNarrowPhaseTest(mOverlappingPairs.mPairIds[pairIndex], pairIndex, collider1, collider2, isShape1Convex ? convexShape : triangleShape,
-                                                isShape1Convex ? triangleShape : convexShape,
-                                                shape1LocalToWorldTransform, shape2LocalToWorldTransform,
-                                                mOverlappingPairs.mNarrowPhaseAlgorithmType[pairIndex], reportContacts, allocator);
+        narrowPhaseInput.addNarrowPhaseTest(overlappingPair.pairID, collider1, collider2, shape1, shape2,
+                                            shape1LocalToWorldTransform, shape2LocalToWorldTransform,
+                                            overlappingPair.narrowPhaseAlgorithmType, reportContacts, lastFrameInfo, allocator);
     }
 }
 
@@ -624,7 +678,8 @@ void CollisionDetectionSystem::notifyOverlappingPairsToTestOverlap(Collider* col
     // Get the overlapping pairs involved with this collider
     List<uint64>& overlappingPairs = mCollidersComponents.getOverlappingPairs(collider->getEntity());
 
-    for (uint i=0; i < overlappingPairs.size(); i++) {
+    const uint nbPairs = overlappingPairs.size();
+    for (uint i=0; i < nbPairs; i++) {
 
         // Notify that the overlapping pair needs to be testbed for overlap
         mOverlappingPairs.setNeedToTestOverlap(overlappingPairs[i], true);
@@ -802,17 +857,34 @@ void CollisionDetectionSystem::createContacts() {
 // 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++) {
+    // For each convex pair
+    const uint nbConvexPairs = mOverlappingPairs.mConvexPairs.size();
+    for (uint i=0; i < nbConvexPairs; 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 (mOverlappingPairs.mConvexPairs[i].collidingInPreviousFrame && !mOverlappingPairs.mConvexPairs[i].collidingInCurrentFrame) {
 
             // If both bodies still exist
-            if (mCollidersComponents.hasComponent(mOverlappingPairs.mColliders1[i]) && mCollidersComponents.hasComponent(mOverlappingPairs.mColliders2[i])) {
+            if (mCollidersComponents.hasComponent(mOverlappingPairs.mConvexPairs[i].collider1) && mCollidersComponents.hasComponent(mOverlappingPairs.mConvexPairs[i].collider2)) {
 
                 // Create a lost contact pair
-                addLostContactPair(i);
+                addLostContactPair(mOverlappingPairs.mConvexPairs[i]);
+            }
+        }
+    }
+
+    // For each convex pair
+    const uint nbConcavePairs = mOverlappingPairs.mConcavePairs.size();
+    for (uint i=0; i < nbConcavePairs; i++) {
+
+        // If the two colliders of the pair were colliding in the previous frame but not in the current one
+        if (mOverlappingPairs.mConcavePairs[i].collidingInPreviousFrame && !mOverlappingPairs.mConcavePairs[i].collidingInCurrentFrame) {
+
+            // If both bodies still exist
+            if (mCollidersComponents.hasComponent(mOverlappingPairs.mConcavePairs[i].collider1) && mCollidersComponents.hasComponent(mOverlappingPairs.mConcavePairs[i].collider2)) {
+
+                // Create a lost contact pair
+                addLostContactPair(mOverlappingPairs.mConcavePairs[i]);
             }
         }
     }
@@ -1025,13 +1097,14 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na
     // For each narrow phase info object
     for(uint i=0; i < nbObjects; i++) {
 
-        const uint64 pairId = narrowPhaseInfoBatch.narrowPhaseInfos[i].overlappingPairId;
-        const uint64 pairIndex = mOverlappingPairs.mMapPairIdToPairIndex[pairId];
-
         // If the two colliders are colliding
         if (narrowPhaseInfoBatch.narrowPhaseInfos[i].isColliding) {
 
-            mOverlappingPairs.mCollidingInCurrentFrame[pairIndex] = true;
+            const uint64 pairId = narrowPhaseInfoBatch.narrowPhaseInfos[i].overlappingPairId;
+            OverlappingPairs::OverlappingPair* overlappingPair = mOverlappingPairs.getOverlappingPair(pairId);
+            assert(overlappingPair != nullptr);
+
+            overlappingPair->collidingInCurrentFrame = true;
 
             const Entity collider1Entity = narrowPhaseInfoBatch.narrowPhaseInfos[i].colliderEntity1;
             const Entity collider2Entity = narrowPhaseInfoBatch.narrowPhaseInfos[i].colliderEntity2;
@@ -1043,8 +1116,7 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na
             const Entity body2Entity = mCollidersComponents.mBodiesEntities[collider2Index];
 
             // If we have a convex vs convex collision (if we consider the base collision shapes of the colliders)
-            if (mCollidersComponents.mCollisionShapes[collider1Index]->isConvex() &&
-                mCollidersComponents.mCollisionShapes[collider2Index]->isConvex()) {
+            if (mCollidersComponents.mCollisionShapes[collider1Index]->isConvex() && mCollidersComponents.mCollisionShapes[collider2Index]->isConvex()) {
 
                 // Create a new ContactPair
 
@@ -1055,7 +1127,7 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na
                 const uint newContactPairIndex = contactPairs->size();
 
                 contactPairs->emplace(pairId, body1Entity, body2Entity, collider1Entity, collider2Entity,
-                                      newContactPairIndex, mOverlappingPairs.getCollidingInPreviousFrame(pairId), isTrigger);
+                                      newContactPairIndex, overlappingPair->collidingInPreviousFrame, isTrigger);
 
                 ContactPair* pairContact = &((*contactPairs)[newContactPairIndex]);
 
@@ -1102,7 +1174,7 @@ void CollisionDetectionSystem::processPotentialContacts(NarrowPhaseInfoBatch& na
 
                     const uint newContactPairIndex = contactPairs->size();
                     contactPairs->emplace(pairId, body1Entity, body2Entity, collider1Entity, collider2Entity,
-                                                       newContactPairIndex, mOverlappingPairs.getCollidingInPreviousFrame(pairId), isTrigger);
+                                                       newContactPairIndex, overlappingPair->collidingInPreviousFrame , isTrigger);
                     pairContact = &((*contactPairs)[newContactPairIndex]);
                     mapPairIdToContactPairIndex.add(Pair<uint64, uint>(pairId, newContactPairIndex));
 
@@ -1236,9 +1308,7 @@ void CollisionDetectionSystem::reducePotentialContactManifolds(List<ContactPair>
             // If there are two many contact points in the manifold
             if (manifold.nbPotentialContactPoints > MAX_CONTACT_POINTS_IN_MANIFOLD) {
 
-                Entity collider1 = mOverlappingPairs.mColliders1[mOverlappingPairs.mMapPairIdToPairIndex[manifold.pairId]];
-
-                Transform shape1LocalToWorldTransoform = mCollidersComponents.getLocalToWorldTransform(collider1);
+                Transform shape1LocalToWorldTransoform = mCollidersComponents.getLocalToWorldTransform(pairContact.collider1Entity);
 
                 // Reduce the number of contact points in the manifold
                 reduceContactPoints(manifold, shape1LocalToWorldTransoform, potentialContactPoints);
@@ -1655,18 +1725,25 @@ void CollisionDetectionSystem::testCollision(CollisionCallback& callback) {
 // Filter the overlapping pairs to keep only the pairs where a given body is involved
 void CollisionDetectionSystem::filterOverlappingPairs(Entity bodyEntity, List<uint64>& convexPairs, List<uint64>& concavePairs) const {
 
-    // For each possible collision pair of bodies
-    for (uint i=0; i < mOverlappingPairs.getNbPairs(); i++) {
+    // For each convex pairs
+    const uint nbConvexPairs = mOverlappingPairs.mConvexPairs.size();
+    for (uint i=0; i < nbConvexPairs; i++) {
 
-        if (mCollidersComponents.getBody(mOverlappingPairs.mColliders1[i]) == bodyEntity ||
-            mCollidersComponents.getBody(mOverlappingPairs.mColliders2[i]) == bodyEntity) {
+        if (mCollidersComponents.getBody(mOverlappingPairs.mConvexPairs[i].collider1) == bodyEntity ||
+            mCollidersComponents.getBody(mOverlappingPairs.mConvexPairs[i].collider2) == bodyEntity) {
 
-            if (i < mOverlappingPairs.getNbConvexVsConvexPairs()) {
-                convexPairs.add(mOverlappingPairs.mPairIds[i]);
-            }
-            else {
-                concavePairs.add(mOverlappingPairs.mPairIds[i]);
-            }
+            convexPairs.add(mOverlappingPairs.mConvexPairs[i].pairID);
+        }
+    }
+
+    // For each concave pairs
+    const uint nbConcavePairs = mOverlappingPairs.mConcavePairs.size();
+    for (uint i=0; i < nbConcavePairs; i++) {
+
+        if (mCollidersComponents.getBody(mOverlappingPairs.mConcavePairs[i].collider1) == bodyEntity ||
+            mCollidersComponents.getBody(mOverlappingPairs.mConcavePairs[i].collider2) == bodyEntity) {
+
+            concavePairs.add(mOverlappingPairs.mConcavePairs[i].pairID);
         }
     }
 }
@@ -1674,21 +1751,31 @@ void CollisionDetectionSystem::filterOverlappingPairs(Entity bodyEntity, List<ui
 // Filter the overlapping pairs to keep only the pairs where two given bodies are involved
 void CollisionDetectionSystem::filterOverlappingPairs(Entity body1Entity, Entity body2Entity, List<uint64>& convexPairs, List<uint64>& concavePairs) const {
 
-    // For each possible collision pair of bodies
-    for (uint i=0; i < mOverlappingPairs.getNbPairs(); i++) {
+    // For each convex pair
+    const uint nbConvexPairs = mOverlappingPairs.mConvexPairs.size();
+    for (uint i=0; i < nbConvexPairs; i++) {
 
-        const Entity collider1Body = mCollidersComponents.getBody(mOverlappingPairs.mColliders1[i]);
-        const Entity collider2Body = mCollidersComponents.getBody(mOverlappingPairs.mColliders2[i]);
+        const Entity collider1Body = mCollidersComponents.getBody(mOverlappingPairs.mConvexPairs[i].collider1);
+        const Entity collider2Body = mCollidersComponents.getBody(mOverlappingPairs.mConvexPairs[i].collider2);
 
         if ((collider1Body == body1Entity && collider2Body == body2Entity) ||
             (collider1Body == body2Entity && collider2Body == body1Entity)) {
 
-            if (i < mOverlappingPairs.getNbConvexVsConvexPairs()) {
-                convexPairs.add(mOverlappingPairs.mPairIds[i]);
-            }
-            else {
-                concavePairs.add(mOverlappingPairs.mPairIds[i]);
-            }
+            convexPairs.add(mOverlappingPairs.mConvexPairs[i].pairID);
+        }
+    }
+
+    // For each concave pair
+    const uint nbConcavePairs = mOverlappingPairs.mConcavePairs.size();
+    for (uint i=0; i < nbConcavePairs; i++) {
+
+        const Entity collider1Body = mCollidersComponents.getBody(mOverlappingPairs.mConcavePairs[i].collider1);
+        const Entity collider2Body = mCollidersComponents.getBody(mOverlappingPairs.mConcavePairs[i].collider2);
+
+        if ((collider1Body == body1Entity && collider2Body == body2Entity) ||
+            (collider1Body == body2Entity && collider2Body == body1Entity)) {
+
+            concavePairs.add(mOverlappingPairs.mConcavePairs[i].pairID);
         }
     }
 }