From 5851ae5309dbd77d2b408a6a0c825bbe52f0ac2f Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Wed, 10 Oct 2018 20:24:51 +0200 Subject: [PATCH 01/12] Fix issue in PoolAllocator: Use default base allocator instead of free() --- src/memory/DefaultPoolAllocator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory/DefaultPoolAllocator.cpp b/src/memory/DefaultPoolAllocator.cpp index 5d4f4ff0..39f84eee 100644 --- a/src/memory/DefaultPoolAllocator.cpp +++ b/src/memory/DefaultPoolAllocator.cpp @@ -135,9 +135,9 @@ void* DefaultPoolAllocator::allocate(size_t size) { MemoryBlock* currentMemoryBlocks = mMemoryBlocks; mNbAllocatedMemoryBlocks += 64; mMemoryBlocks = static_cast(MemoryManager::getBaseAllocator().allocate(mNbAllocatedMemoryBlocks * sizeof(MemoryBlock))); - memcpy(mMemoryBlocks, currentMemoryBlocks,mNbCurrentMemoryBlocks * sizeof(MemoryBlock)); + memcpy(mMemoryBlocks, currentMemoryBlocks, mNbCurrentMemoryBlocks * sizeof(MemoryBlock)); memset(mMemoryBlocks + mNbCurrentMemoryBlocks, 0, 64 * sizeof(MemoryBlock)); - free(currentMemoryBlocks); + MemoryManager::getBaseAllocator().release(currentMemoryBlocks, mNbCurrentMemoryBlocks * sizeof(MemoryBlock)); } // Allocate a new memory blocks for the corresponding heap and divide it in many From 37cf3de9fae3a3889657fe510f0ceb0d5e284228 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Wed, 10 Oct 2018 20:37:45 +0200 Subject: [PATCH 02/12] Edit changelog file --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7b1ba9..a3374ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Bug [#37](https://github.com/DanielChappuis/reactphysics3d/issues/37) has been fixed. - Bug [#62](https://github.com/DanielChappuis/reactphysics3d/issues/62) has been fixed. - Bug [#63](https://github.com/DanielChappuis/reactphysics3d/issues/63) has been fixed. + - Bug: the free() method was called in PoolAllocator instead of release() method of base allocator. ## Version 0.7.0 (May 1, 2018) From 6ef177329b003d5eca4e44a8e688f70ca6b72162 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Wed, 16 Jan 2019 20:41:41 +0100 Subject: [PATCH 03/12] Fix issue with very small determinant when inverting a 3x3 matrix --- CHANGELOG.md | 1 + src/mathematics/Matrix3x3.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3374ad7..5d30a13d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Bug [#37](https://github.com/DanielChappuis/reactphysics3d/issues/37) has been fixed. - Bug [#62](https://github.com/DanielChappuis/reactphysics3d/issues/62) has been fixed. - Bug [#63](https://github.com/DanielChappuis/reactphysics3d/issues/63) has been fixed. + - Bug [#82](https://github.com/DanielChappuis/reactphysics3d/issues/82) has been fixed. - Bug: the free() method was called in PoolAllocator instead of release() method of base allocator. ## Version 0.7.0 (May 1, 2018) diff --git a/src/mathematics/Matrix3x3.cpp b/src/mathematics/Matrix3x3.cpp index 74ffd9bf..5fb97376 100644 --- a/src/mathematics/Matrix3x3.cpp +++ b/src/mathematics/Matrix3x3.cpp @@ -48,7 +48,7 @@ Matrix3x3 Matrix3x3::getInverse() const { decimal determinant = getDeterminant(); // Check if the determinant is equal to zero - assert(std::abs(determinant) > MACHINE_EPSILON); + assert(determinant != decimal(0.0)); decimal invDeterminant = decimal(1.0) / determinant; From b7506013e5a6eb4538a83f10ce2d9e43aa4f8ba0 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Tue, 22 Jan 2019 23:02:24 +0100 Subject: [PATCH 04/12] Add credits in README file --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 479ac037..1ee78d6f 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,9 @@ your application, it is recommended to checkout the "master" branch. ## Issues If you find any issue with the library, you can report it on the issue tracker [here](https://github.com/DanielChappuis/reactphysics3d/issues). + +## Credits + +Thanks a lot to Erin Catto, Dirk Gregorius, Erwin Coumans, Pierre Terdiman and Christer Ericson for their amazing GDC presentations, +their physics engines, their books or articles and their contributions on many physics engine forums. + From 6bcb586d5228401b63ce82ec11d81cfef729f634 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Thu, 24 Jan 2019 17:50:50 +0100 Subject: [PATCH 05/12] Fix issue with bias in SATAlgorithm and add asserts --- src/collision/NarrowPhaseInfo.cpp | 1 + .../narrowphase/SAT/SATAlgorithm.cpp | 68 ++++++++++++------- src/collision/narrowphase/SAT/SATAlgorithm.h | 9 +-- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/collision/NarrowPhaseInfo.cpp b/src/collision/NarrowPhaseInfo.cpp index 10a29332..52fc60a5 100644 --- a/src/collision/NarrowPhaseInfo.cpp +++ b/src/collision/NarrowPhaseInfo.cpp @@ -65,6 +65,7 @@ void NarrowPhaseInfo::addContactPoint(const Vector3& contactNormal, decimal penD const Vector3& localPt1, const Vector3& localPt2) { assert(penDepth > decimal(0.0)); + assert(contactNormal.length() > decimal(0.8)); // Get the memory allocator MemoryAllocator& allocator = overlappingPair->getTemporaryAllocator(); diff --git a/src/collision/narrowphase/SAT/SATAlgorithm.cpp b/src/collision/narrowphase/SAT/SATAlgorithm.cpp index 374a812a..2ca272fa 100644 --- a/src/collision/narrowphase/SAT/SATAlgorithm.cpp +++ b/src/collision/narrowphase/SAT/SATAlgorithm.cpp @@ -40,7 +40,8 @@ using namespace reactphysics3d; // Static variables initialization -const decimal SATAlgorithm::SAME_SEPARATING_AXIS_BIAS = decimal(0.001); +const decimal SATAlgorithm::SEPARATING_AXIS_RELATIVE_TOLERANCE = decimal(1.02); +const decimal SATAlgorithm::SEPARATING_AXIS_ABSOLUTE_TOLERANCE = decimal(0.005); // Constructor SATAlgorithm::SATAlgorithm(MemoryAllocator& memoryAllocator) : mMemoryAllocator(memoryAllocator) { @@ -478,8 +479,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn uint minSeparatingEdge2Index = 0; Vector3 separatingEdge1A, separatingEdge1B; Vector3 separatingEdge2A, separatingEdge2B; - Vector3 minEdgeVsEdgeSeparatingAxisPolyhedron2Space; - bool isShape1Triangle = polyhedron1->getName() == CollisionShapeName::TRIANGLE; + const bool isShape1Triangle = polyhedron1->getName() == CollisionShapeName::TRIANGLE; LastFrameCollisionInfo* lastFrameCollisionInfo = narrowPhaseInfo->getLastFrameCollisionInfo(); @@ -633,7 +633,6 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn normal = polyhedron1ToPolyhedron2.getOrientation() * ((polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge) - polyhedron1->getCentroid()); } - //Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * minEdgeVsEdgeSeparatingAxisPolyhedron2Space; Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * normal.getUnit(); // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle @@ -657,40 +656,58 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn } } + minPenetrationDepth = DECIMAL_LARGEST; + isMinPenetrationFaceNormal = false; + // Test all the face normals of the polyhedron 1 for separating axis - uint faceIndex; - decimal penetrationDepth = testFacesDirectionPolyhedronVsPolyhedron(polyhedron1, polyhedron2, polyhedron1ToPolyhedron2, faceIndex); - if (penetrationDepth <= decimal(0.0)) { + uint faceIndex1; + decimal penetrationDepth1 = testFacesDirectionPolyhedronVsPolyhedron(polyhedron1, polyhedron2, polyhedron1ToPolyhedron2, faceIndex1); + if (penetrationDepth1 <= decimal(0.0)) { lastFrameCollisionInfo->satIsAxisFacePolyhedron1 = true; lastFrameCollisionInfo->satIsAxisFacePolyhedron2 = false; - lastFrameCollisionInfo->satMinAxisFaceIndex = faceIndex; + lastFrameCollisionInfo->satMinAxisFaceIndex = faceIndex1; // We have found a separating axis return false; } - if (penetrationDepth < minPenetrationDepth - SAME_SEPARATING_AXIS_BIAS) { - isMinPenetrationFaceNormal = true; - minPenetrationDepth = penetrationDepth; - minFaceIndex = faceIndex; - isMinPenetrationFaceNormalPolyhedron1 = true; - } // Test all the face normals of the polyhedron 2 for separating axis - penetrationDepth = testFacesDirectionPolyhedronVsPolyhedron(polyhedron2, polyhedron1, polyhedron2ToPolyhedron1, faceIndex); - if (penetrationDepth <= decimal(0.0)) { + uint faceIndex2; + decimal penetrationDepth2 = testFacesDirectionPolyhedronVsPolyhedron(polyhedron2, polyhedron1, polyhedron2ToPolyhedron1, faceIndex2); + if (penetrationDepth2 <= decimal(0.0)) { lastFrameCollisionInfo->satIsAxisFacePolyhedron1 = false; lastFrameCollisionInfo->satIsAxisFacePolyhedron2 = true; - lastFrameCollisionInfo->satMinAxisFaceIndex = faceIndex; + lastFrameCollisionInfo->satMinAxisFaceIndex = faceIndex2; // We have found a separating axis return false; } - if (penetrationDepth < minPenetrationDepth - SAME_SEPARATING_AXIS_BIAS) { + + // Here we know that we have found penetration along both axis of a face of polyhedron1 and a face of + // polyhedron2. If the two penetration depths are almost the same, we need to make sure we always prefer + // one axis to the other for consistency between frames. This is to prevent the contact manifolds to switch + // from one reference axis to the other for a face to face resting contact for instance. This is better for + // stability. To do this, we use a relative and absolute bias to move penetrationDepth2 a little bit to the right. + // Now if: + // penetrationDepth1 < penetrationDepth2: Nothing happens and we use axis of polygon 1 + // penetrationDepth1 ~ penetrationDepth2: Until penetrationDepth1 becomes significantly less than penetrationDepth2 we still use axis of polygon 1 + // penetrationDepth1 >> penetrationDepth2: penetrationDepth2 is now significantly less than penetrationDepth1 and we use polygon 2 axis + if (penetrationDepth1 < penetrationDepth2 * SEPARATING_AXIS_RELATIVE_TOLERANCE + SEPARATING_AXIS_ABSOLUTE_TOLERANCE) { + + // We use penetration axis of polygon 1 isMinPenetrationFaceNormal = true; - minPenetrationDepth = penetrationDepth; - minFaceIndex = faceIndex; + minPenetrationDepth = penetrationDepth1; + minFaceIndex = faceIndex1; + isMinPenetrationFaceNormalPolyhedron1 = true; + } + else { + + // We use penetration axis of polygon 2 + isMinPenetrationFaceNormal = true; + minPenetrationDepth = penetrationDepth2; + minFaceIndex = faceIndex2; isMinPenetrationFaceNormalPolyhedron1 = false; } @@ -735,7 +752,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn return false; } - if (penetrationDepth < minPenetrationDepth - SAME_SEPARATING_AXIS_BIAS) { + if (penetrationDepth < minPenetrationDepth) { minPenetrationDepth = penetrationDepth; isMinPenetrationFaceNormalPolyhedron1 = false; @@ -746,7 +763,6 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn separatingEdge1B = edge1B; separatingEdge2A = edge2A; separatingEdge2B = edge2B; - minEdgeVsEdgeSeparatingAxisPolyhedron2Space = separatingAxisPolyhedron2Space; } } } @@ -807,8 +823,10 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn else { normal = polyhedron1ToPolyhedron2.getOrientation() * ((polyhedron2ToPolyhedron1 * closestPointPolyhedron1Edge) - polyhedron1->getCentroid()); } - //Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * minEdgeVsEdgeSeparatingAxisPolyhedron2Space; - Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * normal.getUnit(); + const Vector3 unitNormal = normal.getUnit(); + assert(unitNormal.length() > decimal(0.7)); + Vector3 normalWorld = narrowPhaseInfo->shape2ToWorldTransform.getOrientation() * unitNormal; + assert(normalWorld.length() > decimal(0.7)); // Compute smooth triangle mesh contact if one of the two collision shapes is a triangle TriangleShape::computeSmoothTriangleMeshContact(narrowPhaseInfo->collisionShape1, narrowPhaseInfo->collisionShape2, @@ -816,6 +834,8 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn narrowPhaseInfo->shape1ToWorldTransform, narrowPhaseInfo->shape2ToWorldTransform, minPenetrationDepth, normalWorld); + assert(normalWorld.length() > decimal(0.7)); + // Create the contact point narrowPhaseInfo->addContactPoint(normalWorld, minPenetrationDepth, closestPointPolyhedron1EdgeLocalSpace, closestPointPolyhedron2Edge); diff --git a/src/collision/narrowphase/SAT/SATAlgorithm.h b/src/collision/narrowphase/SAT/SATAlgorithm.h index 5be84220..c7eb2695 100644 --- a/src/collision/narrowphase/SAT/SATAlgorithm.h +++ b/src/collision/narrowphase/SAT/SATAlgorithm.h @@ -55,10 +55,11 @@ class SATAlgorithm { // -------------------- Attributes -------------------- // - /// Bias used to make sure the SAT algorithm returns the same penetration axis between frames - /// when there are multiple separating axis with the same penetration depth. The goal is to - /// make sure the contact manifold does not change too much between frames. - static const decimal SAME_SEPARATING_AXIS_BIAS; + /// Relative and absolute bias used to make sure the SAT algorithm returns the same penetration axis between frames + /// when there are multiple separating axis with almost the same penetration depth. The goal is to + /// make sure the contact manifold does not change too much between frames for better stability. + static const decimal SEPARATING_AXIS_RELATIVE_TOLERANCE; + static const decimal SEPARATING_AXIS_ABSOLUTE_TOLERANCE; /// Memory allocator MemoryAllocator& mMemoryAllocator; From 528d7bfd450d93533378f527ceb2796a3efd07bd Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Mon, 28 Jan 2019 18:51:04 +0100 Subject: [PATCH 06/12] Fix issue in SAT algorithm --- src/collision/narrowphase/SAT/SATAlgorithm.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/collision/narrowphase/SAT/SATAlgorithm.cpp b/src/collision/narrowphase/SAT/SATAlgorithm.cpp index 2ca272fa..49c657c5 100644 --- a/src/collision/narrowphase/SAT/SATAlgorithm.cpp +++ b/src/collision/narrowphase/SAT/SATAlgorithm.cpp @@ -40,8 +40,8 @@ using namespace reactphysics3d; // Static variables initialization -const decimal SATAlgorithm::SEPARATING_AXIS_RELATIVE_TOLERANCE = decimal(1.02); -const decimal SATAlgorithm::SEPARATING_AXIS_ABSOLUTE_TOLERANCE = decimal(0.005); +const decimal SATAlgorithm::SEPARATING_AXIS_RELATIVE_TOLERANCE = decimal(1.002); +const decimal SATAlgorithm::SEPARATING_AXIS_ABSOLUTE_TOLERANCE = decimal(0.0005); // Constructor SATAlgorithm::SATAlgorithm(MemoryAllocator& memoryAllocator) : mMemoryAllocator(memoryAllocator) { @@ -698,7 +698,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn // We use penetration axis of polygon 1 isMinPenetrationFaceNormal = true; - minPenetrationDepth = penetrationDepth1; + minPenetrationDepth = std::min(penetrationDepth1, penetrationDepth2); minFaceIndex = faceIndex1; isMinPenetrationFaceNormalPolyhedron1 = true; } @@ -706,7 +706,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn // We use penetration axis of polygon 2 isMinPenetrationFaceNormal = true; - minPenetrationDepth = penetrationDepth2; + minPenetrationDepth = std::min(penetrationDepth1, penetrationDepth2); minFaceIndex = faceIndex2; isMinPenetrationFaceNormalPolyhedron1 = false; } From 117cbdafb7d60a52191887488ffd9795deaa02dd Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Tue, 12 Feb 2019 18:50:47 +0100 Subject: [PATCH 07/12] Fix issue with friction constraint in contact solver --- src/engine/ContactSolver.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/engine/ContactSolver.cpp b/src/engine/ContactSolver.cpp index 385cbe5e..6b67bba5 100644 --- a/src/engine/ContactSolver.cpp +++ b/src/engine/ContactSolver.cpp @@ -615,14 +615,16 @@ void ContactSolver::solve() { contactPointIndex++; } - // ------ First friction constraint at the center of the contact manifol ------ // + // ------ First friction constraint at the center of the contact manifold ------ // // Compute J*v // deltaV = v2 + w2.cross(mContactConstraints[c].r2Friction) - v1 - w1.cross(mContactConstraints[c].r1Friction); Vector3 deltaV(v2.x + w2.y * mContactConstraints[c].r2Friction.z - w2.z * mContactConstraints[c].r2Friction.y - v1.x - w1.y * mContactConstraints[c].r1Friction.z + w1.z * mContactConstraints[c].r1Friction.y, + v2.y + w2.z * mContactConstraints[c].r2Friction.x - w2.x * mContactConstraints[c].r2Friction.z - v1.y - w1.z * mContactConstraints[c].r1Friction.x + w1.x * mContactConstraints[c].r1Friction.z, + v2.z + w2.x * mContactConstraints[c].r2Friction.y - w2.y * mContactConstraints[c].r2Friction.x - v1.z - w1.x * mContactConstraints[c].r1Friction.y + w1.y * mContactConstraints[c].r1Friction.x); decimal Jv = deltaV.x * mContactConstraints[c].frictionVector1.x + @@ -649,6 +651,7 @@ void ContactSolver::solve() { mContactConstraints[c].r2CrossT1.y * deltaLambda, mContactConstraints[c].r2CrossT1.z * deltaLambda); + // Update the velocities of the body 1 by applying the impulse P mLinearVelocities[mContactConstraints[c].indexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.x; mLinearVelocities[mContactConstraints[c].indexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.y; @@ -663,15 +666,15 @@ void ContactSolver::solve() { mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; - // ------ Second friction constraint at the center of the contact manifol ----- // + // ------ Second friction constraint at the center of the contact manifold ----- // // Compute J*v //deltaV = v2 + w2.cross(mContactConstraints[c].r2Friction) - v1 - w1.cross(mContactConstraints[c].r1Friction); - deltaV.x = v2.x + w2.y * mContactConstraints[c].r2Friction.z - v2.z * mContactConstraints[c].r2Friction.y - v1.x - + deltaV.x = v2.x + w2.y * mContactConstraints[c].r2Friction.z - w2.z * mContactConstraints[c].r2Friction.y - v1.x - w1.y * mContactConstraints[c].r1Friction.z + w1.z * mContactConstraints[c].r1Friction.y; - deltaV.y = v2.y + w2.z * mContactConstraints[c].r2Friction.x - v2.x * mContactConstraints[c].r2Friction.z - v1.y - + deltaV.y = v2.y + w2.z * mContactConstraints[c].r2Friction.x - w2.x * mContactConstraints[c].r2Friction.z - v1.y - w1.z * mContactConstraints[c].r1Friction.x + w1.x * mContactConstraints[c].r1Friction.z; - deltaV.z = v2.z + w2.x * mContactConstraints[c].r2Friction.y - v2.y * mContactConstraints[c].r2Friction.x - v1.z - + deltaV.z = v2.z + w2.x * mContactConstraints[c].r2Friction.y - w2.y * mContactConstraints[c].r2Friction.x - v1.z - w1.x * mContactConstraints[c].r1Friction.y + w1.y * mContactConstraints[c].r1Friction.x; Jv = deltaV.x * mContactConstraints[c].frictionVector2.x + deltaV.y * mContactConstraints[c].frictionVector2.y + deltaV.z * mContactConstraints[c].frictionVector2.z; @@ -841,7 +844,7 @@ void ContactSolver::computeFrictionVectors(const Vector3& deltaVelocity, contact.frictionVector1 = contact.normal.getOneUnitOrthogonalVector(); } - // The second friction vector is computed by the cross product of the firs + // The second friction vector is computed by the cross product of the first // friction vector and the contact normal contact.frictionVector2 = contact.normal.cross(contact.frictionVector1).getUnit(); } From 22109d02ff10b8146b2b0087a66c5cad9a1017c6 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Mon, 18 Feb 2019 22:04:52 +0100 Subject: [PATCH 08/12] Update changelog file --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d30a13d..6a79e1cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Bug [#62](https://github.com/DanielChappuis/reactphysics3d/issues/62) has been fixed. - Bug [#63](https://github.com/DanielChappuis/reactphysics3d/issues/63) has been fixed. - Bug [#82](https://github.com/DanielChappuis/reactphysics3d/issues/82) has been fixed. + - Bug [#85](https://github.com/DanielChappuis/reactphysics3d/issues/85) has been fixed. - Bug: the free() method was called in PoolAllocator instead of release() method of base allocator. ## Version 0.7.0 (May 1, 2018) From 2ce0f8d76f28130307a2cfdf5af3ffd0514a8d37 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Fri, 22 Feb 2019 07:27:47 +0100 Subject: [PATCH 09/12] Make memory allocators thread-safe --- src/memory/DefaultAllocator.h | 14 ++++++++++++++ src/memory/DefaultPoolAllocator.cpp | 6 ++++++ src/memory/DefaultPoolAllocator.h | 4 ++++ src/memory/DefaultSingleFrameAllocator.cpp | 6 ++++++ src/memory/DefaultSingleFrameAllocator.h | 7 ++++++- 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/memory/DefaultAllocator.h b/src/memory/DefaultAllocator.h index 5ff94895..c8432438 100644 --- a/src/memory/DefaultAllocator.h +++ b/src/memory/DefaultAllocator.h @@ -29,6 +29,7 @@ // Libraries #include "memory/MemoryAllocator.h" #include +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -39,6 +40,11 @@ namespace reactphysics3d { */ class DefaultAllocator : public MemoryAllocator { + protected: + + /// Mutex + std::mutex mMutex; + public: /// Destructor @@ -50,11 +56,19 @@ class DefaultAllocator : public MemoryAllocator { /// Allocate memory of a given size (in bytes) and return a pointer to the /// allocated memory. virtual void* allocate(size_t size) override { + + // Lock the method with a mutex + std::lock_guard lock(mMutex); + return malloc(size); } /// Release previously allocated memory. virtual void release(void* pointer, size_t size) override { + + // Lock the method with a mutex + std::lock_guard lock(mMutex); + free(pointer); } }; diff --git a/src/memory/DefaultPoolAllocator.cpp b/src/memory/DefaultPoolAllocator.cpp index 39f84eee..fddcc2f8 100644 --- a/src/memory/DefaultPoolAllocator.cpp +++ b/src/memory/DefaultPoolAllocator.cpp @@ -100,6 +100,9 @@ DefaultPoolAllocator::~DefaultPoolAllocator() { // allocated memory. void* DefaultPoolAllocator::allocate(size_t size) { + // Lock the method with a mutex + std::lock_guard lock(mMutex); + // We cannot allocate zero bytes if (size == 0) return nullptr; @@ -173,6 +176,9 @@ void* DefaultPoolAllocator::allocate(size_t size) { // Release previously allocated memory. void DefaultPoolAllocator::release(void* pointer, size_t size) { + // Lock the method with a mutex + std::lock_guard lock(mMutex); + // Cannot release a 0-byte allocated memory if (size == 0) return; diff --git a/src/memory/DefaultPoolAllocator.h b/src/memory/DefaultPoolAllocator.h index 172a3c6c..8610da0c 100644 --- a/src/memory/DefaultPoolAllocator.h +++ b/src/memory/DefaultPoolAllocator.h @@ -29,6 +29,7 @@ // Libraries #include "configuration.h" #include "MemoryAllocator.h" +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -101,6 +102,9 @@ class DefaultPoolAllocator : public MemoryAllocator { /// True if the mMapSizeToHeapIndex array has already been initialized static bool isMapSizeToHeadIndexInitialized; + /// Mutex + std::mutex mMutex; + /// Pointers to the first free memory unit for each heap MemoryUnit* mFreeMemoryUnits[NB_HEAPS]; diff --git a/src/memory/DefaultSingleFrameAllocator.cpp b/src/memory/DefaultSingleFrameAllocator.cpp index de76ce57..3037488e 100644 --- a/src/memory/DefaultSingleFrameAllocator.cpp +++ b/src/memory/DefaultSingleFrameAllocator.cpp @@ -54,6 +54,9 @@ DefaultSingleFrameAllocator::~DefaultSingleFrameAllocator() { // allocated memory. void* DefaultSingleFrameAllocator::allocate(size_t size) { + // Lock the method with a mutex + std::lock_guard lock(mMutex); + // Check that there is enough remaining memory in the buffer if (mCurrentOffset + size > mTotalSizeBytes) { @@ -77,6 +80,9 @@ void* DefaultSingleFrameAllocator::allocate(size_t size) { // Release previously allocated memory. void DefaultSingleFrameAllocator::release(void* pointer, size_t size) { + // Lock the method with a mutex + std::lock_guard lock(mMutex); + // If allocated memory is not within the single frame allocation range char* p = static_cast(pointer); if (p < mMemoryBufferStart || p > mMemoryBufferStart + mTotalSizeBytes) { diff --git a/src/memory/DefaultSingleFrameAllocator.h b/src/memory/DefaultSingleFrameAllocator.h index 65641129..1ad98769 100644 --- a/src/memory/DefaultSingleFrameAllocator.h +++ b/src/memory/DefaultSingleFrameAllocator.h @@ -29,6 +29,7 @@ // Libraries #include "MemoryAllocator.h" #include "configuration.h" +#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -52,7 +53,11 @@ class DefaultSingleFrameAllocator : public SingleFrameAllocator { static const size_t INIT_SINGLE_FRAME_ALLOCATOR_NB_BYTES = 1048576; // 1Mb // -------------------- Attributes -------------------- // - /// Cached memory allocator used on construction + + /// Mutex + std::mutex mMutex; + + /// Cached memory allocator used on construction MemoryAllocator* mBaseMemoryAllocator; /// Total size (in bytes) of memory of the allocator From ca87fb624d78c7e84a55e22c228a6dbcf4e517fe Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Thu, 28 Feb 2019 17:25:37 +0100 Subject: [PATCH 10/12] Remove mutexes and do not use static pool and single frame memory allocators --- src/engine/CollisionWorld.h | 8 ++++++++ src/memory/DefaultAllocator.h | 10 ---------- src/memory/DefaultPoolAllocator.cpp | 6 ------ src/memory/DefaultPoolAllocator.h | 4 ---- src/memory/DefaultSingleFrameAllocator.cpp | 6 ------ src/memory/DefaultSingleFrameAllocator.h | 4 ---- src/memory/MemoryManager.cpp | 10 ++++++---- src/memory/MemoryManager.h | 14 +++++++------- 8 files changed, 21 insertions(+), 41 deletions(-) diff --git a/src/engine/CollisionWorld.h b/src/engine/CollisionWorld.h index 1d359a9c..d292c728 100644 --- a/src/engine/CollisionWorld.h +++ b/src/engine/CollisionWorld.h @@ -165,6 +165,9 @@ class CollisionWorld { /// Test and report collisions between all shapes of the world void testCollision(CollisionCallback* callback); + /// Return a reference to the memory manager of the world + MemoryManager& getMemoryManager(); + #ifdef IS_PROFILING_ACTIVE /// Return a reference to the profiler @@ -255,6 +258,11 @@ inline void CollisionWorld::testOverlap(CollisionBody* body, OverlapCallback* ov mCollisionDetection.testOverlap(body, overlapCallback, categoryMaskBits); } +// Return a reference to the memory manager of the world +inline MemoryManager& CollisionWorld::getMemoryManager() { + return mMemoryManager; +} + // Return the name of the world /** * @return Name of the world diff --git a/src/memory/DefaultAllocator.h b/src/memory/DefaultAllocator.h index c8432438..fcfb8e15 100644 --- a/src/memory/DefaultAllocator.h +++ b/src/memory/DefaultAllocator.h @@ -29,7 +29,6 @@ // Libraries #include "memory/MemoryAllocator.h" #include -#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -42,9 +41,6 @@ class DefaultAllocator : public MemoryAllocator { protected: - /// Mutex - std::mutex mMutex; - public: /// Destructor @@ -57,18 +53,12 @@ class DefaultAllocator : public MemoryAllocator { /// allocated memory. virtual void* allocate(size_t size) override { - // Lock the method with a mutex - std::lock_guard lock(mMutex); - return malloc(size); } /// Release previously allocated memory. virtual void release(void* pointer, size_t size) override { - // Lock the method with a mutex - std::lock_guard lock(mMutex); - free(pointer); } }; diff --git a/src/memory/DefaultPoolAllocator.cpp b/src/memory/DefaultPoolAllocator.cpp index fddcc2f8..39f84eee 100644 --- a/src/memory/DefaultPoolAllocator.cpp +++ b/src/memory/DefaultPoolAllocator.cpp @@ -100,9 +100,6 @@ DefaultPoolAllocator::~DefaultPoolAllocator() { // allocated memory. void* DefaultPoolAllocator::allocate(size_t size) { - // Lock the method with a mutex - std::lock_guard lock(mMutex); - // We cannot allocate zero bytes if (size == 0) return nullptr; @@ -176,9 +173,6 @@ void* DefaultPoolAllocator::allocate(size_t size) { // Release previously allocated memory. void DefaultPoolAllocator::release(void* pointer, size_t size) { - // Lock the method with a mutex - std::lock_guard lock(mMutex); - // Cannot release a 0-byte allocated memory if (size == 0) return; diff --git a/src/memory/DefaultPoolAllocator.h b/src/memory/DefaultPoolAllocator.h index 8610da0c..172a3c6c 100644 --- a/src/memory/DefaultPoolAllocator.h +++ b/src/memory/DefaultPoolAllocator.h @@ -29,7 +29,6 @@ // Libraries #include "configuration.h" #include "MemoryAllocator.h" -#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -102,9 +101,6 @@ class DefaultPoolAllocator : public MemoryAllocator { /// True if the mMapSizeToHeapIndex array has already been initialized static bool isMapSizeToHeadIndexInitialized; - /// Mutex - std::mutex mMutex; - /// Pointers to the first free memory unit for each heap MemoryUnit* mFreeMemoryUnits[NB_HEAPS]; diff --git a/src/memory/DefaultSingleFrameAllocator.cpp b/src/memory/DefaultSingleFrameAllocator.cpp index 3037488e..de76ce57 100644 --- a/src/memory/DefaultSingleFrameAllocator.cpp +++ b/src/memory/DefaultSingleFrameAllocator.cpp @@ -54,9 +54,6 @@ DefaultSingleFrameAllocator::~DefaultSingleFrameAllocator() { // allocated memory. void* DefaultSingleFrameAllocator::allocate(size_t size) { - // Lock the method with a mutex - std::lock_guard lock(mMutex); - // Check that there is enough remaining memory in the buffer if (mCurrentOffset + size > mTotalSizeBytes) { @@ -80,9 +77,6 @@ void* DefaultSingleFrameAllocator::allocate(size_t size) { // Release previously allocated memory. void DefaultSingleFrameAllocator::release(void* pointer, size_t size) { - // Lock the method with a mutex - std::lock_guard lock(mMutex); - // If allocated memory is not within the single frame allocation range char* p = static_cast(pointer); if (p < mMemoryBufferStart || p > mMemoryBufferStart + mTotalSizeBytes) { diff --git a/src/memory/DefaultSingleFrameAllocator.h b/src/memory/DefaultSingleFrameAllocator.h index 1ad98769..5e4c736a 100644 --- a/src/memory/DefaultSingleFrameAllocator.h +++ b/src/memory/DefaultSingleFrameAllocator.h @@ -29,7 +29,6 @@ // Libraries #include "MemoryAllocator.h" #include "configuration.h" -#include /// ReactPhysics3D namespace namespace reactphysics3d { @@ -54,9 +53,6 @@ class DefaultSingleFrameAllocator : public SingleFrameAllocator { // -------------------- Attributes -------------------- // - /// Mutex - std::mutex mMutex; - /// Cached memory allocator used on construction MemoryAllocator* mBaseMemoryAllocator; diff --git a/src/memory/MemoryManager.cpp b/src/memory/MemoryManager.cpp index 7427bcdd..e9954f55 100644 --- a/src/memory/MemoryManager.cpp +++ b/src/memory/MemoryManager.cpp @@ -30,9 +30,11 @@ using namespace reactphysics3d; // Static variables DefaultAllocator MemoryManager::mDefaultAllocator; -DefaultSingleFrameAllocator MemoryManager::mDefaultSingleFrameAllocator; -DefaultPoolAllocator MemoryManager::mDefaultPoolAllocator; MemoryAllocator* MemoryManager::mBaseAllocator = &mDefaultAllocator; -MemoryAllocator* MemoryManager::mPoolAllocator = &mDefaultPoolAllocator; -SingleFrameAllocator* MemoryManager::mSingleFrameAllocator = &mDefaultSingleFrameAllocator; +// Constructor +MemoryManager::MemoryManager() { + + mSingleFrameAllocator = &mDefaultSingleFrameAllocator; + mPoolAllocator = &mDefaultPoolAllocator; +} diff --git a/src/memory/MemoryManager.h b/src/memory/MemoryManager.h index b4269cbb..d0495543 100644 --- a/src/memory/MemoryManager.h +++ b/src/memory/MemoryManager.h @@ -51,19 +51,19 @@ class MemoryManager { static DefaultAllocator mDefaultAllocator; /// Default single frame memory allocator - static DefaultSingleFrameAllocator mDefaultSingleFrameAllocator; + DefaultSingleFrameAllocator mDefaultSingleFrameAllocator; /// Default pool memory allocator - static DefaultPoolAllocator mDefaultPoolAllocator; + DefaultPoolAllocator mDefaultPoolAllocator; /// Pointer to the base memory allocator to use static MemoryAllocator* mBaseAllocator; /// Single frame stack allocator - static SingleFrameAllocator* mSingleFrameAllocator; + SingleFrameAllocator* mSingleFrameAllocator; /// Memory pool allocator - static MemoryAllocator* mPoolAllocator; + MemoryAllocator* mPoolAllocator; public: @@ -75,7 +75,7 @@ class MemoryManager { }; /// Constructor - MemoryManager() = default; + MemoryManager(); /// Destructor ~MemoryManager() = default; @@ -99,10 +99,10 @@ class MemoryManager { static void setBaseAllocator(MemoryAllocator* memoryAllocator); /// Set the single frame memory allocator - static void setSingleFrameAllocator(SingleFrameAllocator* singleFrameAllocator); + void setSingleFrameAllocator(SingleFrameAllocator* singleFrameAllocator); /// Set the pool memory allocator - static void setPoolAllocator(MemoryAllocator* poolAllocator); + void setPoolAllocator(MemoryAllocator* poolAllocator); /// Reset the single frame allocator void resetFrameAllocator(); From 0e563f028389f2dbac346a5213d8f94154fe1c4d Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Fri, 1 Mar 2019 17:16:58 +0100 Subject: [PATCH 11/12] Update python script to generate new release --- GenerateNewVersion.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/GenerateNewVersion.py b/GenerateNewVersion.py index ec0c9e91..57c89194 100644 --- a/GenerateNewVersion.py +++ b/GenerateNewVersion.py @@ -19,7 +19,7 @@ def findReplaceText(directory, findRegex, substituteExpr, filePattern): # ----- Code ----- # -# Read new version number from user +# Read old version number from user oldVersion = raw_input("Enter the old version string: ") # Read new version number from user @@ -32,10 +32,14 @@ file.write(newVersion + "\n") file.close() print("Version number has been updated in VERSION file") -# Update the RP3D version number in the documentation/API/Doxyfile +# Update the RP3D version number in the documentation/API/Doxyfile file findReplaceText("documentation/API/", r'(PROJECT_NUMBER[ \t]+=[ \t]+)"[\d\.]+"', r'\g<1>"' + newVersion + '"', "Doxyfile") print("Version number has been updated in documentation/API/Doxyfile file") +# Update the RP3D version number in the documentation/UserManual/title.tex file +findReplaceText("documentation/UserManual/", r'(Version:[\s]+)[\d\.]+', r'\g<1>' + newVersion, "title.tex") +print("Version number has been updated in documentation/API/Doxyfile file") + # Update the RP3D version number in the src/configuration.h file findReplaceText("src/", r'(RP3D_VERSION[ \t]+=[ \t]+std::string\()"[\d\.]+"', r'\g<1>"' + newVersion + '"', "configuration.h") print("Version number has been updated in src/configuration.h file") @@ -48,4 +52,3 @@ print("Copyright date has been updated in LICENSE file") findReplaceText("src/", '(Copyright ' + re.escape("(c)") + r' 2010-)[\d]+', r'\g<1>' + str(date.today().year), "*.h") findReplaceText("src/", '(Copyright ' + re.escape("(c)") + r' 2010-)[\d]+', r'\g<1>' + str(date.today().year), "*.cpp") print("Copyright date in license has been updated in all source code files") - From 061469a539a728923281f0f51362af27c78023a7 Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Mon, 11 Mar 2019 11:12:42 +0100 Subject: [PATCH 12/12] Check that proxy-shape is part of broad-phase before updating it in RigidBody --- src/body/RigidBody.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/body/RigidBody.cpp b/src/body/RigidBody.cpp index 27fd75d5..6322c237 100644 --- a/src/body/RigidBody.cpp +++ b/src/body/RigidBody.cpp @@ -547,17 +547,21 @@ void RigidBody::updateBroadPhaseState() const { RP3D_PROFILE("RigidBody::updateBroadPhaseState()", mProfiler); DynamicsWorld& world = static_cast(mWorld); - const Vector3 displacement = world.mTimeStep * mLinearVelocity; + const Vector3 displacement = world.mTimeStep * mLinearVelocity; // For all the proxy collision shapes of the body for (ProxyShape* shape = mProxyCollisionShapes; shape != nullptr; shape = shape->mNext) { - // Recompute the world-space AABB of the collision shape - AABB aabb; - shape->getCollisionShape()->computeAABB(aabb, mTransform * shape->getLocalToBodyTransform()); + // If the proxy-shape shape is still part of the broad-phase + if (shape->getBroadPhaseId() != -1) { - // Update the broad-phase state for the proxy collision shape - mWorld.mCollisionDetection.updateProxyCollisionShape(shape, aabb, displacement); + // Recompute the world-space AABB of the collision shape + AABB aabb; + shape->getCollisionShape()->computeAABB(aabb, mTransform * shape->getLocalToBodyTransform()); + + // Update the broad-phase state for the proxy collision shape + mWorld.mCollisionDetection.updateProxyCollisionShape(shape, aabb, displacement); + } } }