Merge branch 'allocators' into develop

This commit is contained in:
Daniel Chappuis 2019-06-28 08:08:02 +02:00
commit cde8273f6a
15 changed files with 114 additions and 58 deletions

View File

@ -22,6 +22,9 @@
- 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 [#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)

View File

@ -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")

View File

@ -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.

View File

@ -547,17 +547,21 @@ void RigidBody::updateBroadPhaseState() const {
RP3D_PROFILE("RigidBody::updateBroadPhaseState()", mProfiler);
DynamicsWorld& world = static_cast<DynamicsWorld&>(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);
}
}
}

View File

@ -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();

View File

@ -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.002);
const decimal SATAlgorithm::SEPARATING_AXIS_ABSOLUTE_TOLERANCE = decimal(0.0005);
// 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 = std::min(penetrationDepth1, penetrationDepth2);
minFaceIndex = faceIndex1;
isMinPenetrationFaceNormalPolyhedron1 = true;
}
else {
// We use penetration axis of polygon 2
isMinPenetrationFaceNormal = true;
minPenetrationDepth = std::min(penetrationDepth1, 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);

View File

@ -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;

View File

@ -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

View File

@ -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();
}

View File

@ -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;

View File

@ -39,6 +39,8 @@ namespace reactphysics3d {
*/
class DefaultAllocator : public MemoryAllocator {
protected:
public:
/// Destructor
@ -50,11 +52,13 @@ 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 {
return malloc(size);
}
/// Release previously allocated memory.
virtual void release(void* pointer, size_t size) override {
free(pointer);
}
};

View File

@ -135,9 +135,9 @@ void* DefaultPoolAllocator::allocate(size_t size) {
MemoryBlock* currentMemoryBlocks = mMemoryBlocks;
mNbAllocatedMemoryBlocks += 64;
mMemoryBlocks = static_cast<MemoryBlock*>(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

View File

@ -52,7 +52,8 @@ class DefaultSingleFrameAllocator : public SingleFrameAllocator {
static const size_t INIT_SINGLE_FRAME_ALLOCATOR_NB_BYTES = 1048576; // 1Mb
// -------------------- Attributes -------------------- //
/// Cached memory allocator used on construction
/// Cached memory allocator used on construction
MemoryAllocator* mBaseMemoryAllocator;
/// Total size (in bytes) of memory of the allocator

View File

@ -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;
}

View File

@ -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();