From 8b9fdc15a71501ef1990d614ec7a847d809cf98b Mon Sep 17 00:00:00 2001 From: Daniel Chappuis <chappuis.daniel@gmail.com> Date: Sat, 27 Jun 2020 23:48:19 +0200 Subject: [PATCH 1/7] =?UTF-8?q?Fix=20issue=20in=20SAT=20with=20edge-edge?= =?UTF-8?q?=20contact=20(wrong=20contact=20normal)=20and=20favor=20face=20?= =?UTF-8?q?contacts=20over=20edge-edge=20contacts=20in=20polyhedron=20vs?= =?UTF-8?q?=20polyhedron=20collision=20in=20SAT=C2=A0algorithm=20for=20bet?= =?UTF-8?q?ter=20stability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../narrowphase/SAT/SATAlgorithm.cpp | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/collision/narrowphase/SAT/SATAlgorithm.cpp b/src/collision/narrowphase/SAT/SATAlgorithm.cpp index 54f8e90a..4271b900 100644 --- a/src/collision/narrowphase/SAT/SATAlgorithm.cpp +++ b/src/collision/narrowphase/SAT/SATAlgorithm.cpp @@ -657,18 +657,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn Vector3 closestPointPolyhedron1EdgeLocalSpace = polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge; // Compute the world normal - // We use the direction from the centroid to the edge of the shape that is not a triangle - // to avoid possible degeneracies when axis direction is orthogonal to triangle normal - Vector3 normal; - if (isShape1Triangle) { - normal = polyhedron2->getCentroid() - closestPointPolyhedron2Edge; - } - else { - normal = polyhedron1ToPolyhedron2.getOrientation() * ((polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge) - polyhedron1->getCentroid()); - } - - //Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * minEdgeVsEdgeSeparatingAxisPolyhedron2Space; - Vector3 normalWorld = narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex].getOrientation() * normal.getUnit(); + Vector3 normalWorld = narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex].getOrientation() * separatingAxisPolyhedron2Space; // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle TriangleShape::computeSmoothTriangleMeshContact(narrowPhaseInfoBatch.collisionShapes1[batchIndex], narrowPhaseInfoBatch.collisionShapes2[batchIndex], @@ -794,7 +783,15 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn break; } - if (penetrationDepth < minPenetrationDepth) { + // If the current minimum penetration depth is along a face normal axis (isMinPenetrationFaceNormal=true) and we have found a new + // smaller pentration depth along an edge-edge cross-product axis we want to favor the face normal axis because contact manifolds between + // faces have more contact points and therefore more stable than the single contact point of an edge-edge collision. It means that if the new minimum + // penetration depth from the edge-edge contact is only a little bit smaller than the current minPenetrationDepth (from a face contact), we favor + // the face contact and do not generate an edge-edge contact. However, if the new penetration depth from the edge-edge contact is really smaller than + // the current one, we generate an edge-edge contact. + // To do this, we use a relative and absolute bias to increase a little bit the new penetration depth from the edge-edge contact during the comparison test + if ((isMinPenetrationFaceNormal && penetrationDepth1 * SEPARATING_AXIS_RELATIVE_TOLERANCE + SEPARATING_AXIS_ABSOLUTE_TOLERANCE < minPenetrationDepth) || + (!isMinPenetrationFaceNormal && penetrationDepth < minPenetrationDepth)) { minPenetrationDepth = penetrationDepth; isMinPenetrationFaceNormalPolyhedron1 = false; @@ -862,17 +859,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn Vector3 closestPointPolyhedron1EdgeLocalSpace = polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge; // Compute the world normal - // We use the direction from the centroid to the edge of the shape that is not a triangle - // to avoid possible degeneracies when axis direction is orthogonal to triangle normal - Vector3 normal; - if (isShape1Triangle) { - normal = polyhedron2->getCentroid() - closestPointPolyhedron2Edge; - } - else { - normal = polyhedron1ToPolyhedron2.getOrientation() * ((polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge) - polyhedron1->getCentroid()); - } - //Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * minEdgeVsEdgeSeparatingAxisPolyhedron2Space; - Vector3 normalWorld = narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex].getOrientation() * normal.getUnit(); + Vector3 normalWorld = narrowPhaseInfoBatch.shape2ToWorldTransforms[batchIndex].getOrientation() * minEdgeVsEdgeSeparatingAxisPolyhedron2Space; // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle TriangleShape::computeSmoothTriangleMeshContact(narrowPhaseInfoBatch.collisionShapes1[batchIndex], narrowPhaseInfoBatch.collisionShapes2[batchIndex], From fa05e1561eea366a47e601663ea0af07f031aee1 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis <chappuis.daniel@gmail.com> Date: Tue, 7 Jul 2020 18:27:01 +0200 Subject: [PATCH 2/7] Use const parameter in PhysicsWorld::setGravity() method --- include/reactphysics3d/engine/PhysicsWorld.h | 2 +- src/engine/PhysicsWorld.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/reactphysics3d/engine/PhysicsWorld.h b/include/reactphysics3d/engine/PhysicsWorld.h index 9aa3da2c..8ebf4ab1 100644 --- a/include/reactphysics3d/engine/PhysicsWorld.h +++ b/include/reactphysics3d/engine/PhysicsWorld.h @@ -399,7 +399,7 @@ class PhysicsWorld { Vector3 getGravity() const; /// Set the gravity vector of the world - void setGravity(Vector3& gravity); + void setGravity(const Vector3& gravity); /// Return if the gravity is on bool isGravityEnabled() const; diff --git a/src/engine/PhysicsWorld.cpp b/src/engine/PhysicsWorld.cpp index b188f635..19e55eab 100644 --- a/src/engine/PhysicsWorld.cpp +++ b/src/engine/PhysicsWorld.cpp @@ -975,7 +975,7 @@ void PhysicsWorld::setNbIterationsPositionSolver(uint nbIterations) { /** * @param gravity The gravity vector (in meter per seconds squared) */ -void PhysicsWorld::setGravity(Vector3& gravity) { +void PhysicsWorld::setGravity(const Vector3& gravity) { mConfig.gravity = gravity; From 68212e26d875c1c758fc525159eee738ac94a936 Mon Sep 17 00:00:00 2001 From: manuel5975p <manuel.winkler@gmx.ch> Date: Sun, 12 Jul 2020 01:01:40 +0200 Subject: [PATCH 3/7] Fix typos in documentation Add variable type in declaration Fix PolygonaVertexArray -> PolygonVertexArray --- documentation/UserManual/ReactPhysics3D-UserManual.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/UserManual/ReactPhysics3D-UserManual.tex b/documentation/UserManual/ReactPhysics3D-UserManual.tex index a7a33052..8aaf65bf 100644 --- a/documentation/UserManual/ReactPhysics3D-UserManual.tex +++ b/documentation/UserManual/ReactPhysics3D-UserManual.tex @@ -1201,7 +1201,7 @@ indices[16]=2; indices[17]=3; indices[18]=7; indices[19]=6; indices[20]=0; indices[21]=4; indices[22]=7; indices[23]=3; // Description of the six faces of the convex mesh -polygonFaces = new PolygonVertexArray::PolygonFace[6]; +PolygonVertexArray::PolygonFace* polygonFaces = new PolygonVertexArray::PolygonFace[6]; PolygonVertexArray::PolygonFace* face = polygonFaces; for (int f = 0; f < 6; f++) { @@ -1215,7 +1215,7 @@ for (int f = 0; f < 6; f++) { } // Create the polygon vertex array -PolygonaVertexArray* polygonVertexArray = new PolygonVertexArray(8, vertices, 3 x sizeof(float), +PolygonVertexArray* polygonVertexArray = new PolygonVertexArray(8, vertices, 3 x sizeof(float), indices, sizeof(int), 6, polygonFaces, PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE, PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE); From 3b2f973ffdffbcbfaa998bc59bb478a245d631a7 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis <chappuis.daniel@gmail.com> Date: Sun, 19 Jul 2020 01:02:02 +0200 Subject: [PATCH 4/7] Fix issue in Quaternion::Quaternion(Matrix3x3& matrix) constructor and add corresponding unit test --- src/mathematics/Quaternion.cpp | 2 +- test/tests/mathematics/TestQuaternion.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/mathematics/Quaternion.cpp b/src/mathematics/Quaternion.cpp index d1451a3d..a8918f93 100644 --- a/src/mathematics/Quaternion.cpp +++ b/src/mathematics/Quaternion.cpp @@ -106,7 +106,7 @@ Quaternion::Quaternion(const Matrix3x3& matrix) { // Compute the quaternion x = decimal(0.5) * r; y = (matrix[0][1] + matrix[1][0]) * s; - z = (matrix[2][0] - matrix[0][2]) * s; + z = (matrix[2][0] + matrix[0][2]) * s; w = (matrix[2][1] - matrix[1][2]) * s; } } diff --git a/test/tests/mathematics/TestQuaternion.h b/test/tests/mathematics/TestQuaternion.h index 3a25cba9..042aa577 100644 --- a/test/tests/mathematics/TestQuaternion.h +++ b/test/tests/mathematics/TestQuaternion.h @@ -90,6 +90,21 @@ class TestQuaternion : public Test { rp3d_test(approxEqual(quaternion4.z, mQuaternion1.z)); rp3d_test(approxEqual(quaternion4.w, mQuaternion1.w)); + Matrix3x3 original(0.001743,-0.968608,0.248589,-0.614229,-0.197205,-0.764090,0.789126,-0.151359,-0.595290); + Matrix3x3 converted = Quaternion(original).getMatrix(); + rp3d_test(approxEqual(original[0][0], converted[0][0], 0.0001)); + rp3d_test(approxEqual(original[0][1], converted[0][1], 0.0001)); + rp3d_test(approxEqual(original[0][2], converted[0][2], 0.0001)); + rp3d_test(approxEqual(original[1][0], converted[1][0], 0.0001)); + rp3d_test(approxEqual(original[1][1], converted[1][1], 0.0001)); + rp3d_test(approxEqual(original[1][2], converted[1][2], 0.0001)); + rp3d_test(approxEqual(original[2][0], converted[2][0], 0.0001)); + rp3d_test(approxEqual(original[2][1], converted[2][1], 0.0001)); + rp3d_test(approxEqual(original[2][2], converted[2][2], 0.0001)); + + std::cout << original.to_string() << std::endl; + std::cout << converted.to_string() << std::endl; + // Test conversion from Euler angles to quaternion const decimal PI_OVER_2 = PI * decimal(0.5); From e7d9e106e92584013b76c4f08269615557687496 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis <chappuis.daniel@gmail.com> Date: Fri, 24 Jul 2020 07:37:09 +0200 Subject: [PATCH 5/7] Remove unused friend class declaration --- include/reactphysics3d/collision/shapes/CollisionShape.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/reactphysics3d/collision/shapes/CollisionShape.h b/include/reactphysics3d/collision/shapes/CollisionShape.h index 77b56de6..ad7bb3d2 100644 --- a/include/reactphysics3d/collision/shapes/CollisionShape.h +++ b/include/reactphysics3d/collision/shapes/CollisionShape.h @@ -164,7 +164,6 @@ class CollisionShape { friend class Collider; friend class CollisionBody; friend class RigidBody; - friend class PhyscisWorld; friend class BroadPhaseSystem; }; From b5a7454f4cfc1a955191e7a8207ebfe1970a2c84 Mon Sep 17 00:00:00 2001 From: SlavicPotato <sp@eth.si> Date: Tue, 11 Aug 2020 09:14:18 +0200 Subject: [PATCH 6/7] Fix DebugRenderer contact point sphere radius --- src/utils/DebugRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/DebugRenderer.cpp b/src/utils/DebugRenderer.cpp index e403253b..4e1533f7 100644 --- a/src/utils/DebugRenderer.cpp +++ b/src/utils/DebugRenderer.cpp @@ -463,7 +463,7 @@ void DebugRenderer::onContact(const CollisionCallback::CallbackData& callbackDat if (getIsDebugItemDisplayed(DebugItem::CONTACT_POINT)) { // Contact point - drawSphere(point, DEFAULT_CONTACT_POINT_SPHERE_RADIUS, mMapDebugItemWithColor[DebugItem::CONTACT_POINT]); + drawSphere(point, mContactPointSphereRadius, mMapDebugItemWithColor[DebugItem::CONTACT_POINT]); } if (getIsDebugItemDisplayed(DebugItem::CONTACT_NORMAL)) { From 98ba2f10e6f3c21b684d94e2970c89c8fd6a9499 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis <chappuis.daniel@gmail.com> Date: Wed, 2 Sep 2020 21:59:19 +0200 Subject: [PATCH 7/7] Fix issue with contact manifolds order in islands creation process --- include/reactphysics3d/engine/PhysicsWorld.h | 5 +++ .../systems/CollisionDetectionSystem.h | 6 ++-- src/engine/PhysicsWorld.cpp | 20 ++++++++++- src/systems/CollisionDetectionSystem.cpp | 33 ++++++++++++++----- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/include/reactphysics3d/engine/PhysicsWorld.h b/include/reactphysics3d/engine/PhysicsWorld.h index 8ebf4ab1..907bf7b5 100644 --- a/include/reactphysics3d/engine/PhysicsWorld.h +++ b/include/reactphysics3d/engine/PhysicsWorld.h @@ -241,6 +241,11 @@ class PhysicsWorld { /// All the islands of bodies of the current frame Islands mIslands; + /// Order in which to process the ContactPairs for contact creation such that + /// all the contact manifolds and contact points of a given island are packed together + /// This array contains the indices of the ContactPairs. + List<uint32> mProcessContactPairsOrderIslands; + /// Contact solver system ContactSolverSystem mContactSolverSystem; diff --git a/include/reactphysics3d/systems/CollisionDetectionSystem.h b/include/reactphysics3d/systems/CollisionDetectionSystem.h index b2d24f39..4909aca5 100644 --- a/include/reactphysics3d/systems/CollisionDetectionSystem.h +++ b/include/reactphysics3d/systems/CollisionDetectionSystem.h @@ -237,9 +237,6 @@ class CollisionDetectionSystem { void reducePotentialContactManifolds(List<ContactPair>* contactPairs, List<ContactManifoldInfo>& potentialContactManifolds, const List<ContactPointInfo>& potentialContactPoints) const; - /// Create the actual contact manifolds and contacts points (from potential contacts) for a given contact pair - void createContacts(); - /// Compute the lost contact pairs (contact pairs in contact in the previous frame but not in the current one) void computeLostContactPairs(); @@ -279,6 +276,9 @@ class CollisionDetectionSystem { /// Filter the overlapping pairs to keep only the pairs where two given bodies are involved void filterOverlappingPairs(Entity body1Entity, Entity body2Entity, List<uint64>& convexPairs, List<uint64>& concavePairs) const; + /// Create the actual contact manifolds and contacts points (from potential contacts) for a given contact pair + void createContacts(); + public : // -------------------- Methods -------------------- // diff --git a/src/engine/PhysicsWorld.cpp b/src/engine/PhysicsWorld.cpp index 19e55eab..fc59195d 100644 --- a/src/engine/PhysicsWorld.cpp +++ b/src/engine/PhysicsWorld.cpp @@ -59,7 +59,7 @@ PhysicsWorld::PhysicsWorld(MemoryManager& memoryManager, const WorldSettings& wo mSliderJointsComponents(mMemoryManager.getHeapAllocator()), mCollisionDetection(this, mCollidersComponents, mTransformComponents, mCollisionBodyComponents, mRigidBodyComponents, mMemoryManager), mCollisionBodies(mMemoryManager.getHeapAllocator()), mEventListener(nullptr), - mName(worldSettings.worldName), mIslands(mMemoryManager.getSingleFrameAllocator()), + mName(worldSettings.worldName), mIslands(mMemoryManager.getSingleFrameAllocator()), mProcessContactPairsOrderIslands(mMemoryManager.getSingleFrameAllocator()), mContactSolverSystem(mMemoryManager, *this, mIslands, mCollisionBodyComponents, mRigidBodyComponents, mCollidersComponents, mConfig.restitutionVelocityThreshold), mConstraintSolverSystem(*this, mIslands, mRigidBodyComponents, mTransformComponents, mJointsComponents, @@ -342,6 +342,9 @@ void PhysicsWorld::update(decimal timeStep) { // Create the islands createIslands(); + // Create the actual narrow-phase contacts + mCollisionDetection.createContacts(); + // Report the contacts to the user mCollisionDetection.reportContactsAndTriggers(); @@ -374,6 +377,8 @@ void PhysicsWorld::update(decimal timeStep) { // Reset the islands mIslands.clear(); + mProcessContactPairsOrderIslands.clear(true); + // Generate debug rendering primitives (if enabled) if (mIsDebugRenderingEnabled) { mDebugRenderer.computeDebugRenderingPrimitives(*this); @@ -747,6 +752,8 @@ void PhysicsWorld::createIslands() { RP3D_PROFILE("PhysicsWorld::createIslands()", mProfiler); + assert(mProcessContactPairsOrderIslands.size() == 0); + // Reset all the isAlreadyInIsland variables of bodies and joints for (uint b=0; b < mRigidBodyComponents.getNbComponents(); b++) { @@ -814,6 +821,8 @@ void PhysicsWorld::createIslands() { if (mRigidBodyComponents.hasComponent(pair.body1Entity) && mRigidBodyComponents.hasComponent(pair.body2Entity) && !mCollidersComponents.getIsTrigger(pair.collider1Entity) && !mCollidersComponents.getIsTrigger(pair.collider2Entity)) { + mProcessContactPairsOrderIslands.add(contactPairs[p]); + assert(pair.potentialContactManifoldsIndices.size() > 0); nbTotalManifolds += pair.potentialContactManifoldsIndices.size(); @@ -871,6 +880,15 @@ void PhysicsWorld::createIslands() { } } + for (uint32 i=0; i < (*mCollisionDetection.mCurrentContactPairs).size(); i++) { + + ContactPair& contactPair = (*mCollisionDetection.mCurrentContactPairs)[i]; + + if (mRigidBodyComponents.hasComponent(contactPair.body1Entity) && mRigidBodyComponents.hasComponent(contactPair.body2Entity)) { + + } + } + mCollisionDetection.mMapBodyToContactPairs.clear(true); } diff --git a/src/systems/CollisionDetectionSystem.cpp b/src/systems/CollisionDetectionSystem.cpp index 24f9660c..b794b32d 100644 --- a/src/systems/CollisionDetectionSystem.cpp +++ b/src/systems/CollisionDetectionSystem.cpp @@ -532,11 +532,6 @@ void CollisionDetectionSystem::computeNarrowPhase() { assert(mCurrentContactManifolds->size() == 0); assert(mCurrentContactPoints->size() == 0); - - // Create the actual narrow-phase contacts - createContacts(); - - mNarrowPhaseInput.clear(); } // Compute the narrow-phase collision detection for the testOverlap() methods. @@ -716,10 +711,30 @@ void CollisionDetectionSystem::createContacts() { mCurrentContactManifolds->reserve(mCurrentContactPairs->size()); mCurrentContactPoints->reserve(mCurrentContactManifolds->size()); - // For each contact pair - for (uint p=0; p < (*mCurrentContactPairs).size(); p++) { + // We go through all the contact pairs and add the pairs with a least a CollisionBody at the end of the + // mProcessContactPairsOrderIslands array because those pairs have not been added during the islands + // creation (only the pairs with two RigidBodies are added during island creation) + Set<uint32> processedContactPairsIndices(mMemoryManager.getSingleFrameAllocator(), mCurrentContactPairs->size()); + for (uint32 i=0; i < mWorld->mProcessContactPairsOrderIslands.size(); i++) { + processedContactPairsIndices.add(mWorld->mProcessContactPairsOrderIslands[i]); + } + for (uint32 i=0; i < mCurrentContactPairs->size(); i++) { + if (!processedContactPairsIndices.contains(i)) { + mWorld->mProcessContactPairsOrderIslands.add(i); + } + } - ContactPair& contactPair = (*mCurrentContactPairs)[p]; + assert(mWorld->mProcessContactPairsOrderIslands.size() == (*mCurrentContactPairs).size()); + + // Process the contact pairs in the order defined by the islands such that the contact manifolds and + // contact points of a given island are packed together in the array of manifolds and contact points + for (uint p=0; p < mWorld->mProcessContactPairsOrderIslands.size(); p++) { + + uint32 contactPairIndex = mWorld->mProcessContactPairsOrderIslands[p]; + + processedContactPairsIndices.add(contactPairIndex); + + ContactPair& contactPair = (*mCurrentContactPairs)[contactPairIndex]; contactPair.contactManifoldsIndex = mCurrentContactManifolds->size(); contactPair.nbContactManifolds = contactPair.potentialContactManifoldsIndices.size(); @@ -772,6 +787,8 @@ void CollisionDetectionSystem::createContacts() { // Reset the potential contacts mPotentialContactPoints.clear(true); mPotentialContactManifolds.clear(true); + + mNarrowPhaseInput.clear(); } // Compute the lost contact pairs (contact pairs in contact in the previous frame but not in the current one)