/** @file * Original ReactPhysics3D C++ library by Daniel Chappuis This code is re-licensed with permission from ReactPhysics3D author. * @author Daniel CHAPPUIS * @author Edouard DUPIN * @copyright 2010-2016, Daniel Chappuis * @copyright 2017, Edouard DUPIN * @license MPL v2.0 (see license file) */ #include #include #include #include #include #include // We want to use the ReactPhysics3D namespace using namespace ephysics; using namespace std; // Constructor CollisionDetection::CollisionDetection(CollisionWorld* world): this.world(world), this.broadPhaseAlgorithm(*this), this.isCollisionShapesAdded(false) { // Set the default collision dispatch configuration setCollisionDispatch(this.defaultCollisionDispatch); // Fill-in the collision detection matrix with algorithms fillInCollisionMatrix(); } CollisionDetection::~CollisionDetection() { } void CollisionDetection::computeCollisionDetection() { PROFILE("CollisionDetection::computeCollisionDetection()"); // Compute the broad-phase collision detection computeBroadPhase(); // Compute the narrow-phase collision detection computeNarrowPhase(); } void CollisionDetection::testCollisionBetweenShapes(CollisionCallback* callback, Set shapes1, Set shapes2) { // Compute the broad-phase collision detection computeBroadPhase(); // Delete all the contact points in the currently overlapping pairs clearContactPoints(); // Compute the narrow-phase collision detection among given sets of shapes computeNarrowPhaseBetweenShapes(callback, shapes1, shapes2); } void CollisionDetection::reportCollisionBetweenShapes(CollisionCallback* callback, Set shapes1, Set shapes2) { // For each possible collision pair of bodies Map::Iterator it; for (it = this.overlappingPairs.begin(); it != this.overlappingPairs.end(); ++it) { OverlappingPair* pair = it.second; ProxyShape* shape1 = pair.getShape1(); ProxyShape* shape2 = pair.getShape2(); assert(shape1.this.broadPhaseID != shape2.this.broadPhaseID); // If both shapes1 and shapes2 sets are non-empty, we check that // shape1 is among on set and shape2 is among the other one if ( !shapes1.empty() && !shapes2.empty() && ( shapes1.count(shape1.this.broadPhaseID) == 0 || shapes2.count(shape2.this.broadPhaseID) == 0 ) && ( shapes1.count(shape2.this.broadPhaseID) == 0 || shapes2.count(shape1.this.broadPhaseID) == 0 ) ) { continue; } if ( !shapes1.empty() && shapes2.empty() && shapes1.count(shape1.this.broadPhaseID) == 0 && shapes1.count(shape2.this.broadPhaseID) == 0) { continue; } if ( !shapes2.empty() && shapes1.empty() && shapes2.count(shape1.this.broadPhaseID) == 0 && shapes2.count(shape2.this.broadPhaseID) == 0) { continue; } // For each contact manifold set of the overlapping pair ContactManifoldSet manifoldSet = pair.getContactManifoldSet(); for (int j=0; j::Iterator it; for (it = this.overlappingPairs.begin(); it != this.overlappingPairs.end(); ) { OverlappingPair* pair = it.second; ProxyShape* shape1 = pair.getShape1(); ProxyShape* shape2 = pair.getShape2(); assert(shape1.this.broadPhaseID != shape2.this.broadPhaseID); // Check if the collision filtering allows collision between the two shapes and // that the two shapes are still overlapping. Otherwise, we destroy the // overlapping pair if (((shape1.getCollideWithMaskBits() shape2.getCollisionCategoryBits()) == 0 || (shape1.getCollisionCategoryBits() shape2.getCollideWithMaskBits()) == 0) || !this.broadPhaseAlgorithm.testOverlappingShapes(shape1, shape2)) { // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved // Destroy the overlapping pair ETKDELETE(OverlappingPair, it.second); it.second = null; it = this.overlappingPairs.erase(it); continue; } else { ++it; } CollisionBody* body1 = shape1.getBody(); CollisionBody* body2 = shape2.getBody(); // Update the contact cache of the overlapping pair pair.update(); // Check that at least one body is awake and not static boolean isBody1Active = !body1.isSleeping() && body1.getType() != STATIC; boolean isBody2Active = !body2.isSleeping() && body2.getType() != STATIC; if (!isBody1Active && !isBody2Active) { continue; } // Check if the bodies are in the set of bodies that cannot collide between each other longpair bodiesIndex = OverlappingPair::computeBodiesIndexPair(body1, body2); if (this.noCollisionPairs.count(bodiesIndex) > 0) { continue; } // Select the narrow phase algorithm to use according to the two collision shapes CollisionShapeType shape1Type = shape1.getCollisionShape().getType(); CollisionShapeType shape2Type = shape2.getCollisionShape().getType(); NarrowPhaseAlgorithm* narrowPhaseAlgorithm = this.collisionMatrix[shape1Type][shape2Type]; // If there is no collision algorithm between those two kinds of shapes if (narrowPhaseAlgorithm == null) { continue; } // Notify the narrow-phase algorithm about the overlapping pair we are going to test narrowPhaseAlgorithm.setCurrentOverlappingPair(pair); // Create the CollisionShapeInfo objects CollisionShapeInfo shape1Info(shape1, shape1.getCollisionShape(), shape1.getLocalToWorldTransform(), pair, shape1.getCachedCollisionData()); CollisionShapeInfo shape2Info(shape2, shape2.getCollisionShape(), shape2.getLocalToWorldTransform(), pair, shape2.getCachedCollisionData()); // Use the narrow-phase collision detection algorithm to check // if there really is a collision. If a collision occurs, the // notifyContact() callback method will be called. narrowPhaseAlgorithm.testCollision(shape1Info, shape2Info, this); } // Add all the contact manifolds (between colliding bodies) to the bodies addAllContactManifoldsToBodies(); } void CollisionDetection::computeNarrowPhaseBetweenShapes(CollisionCallback* callback, Set shapes1, Set shapes2) { this.contactOverlappingPairs.clear(); // For each possible collision pair of bodies Map::Iterator it; for (it = this.overlappingPairs.begin(); it != this.overlappingPairs.end(); ) { OverlappingPair* pair = it.second; ProxyShape* shape1 = pair.getShape1(); ProxyShape* shape2 = pair.getShape2(); assert(shape1.this.broadPhaseID != shape2.this.broadPhaseID); // If both shapes1 and shapes2 sets are non-empty, we check that // shape1 is among on set and shape2 is among the other one if ( !shapes1.empty() && !shapes2.empty() && ( shapes1.count(shape1.this.broadPhaseID) == 0 || shapes2.count(shape2.this.broadPhaseID) == 0 ) && ( shapes1.count(shape2.this.broadPhaseID) == 0 || shapes2.count(shape1.this.broadPhaseID) == 0 ) ) { ++it; continue; } if ( !shapes1.empty() && shapes2.empty() && shapes1.count(shape1.this.broadPhaseID) == 0 && shapes1.count(shape2.this.broadPhaseID) == 0) { ++it; continue; } if ( !shapes2.empty() && shapes1.empty() && shapes2.count(shape1.this.broadPhaseID) == 0 && shapes2.count(shape2.this.broadPhaseID) == 0) { ++it; continue; } // Check if the collision filtering allows collision between the two shapes and // that the two shapes are still overlapping. Otherwise, we destroy the // overlapping pair if ( ( (shape1.getCollideWithMaskBits() shape2.getCollisionCategoryBits()) == 0 || (shape1.getCollisionCategoryBits() shape2.getCollideWithMaskBits()) == 0 ) || !this.broadPhaseAlgorithm.testOverlappingShapes(shape1, shape2) ) { // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved // Destroy the overlapping pair ETKDELETE(OverlappingPair, it.second); it.second = null; it = this.overlappingPairs.erase(it); continue; } else { ++it; } CollisionBody* body1 = shape1.getBody(); CollisionBody* body2 = shape2.getBody(); // Update the contact cache of the overlapping pair pair.update(); // Check if the two bodies are allowed to collide, otherwise, we do not test for collision if (body1.getType() != DYNAMIC && body2.getType() != DYNAMIC) { continue; } longpair bodiesIndex = OverlappingPair::computeBodiesIndexPair(body1, body2); if (this.noCollisionPairs.count(bodiesIndex) > 0) { continue; } // Check if the two bodies are sleeping, if so, we do no test collision between them if (body1.isSleeping() && body2.isSleeping()) { continue; } // Select the narrow phase algorithm to use according to the two collision shapes CollisionShapeType shape1Type = shape1.getCollisionShape().getType(); CollisionShapeType shape2Type = shape2.getCollisionShape().getType(); NarrowPhaseAlgorithm* narrowPhaseAlgorithm = this.collisionMatrix[shape1Type][shape2Type]; // If there is no collision algorithm between those two kinds of shapes if (narrowPhaseAlgorithm == null) { continue; } // Notify the narrow-phase algorithm about the overlapping pair we are going to test narrowPhaseAlgorithm.setCurrentOverlappingPair(pair); // Create the CollisionShapeInfo objects CollisionShapeInfo shape1Info(shape1, shape1.getCollisionShape(), shape1.getLocalToWorldTransform(), pair, shape1.getCachedCollisionData()); CollisionShapeInfo shape2Info(shape2, shape2.getCollisionShape(), shape2.getLocalToWorldTransform(), pair, shape2.getCachedCollisionData()); TestCollisionBetweenShapesCallback narrowPhaseCallback(callback); // Use the narrow-phase collision detection algorithm to check // if there really is a collision narrowPhaseAlgorithm.testCollision(shape1Info, shape2Info, narrowPhaseCallback); } // Add all the contact manifolds (between colliding bodies) to the bodies addAllContactManifoldsToBodies(); } void CollisionDetection::broadPhaseNotifyOverlappingPair(ProxyShape* shape1, ProxyShape* shape2) { assert(shape1.this.broadPhaseID != shape2.this.broadPhaseID); // If the two proxy collision shapes are from the same body, skip it if (shape1.getBody().getID() == shape2.getBody().getID()) { return; } // Check if the collision filtering allows collision between the two shapes if ( (shape1.getCollideWithMaskBits() shape2.getCollisionCategoryBits()) == 0 || (shape1.getCollisionCategoryBits() shape2.getCollideWithMaskBits()) == 0) { return; } // Compute the overlapping pair ID overlappingpairid pairID = OverlappingPair::computeID(shape1, shape2); // Check if the overlapping pair already exists if (this.overlappingPairs.find(pairID) != this.overlappingPairs.end()) return; // Compute the maximum number of contact manifolds for this pair int nbMaxManifolds = CollisionShape::computeNbMaxContactManifolds(shape1.getCollisionShape().getType(), shape2.getCollisionShape().getType()); // Create the overlapping pair and add it into the set of overlapping pairs OverlappingPair* newPair = ETKNEW(OverlappingPair, shape1, shape2, nbMaxManifolds); assert(newPair != null); this.overlappingPairs.set(pairID, newPair); // Wake up the two bodies shape1.getBody().setIsSleeping(false); shape2.getBody().setIsSleeping(false); } void CollisionDetection::removeProxyCollisionShape(ProxyShape* proxyShape) { // Remove all the overlapping pairs involving this proxy shape Map::Iterator it; for (it = this.overlappingPairs.begin(); it != this.overlappingPairs.end(); ) { if (it.second.getShape1().this.broadPhaseID == proxyShape.this.broadPhaseID|| it.second.getShape2().this.broadPhaseID == proxyShape.this.broadPhaseID) { // TODO : Remove all the contact manifold of the overlapping pair from the contact manifolds list of the two bodies involved // Destroy the overlapping pair ETKDELETE(OverlappingPair, it.second); it.second = null; it = this.overlappingPairs.erase(it); } else { ++it; } } // Remove the body from the broad-phase this.broadPhaseAlgorithm.removeProxyCollisionShape(proxyShape); } void CollisionDetection::notifyContact(OverlappingPair* overlappingPair, ContactPointInfo contactInfo) { // If it is the first contact since the pairs are overlapping if (overlappingPair.getNbContactPoints() == 0) { // Trigger a callback event if (this.world.this.eventListener != NULL) { this.world.this.eventListener.beginContact(contactInfo); } } // Create a new contact createContact(overlappingPair, contactInfo); // Trigger a callback event for the new contact if (this.world.this.eventListener != NULL) { this.world.this.eventListener.newContact(contactInfo); } } void CollisionDetection::createContact(OverlappingPair* overlappingPair, ContactPointInfo contactInfo) { // Create a new contact ContactPoint* contact = ETKNEW(ContactPoint, contactInfo); // Add the contact to the contact manifold set of the corresponding overlapping pair overlappingPair.addContact(contact); // Add the overlapping pair into the set of pairs in contact during narrow-phase overlappingpairid pairId = OverlappingPair::computeID(overlappingPair.getShape1(), overlappingPair.getShape2()); this.contactOverlappingPairs.set(pairId, overlappingPair); } void CollisionDetection::addAllContactManifoldsToBodies() { // For each overlapping pairs in contact during the narrow-phase Map::Iterator it; for (it = this.contactOverlappingPairs.begin(); it != this.contactOverlappingPairs.end(); ++it) { // Add all the contact manifolds of the pair into the list of contact manifolds // of the two bodies involved in the contact addContactManifoldToBody(it.second); } } void CollisionDetection::addContactManifoldToBody(OverlappingPair* pair) { assert(pair != null); CollisionBody* body1 = pair.getShape1().getBody(); CollisionBody* body2 = pair.getShape2().getBody(); ContactManifoldSet manifoldSet = pair.getContactManifoldSet(); // For each contact manifold in the set of manifolds in the pair for (int i=0; i 0); // Add the contact manifold at the beginning of the linked // list of contact manifolds of the first body body1.this.contactManifoldsList = ETKNEW(ContactManifoldListElement, contactManifold, body1.this.contactManifoldsList);; // Add the contact manifold at the beginning of the linked // list of the contact manifolds of the second body body2.this.contactManifoldsList = ETKNEW(ContactManifoldListElement, contactManifold, body2.this.contactManifoldsList);; } } void CollisionDetection::clearContactPoints() { // For each overlapping pair Map::Iterator it; for (it = this.overlappingPairs.begin(); it != this.overlappingPairs.end(); ++it) { it.second.clearContactPoints(); } } void CollisionDetection::fillInCollisionMatrix() { // For each possible type of collision shape for (int i=0; i