/******************************************************************************** * ReactPhysics3D physics library, http://code.google.com/p/reactphysics3d/ * * Copyright (c) 2010-2012 Daniel Chappuis * ********************************************************************************* * * * This software is provided 'as-is', without any express or implied warranty. * * In no event will the authors be held liable for any damages arising from the * * use of this software. * * * * Permission is granted to anyone to use this software for any purpose, * * including commercial applications, and to alter it and redistribute it * * freely, subject to the following restrictions: * * * * 1. The origin of this software must not be misrepresented; you must not claim * * that you wrote the original software. If you use this software in a * * product, an acknowledgment in the product documentation would be * * appreciated but is not required. * * * * 2. Altered source versions must be plainly marked as such, and must not be * * misrepresented as being the original software. * * * * 3. This notice may not be removed or altered from any source distribution. * * * ********************************************************************************/ // Libraries #include "PairManager.h" #include "../CollisionDetection.h" #include using namespace reactphysics3d; // Initialization of static variables bodyindex PairManager::INVALID_INDEX = std::numeric_limits::max(); // Constructor of PairManager PairManager::PairManager(CollisionDetection& collisionDetection) : collisionDetection(collisionDetection) { hashTable = 0; overlappingPairs = 0; offsetNextPair = 0; nbOverlappingPairs = 0; hashMask = 0; nbElementsHashTable = 0; } // Destructor of PairManager PairManager::~PairManager() { // Release the allocated memory free(offsetNextPair); free(overlappingPairs); free(hashTable); } // Add a pair of bodies in the pair manager and returns a pointer to // that pair. If the pair to add does not already exist in the set of // overlapping pairs, it will be created and if it already exists, we only // return a pointer to that pair. BroadPhasePair* PairManager::addPair(Body* body1, Body* body2) { // Sort the bodies to have the body with smallest ID first sortBodiesUsingID(body1, body2); // Get the bodies IDs bodyindex id1 = body1->getID(); bodyindex id2 = body2->getID(); // Compute the hash value of the two bodies uint hashValue = computeHashBodies(id1, id2) & hashMask; // Try to find the pair in the current overlapping pairs. BroadPhasePair* pair = findPairWithHashValue(id1, id2, hashValue); // If the pair is already in the set of overlapping pairs if (pair) { // We only return a pointer to that pair return pair; } // If we need to allocate more pairs in the set of overlapping pairs if (nbOverlappingPairs >= nbElementsHashTable) { // Increase the size of the hash table (always a power of two) nbElementsHashTable = computeNextPowerOfTwo(nbOverlappingPairs + 1); // Compute the new hash mask with the new hash size hashMask = nbElementsHashTable - 1; // Reallocate more pairs reallocatePairs(); // Compute the new hash value (using the new hash size and hash mask) hashValue = computeHashBodies(id1, id2) & hashMask; } // Create the new overlapping pair BroadPhasePair* newPair = &overlappingPairs[nbOverlappingPairs]; newPair->body1 = body1; newPair->body2 = body2; // Put the new pair as the initial pair with this hash value offsetNextPair[nbOverlappingPairs] = hashTable[hashValue]; hashTable[hashValue] = nbOverlappingPairs++; // Notify the collision detection about this new overlapping pair collisionDetection.broadPhaseNotifyAddedOverlappingPair(newPair); // Return a pointer to the new created pair return newPair; } // Remove a pair of bodies from the pair manager. This method returns // true if the pair has been found and removed. bool PairManager::removePair(bodyindex id1, bodyindex id2) { // Sort the bodies IDs sortIDs(id1, id2); // Compute the hash value of the pair to remove const uint hashValue = computeHashBodies(id1, id2) & hashMask; // Find the pair to remove const BroadPhasePair* pair = findPairWithHashValue(id1, id2, hashValue); // If we have not found the pair if (!pair) { return false; } assert(pair->body1->getID() == id1); assert(pair->body2->getID() == id2); // Notify the collision detection about this removed overlapping pair collisionDetection.broadPhaseNotifyRemovedOverlappingPair(pair); // Remove the pair from the set of overlapping pairs removePairWithHashValue(id1, id2, hashValue, computePairOffset(pair)); // Try to shrink the memory used by the pair manager shrinkMemory(); return true; } // Internal method to remove a pair from the set of overlapping pair void PairManager::removePairWithHashValue(bodyindex id1, bodyindex id2, luint hashValue, bodyindex indexPair) { // Get the initial offset of the pairs with // the corresponding hash value bodyindex offset = hashTable[hashValue]; assert(offset != INVALID_INDEX); // Look for the pair in the set of overlapping pairs bodyindex previousPair = INVALID_INDEX; while(offset != indexPair) { previousPair = offset; offset = offsetNextPair[offset]; } // If the pair was the first one with this hash // value in the hash table if (previousPair == INVALID_INDEX) { // Replace the pair to remove in the // hash table by the next one hashTable[hashValue] = offsetNextPair[indexPair]; } else { // If the pair was not the first one // Replace the pair to remove in the // hash table by the next one assert(offsetNextPair[previousPair] == indexPair); offsetNextPair[previousPair] = offsetNextPair[indexPair]; } const bodyindex indexLastPair = nbOverlappingPairs - 1; // If the pair to remove is the last one in the list if (indexPair == indexLastPair) { // We simply decrease the number of overlapping pairs nbOverlappingPairs--; } else { // If the pair to remove is in the middle of the list // Now, we want to move the last pair into the location that is // now free because of the pair we want to remove // Get the last pair const BroadPhasePair* lastPair = &overlappingPairs[indexLastPair]; const uint lastPairHashValue = computeHashBodies(lastPair->body1->getID(), lastPair->body2->getID()) & hashMask; // Compute the initial offset of the last pair bodyindex offset = hashTable[lastPairHashValue]; assert(offset != INVALID_INDEX); // Go through the pairs with the same hash value // and find the offset of the last pair bodyindex previous = INVALID_INDEX; while(offset != indexLastPair) { previous = offset; offset = offsetNextPair[offset]; } // If the last pair is not the first one with this hash value if (previous != INVALID_INDEX) { // Remove the offset of the last pair in the "nextOffset" array assert(offsetNextPair[previous] == indexLastPair); offsetNextPair[previous] = offsetNextPair[indexLastPair]; } else { // If the last pair is the first offset with this hash value // Remove the offset of the last pair in the "nextOffset" array hashTable[lastPairHashValue] = offsetNextPair[indexLastPair]; } // Replace the pair to remove by the last pair in // the overlapping pairs array overlappingPairs[indexPair] = overlappingPairs[indexLastPair]; offsetNextPair[indexPair] = hashTable[lastPairHashValue]; hashTable[lastPairHashValue] = indexPair; nbOverlappingPairs--; } } // Look for a pair in the set of overlapping pairs BroadPhasePair* PairManager::lookForAPair(bodyindex id1, bodyindex id2, luint hashValue) const { // Look for the pair in the set of overlapping pairs bodyindex offset = hashTable[hashValue]; while (offset != INVALID_INDEX && isDifferentPair(overlappingPairs[offset], id1, id2)) { offset = offsetNextPair[offset]; } // If the pair has not been found in the overlapping pairs if (offset == INVALID_INDEX) { // Return null return 0; } assert(offset < nbOverlappingPairs); // The pair has been found in the set of overlapping pairs, then // we return a pointer to it return &overlappingPairs[offset]; } // Reallocate more pairs void PairManager::reallocatePairs() { // Reallocate the hash table and initialize it free(hashTable); hashTable = (bodyindex*) malloc(nbElementsHashTable * sizeof(bodyindex)); assert(hashTable); for (bodyindex i=0; igetID(), overlappingPairs[i].body2->getID()) & hashMask; newOffsetNextPair[i] = hashTable[newHashValue]; hashTable[newHashValue] = i; } // Delete the old pairs free(offsetNextPair); free(overlappingPairs); // Replace by the new data overlappingPairs = newOverlappingPairs; offsetNextPair = newOffsetNextPair; }