diff --git a/CHANGELOG.md b/CHANGELOG.md index cedb3ff7..7ff8f1d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## Develop + +### Changed + + - The CollisionWorld::testCollision() methods do not have the 'categoryMaskBits' parameter anymore. + - The CollisionWorld::testOverlap() methods do not have the 'categoryMaskBits' parameter anymore. + - Many methods in the EventListener class have changed. Check the user manual for more information. + - The way to retrieve contacts from a CollisionCallbackInfo object has changed. Check the user manual for more information. + +### Removed + + - DynamicsWorld::getContactsList(). You need to use the EventListener class to retrieve contacts now. + ## Release Candidate ### Fixed @@ -25,6 +38,7 @@ - The methods CollisionBody::getProxyShapesList() has been remove. You can now use the CollisionBody::getNbProxyShapes() method to know the number of proxy-shapes of a body and the CollisionBody::getProxyShape(uint proxyShapeIndex) method to get a given proxy-shape of the body. + - The CollisionWorld::testAABBOverlap() methods have been removed. ## Version 0.7.0 (May 1, 2018) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63cc8a17..2057916f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,8 @@ SET (REACTPHYSICS3D_HEADERS "src/body/CollisionBody.h" "src/body/RigidBody.h" "src/collision/ContactPointInfo.h" + "src/collision/ContactManifoldInfo.h" + "src/collision/ContactPair.h" "src/collision/broadphase/DynamicAABBTree.h" "src/collision/narrowphase/CollisionDispatch.h" "src/collision/narrowphase/GJK/VoronoiSimplex.h" @@ -119,7 +121,6 @@ SET (REACTPHYSICS3D_HEADERS "src/collision/HalfEdgeStructure.h" "src/collision/CollisionDetection.h" "src/collision/ContactManifold.h" - "src/collision/ContactManifoldSet.h" "src/collision/MiddlePhaseTriangleCallback.h" "src/constraint/BallAndSocketJoint.h" "src/constraint/ContactPoint.h" @@ -135,6 +136,7 @@ SET (REACTPHYSICS3D_HEADERS "src/engine/DynamicsWorld.h" "src/engine/EventListener.h" "src/engine/Island.h" + "src/engine/Islands.h" "src/engine/Material.h" "src/engine/OverlappingPair.h" "src/engine/Timer.h" @@ -213,7 +215,6 @@ SET (REACTPHYSICS3D_SOURCES "src/collision/HalfEdgeStructure.cpp" "src/collision/CollisionDetection.cpp" "src/collision/ContactManifold.cpp" - "src/collision/ContactManifoldSet.cpp" "src/collision/MiddlePhaseTriangleCallback.cpp" "src/constraint/BallAndSocketJoint.cpp" "src/constraint/ContactPoint.cpp" @@ -238,6 +239,7 @@ SET (REACTPHYSICS3D_SOURCES "src/components/ProxyShapeComponents.cpp" "src/components/DynamicsComponents.cpp" "src/collision/CollisionCallback.cpp" + "src/collision/OverlapCallback.cpp" "src/mathematics/mathematics_functions.cpp" "src/mathematics/Matrix2x2.cpp" "src/mathematics/Matrix3x3.cpp" diff --git a/src/body/Body.cpp b/src/body/Body.cpp index c322c53e..1117572f 100644 --- a/src/body/Body.cpp +++ b/src/body/Body.cpp @@ -36,7 +36,7 @@ using namespace reactphysics3d; * @param id ID of the new body */ Body::Body(Entity entity, bodyindex id) - : mID(id), mEntity(entity), mIsAlreadyInIsland(false), mIsAllowedToSleep(true), mIsActive(true), + : mID(id), mEntity(entity), mIsAllowedToSleep(true), mIsActive(true), mIsSleeping(false), mSleepTime(0), mUserData(nullptr) { #ifdef IS_LOGGING_ACTIVE diff --git a/src/body/Body.h b/src/body/Body.h index bdee42c6..4c7513a5 100644 --- a/src/body/Body.h +++ b/src/body/Body.h @@ -57,9 +57,6 @@ class Body { /// Identifier of the entity in the ECS Entity mEntity; - /// True if the body has already been added in an island (for sleeping technique) - bool mIsAlreadyInIsland; - /// True if the body is allowed to go to sleep for better efficiency bool mIsAllowedToSleep; @@ -75,8 +72,10 @@ class Body { bool mIsActive; /// True if the body is sleeping (for sleeping technique) + // TODO : DELETE THIS bool mIsSleeping; + // TODO : Move this into the body components /// Elapsed time since the body velocity was bellow the sleep velocity decimal mSleepTime; @@ -141,6 +140,8 @@ class Body { void setLogger(Logger* logger); #endif + // TODO : Check if those operators are still used + /// Smaller than operator bool operator<(const Body& body2) const; diff --git a/src/body/CollisionBody.cpp b/src/body/CollisionBody.cpp index 5ff628e4..e7023f3a 100644 --- a/src/body/CollisionBody.cpp +++ b/src/body/CollisionBody.cpp @@ -40,8 +40,7 @@ using namespace reactphysics3d; * @param id ID of the body */ CollisionBody::CollisionBody(CollisionWorld& world, Entity entity, bodyindex id) - : Body(entity, id), mType(BodyType::DYNAMIC), - mContactManifoldsList(nullptr), mWorld(world) { + : Body(entity, id), mType(BodyType::DYNAMIC), mWorld(world) { #ifdef IS_PROFILING_ACTIVE mProfiler = nullptr; @@ -51,7 +50,7 @@ CollisionBody::CollisionBody(CollisionWorld& world, Entity entity, bodyindex id) // Destructor CollisionBody::~CollisionBody() { - assert(mContactManifoldsList == nullptr); + } // Add a collision shape to the body. Note that you can share a collision @@ -198,23 +197,6 @@ void CollisionBody::removeAllCollisionShapes() { } } -// Reset the contact manifold lists -void CollisionBody::resetContactManifoldsList() { - - // Delete the linked list of contact manifolds of that body - ContactManifoldListElement* currentElement = mContactManifoldsList; - while (currentElement != nullptr) { - ContactManifoldListElement* nextElement = currentElement->getNext(); - - // Delete the current element - currentElement->~ContactManifoldListElement(); - mWorld.mMemoryManager.release(MemoryManager::AllocationType::Pool, currentElement, sizeof(ContactManifoldListElement)); - - currentElement = nextElement; - } - mContactManifoldsList = nullptr; -} - // Return the current position and orientation /** * @return The current transformation of the body that transforms the local-space @@ -283,9 +265,6 @@ void CollisionBody::setIsActive(bool isActive) { mWorld.mCollisionDetection.removeProxyCollisionShape(proxyShape); } } - - // Reset the contact manifold list of the body - resetContactManifoldsList(); } RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body, @@ -307,26 +286,6 @@ void CollisionBody::askForBroadPhaseCollisionCheck() const { } } -// Reset the mIsAlreadyInIsland variable of the body and contact manifolds. -/// This method also returns the number of contact manifolds of the body. -int CollisionBody::resetIsAlreadyInIslandAndCountManifolds() { - - mIsAlreadyInIsland = false; - - int nbManifolds = 0; - - // Reset the mIsAlreadyInIsland variable of the contact manifolds for - // this body - ContactManifoldListElement* currentElement = mContactManifoldsList; - while (currentElement != nullptr) { - currentElement->getContactManifold()->mIsAlreadyInIsland = false; - currentElement = currentElement->getNext(); - nbManifolds++; - } - - return nbManifolds; -} - // Return true if a point is inside the collision body /// This method returns true if a point is inside any collision shape of the body /** diff --git a/src/body/CollisionBody.h b/src/body/CollisionBody.h index 018eb87e..152b3ec9 100644 --- a/src/body/CollisionBody.h +++ b/src/body/CollisionBody.h @@ -67,12 +67,10 @@ class CollisionBody : public Body { // -------------------- Attributes -------------------- // + // TODO : Move this into the dynamics components /// Type of body (static, kinematic or dynamic) BodyType mType; - /// First element of the linked list of contact manifolds involving this body - ContactManifoldListElement* mContactManifoldsList; - /// Reference to the world the body belongs to CollisionWorld& mWorld; @@ -85,9 +83,6 @@ class CollisionBody : public Body { // -------------------- Methods -------------------- // - /// Reset the contact manifold lists - void resetContactManifoldsList(); - /// Remove all the collision shapes void removeAllCollisionShapes(); @@ -98,9 +93,6 @@ class CollisionBody : public Body { /// (as if the body has moved). void askForBroadPhaseCollisionCheck() const; - /// Reset the mIsAlreadyInIsland variable of the body and contact manifolds - int resetIsAlreadyInIslandAndCountManifolds(); - /// Set the variable to know whether or not the body is sleeping virtual void setIsSleeping(bool isSleeping) override; @@ -142,9 +134,6 @@ class CollisionBody : public Body { /// Remove a collision shape from the body virtual void removeCollisionShape(ProxyShape *proxyShape); - /// Return the first element of the linked list of contact manifolds involving this body - const ContactManifoldListElement* getContactManifoldsList() const; - /// Return true if a point is inside the collision body bool testPointInside(const Vector3& worldPoint) const; @@ -203,15 +192,6 @@ inline BodyType CollisionBody::getType() const { return mType; } -// Return the first element of the linked list of contact manifolds involving this body -/** - * @return A pointer to the first element of the linked-list with the contact - * manifolds of this body - */ -inline const ContactManifoldListElement* CollisionBody::getContactManifoldsList() const { - return mContactManifoldsList; -} - /// Test if the collision body overlaps with a given AABB /** * @param worldAABB The AABB (in world-space coordinates) that will be used to test overlap diff --git a/src/body/RigidBody.cpp b/src/body/RigidBody.cpp index 393481d0..aed6977f 100644 --- a/src/body/RigidBody.cpp +++ b/src/body/RigidBody.cpp @@ -40,13 +40,11 @@ using namespace reactphysics3d; * @param id The ID of the body */ RigidBody::RigidBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id) - : CollisionBody(world, entity, id), mArrayIndex(0), mInitMass(decimal(1.0)), - mCenterOfMassLocal(0, 0, 0), mCenterOfMassWorld(transform.getPosition()), - mIsGravityEnabled(true), mMaterial(world.mConfig), mLinearDamping(decimal(0.0)), mAngularDamping(decimal(0.0)), - mJointsList(nullptr), mIsCenterOfMassSetByUser(false), mIsInertiaTensorSetByUser(false) { + : CollisionBody(world, entity, id), mMaterial(world.mConfig), mJointsList(nullptr), + mIsCenterOfMassSetByUser(false), mIsInertiaTensorSetByUser(false) { // Compute the inverse mass - mMassInverse = decimal(1.0) / mInitMass; + mWorld.mDynamicsComponents.setMassInverse(mEntity, decimal(1.0) / mWorld.mDynamicsComponents.getInitMass(mEntity)); // Update the world inverse inertia tensor updateInertiaTensorInverseWorld(); @@ -91,15 +89,15 @@ void RigidBody::setType(BodyType type) { if (mType == BodyType::STATIC || mType == BodyType::KINEMATIC) { // Reset the inverse mass and inverse inertia tensor to zero - mMassInverse = decimal(0.0); - mInertiaTensorLocalInverse.setToZero(); - mInertiaTensorInverseWorld.setToZero(); + mWorld.mDynamicsComponents.setMassInverse(mEntity, decimal(0)); + mWorld.mDynamicsComponents.setInverseInertiaTensorLocal(mEntity, Matrix3x3::zero()); + mWorld.mDynamicsComponents.setInverseInertiaTensorWorld(mEntity, Matrix3x3::zero()); } else { // If it is a dynamic body - mMassInverse = decimal(1.0) / mInitMass; + mWorld.mDynamicsComponents.setMassInverse(mEntity, decimal(1.0) / mWorld.mDynamicsComponents.getInitMass(mEntity)); if (mIsInertiaTensorSetByUser) { - mInertiaTensorLocalInverse = mUserInertiaTensorLocalInverse; + mWorld.mDynamicsComponents.setInverseInertiaTensorLocal(mEntity, mUserInertiaTensorLocalInverse); } } @@ -109,16 +107,73 @@ void RigidBody::setType(BodyType type) { // Awake the body setIsSleeping(false); - // Remove all the contacts with this body - resetContactManifoldsList(); - // Ask the broad-phase to test again the collision shapes of the body for collision // detection (as if the body has moved) askForBroadPhaseCollisionCheck(); // Reset the force and torque on the body - mExternalForce.setToZero(); - mExternalTorque.setToZero(); + mWorld.mDynamicsComponents.setExternalForce(mEntity, Vector3::zero()); + mWorld.mDynamicsComponents.setExternalTorque(mEntity, Vector3::zero()); +} + +// Get the inverse local inertia tensor of the body (in body coordinates) +const Matrix3x3& RigidBody::getInverseInertiaTensorLocal() const { + return mWorld.mDynamicsComponents.getInertiaTensorLocalInverse(mEntity); +} + +// Return the inverse of the inertia tensor in world coordinates. +/// The inertia tensor I_w in world coordinates is computed with the +/// local inverse inertia tensor I_b^-1 in body coordinates +/// by I_w = R * I_b^-1 * R^T +/// where R is the rotation matrix (and R^T its transpose) of the +/// current orientation quaternion of the body +/** + * @return The 3x3 inverse inertia tensor matrix of the body in world-space + * coordinates + */ +Matrix3x3 RigidBody::getInertiaTensorInverseWorld() const { + + // Compute and return the inertia tensor in world coordinates + return mWorld.mDynamicsComponents.getInertiaTensorWorldInverse(mEntity); +} + +// Method that return the mass of the body +/** + * @return The mass (in kilograms) of the body + */ +decimal RigidBody::getMass() const { + return mWorld.mDynamicsComponents.getInitMass(mEntity); +} + +// Apply an external force to the body at a given point (in world-space coordinates). +/// If the point is not at the center of mass of the body, it will also +/// generate some torque and therefore, change the angular velocity of the body. +/// If the body is sleeping, calling this method will wake it up. Note that the +/// force will we added to the sum of the applied forces and that this sum will be +/// reset to zero at the end of each call of the DynamicsWorld::update() method. +/// You can only apply a force to a dynamic body otherwise, this method will do nothing. +/** + * @param force The force to apply on the body + * @param point The point where the force is applied (in world-space coordinates) + */ +void RigidBody::applyForce(const Vector3& force, const Vector3& point) { + + // If it is not a dynamic body, we do nothing + if (mType != BodyType::DYNAMIC) return; + + // Awake the body if it was sleeping + if (mIsSleeping) { + setIsSleeping(false); + } + + // Add the force + const Vector3& externalForce = mWorld.mDynamicsComponents.getExternalForce(mEntity); + mWorld.mDynamicsComponents.setExternalForce(mEntity, externalForce + force); + + // Add the torque + const Vector3& externalTorque = mWorld.mDynamicsComponents.getExternalTorque(mEntity); + const Vector3& centerOfMassWorld = mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity); + mWorld.mDynamicsComponents.setExternalTorque(mEntity, externalTorque + (point - centerOfMassWorld).cross(force)); } // Set the local inertia tensor of the body (in local-space coordinates) @@ -136,7 +191,7 @@ void RigidBody::setInertiaTensorLocal(const Matrix3x3& inertiaTensorLocal) { if (mType != BodyType::DYNAMIC) return; // Compute the inverse local inertia tensor - mInertiaTensorLocalInverse = mUserInertiaTensorLocalInverse; + mWorld.mDynamicsComponents.setInverseInertiaTensorLocal(mEntity, mUserInertiaTensorLocalInverse); // Update the world inverse inertia tensor updateInertiaTensorInverseWorld(); @@ -145,6 +200,45 @@ void RigidBody::setInertiaTensorLocal(const Matrix3x3& inertiaTensorLocal) { "Body " + std::to_string(mID) + ": Set inertiaTensorLocal=" + inertiaTensorLocal.to_string()); } +// Apply an external force to the body at its center of mass. +/// If the body is sleeping, calling this method will wake it up. Note that the +/// force will we added to the sum of the applied forces and that this sum will be +/// reset to zero at the end of each call of the DynamicsWorld::update() method. +/// You can only apply a force to a dynamic body otherwise, this method will do nothing. +/** + * @param force The external force to apply on the center of mass of the body + */ +void RigidBody::applyForceToCenterOfMass(const Vector3& force) { + + // If it is not a dynamic body, we do nothing + if (mType != BodyType::DYNAMIC) return; + + // Awake the body if it was sleeping + if (mIsSleeping) { + setIsSleeping(false); + } + + // Add the force + const Vector3& externalForce = mWorld.mDynamicsComponents.getExternalForce(mEntity); + mWorld.mDynamicsComponents.setExternalForce(mEntity, externalForce + force); +} + +// Return the linear velocity damping factor +/** + * @return The linear damping factor of this body + */ +decimal RigidBody::getLinearDamping() const { + return mWorld.mDynamicsComponents.getLinearDamping(mEntity); +} + +// Return the angular velocity damping factor +/** + * @return The angular damping factor of this body + */ +decimal RigidBody::getAngularDamping() const { + return mWorld.mDynamicsComponents.getAngularDamping(mEntity); +} + // Set the inverse local inertia tensor of the body (in local-space coordinates) /// If the inverse inertia tensor is set with this method, it will not be computed /// using the collision shapes of the body. @@ -160,7 +254,7 @@ void RigidBody::setInverseInertiaTensorLocal(const Matrix3x3& inverseInertiaTens if (mType != BodyType::DYNAMIC) return; // Compute the inverse local inertia tensor - mInertiaTensorLocalInverse = mUserInertiaTensorLocalInverse; + mWorld.mDynamicsComponents.setInverseInertiaTensorLocal(mEntity, mUserInertiaTensorLocalInverse); // Update the world inverse inertia tensor updateInertiaTensorInverseWorld(); @@ -178,20 +272,24 @@ void RigidBody::setInverseInertiaTensorLocal(const Matrix3x3& inverseInertiaTens */ void RigidBody::setCenterOfMassLocal(const Vector3& centerOfMassLocal) { + // TODO : Check if we need to update the postion of the body here at the end (transform of the body) + if (mType != BodyType::DYNAMIC) return; mIsCenterOfMassSetByUser = true; - const Vector3 oldCenterOfMass = mCenterOfMassWorld; - mCenterOfMassLocal = centerOfMassLocal; + const Vector3 oldCenterOfMass = mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity); + mWorld.mDynamicsComponents.setCenterOfMassLocal(mEntity, centerOfMassLocal); // Compute the center of mass in world-space coordinates - mCenterOfMassWorld = mWorld.mTransformComponents.getTransform(mEntity) * mCenterOfMassLocal; + const Vector3& updatedCenterOfMassLocal = mWorld.mDynamicsComponents.getCenterOfMassLocal(mEntity); + mWorld.mDynamicsComponents.setCenterOfMassWorld(mEntity, mWorld.mTransformComponents.getTransform(mEntity) * updatedCenterOfMassLocal); // Update the linear velocity of the center of mass Vector3 linearVelocity = mWorld.mDynamicsComponents.getAngularVelocity(mEntity); const Vector3& angularVelocity = mWorld.mDynamicsComponents.getAngularVelocity(mEntity); - linearVelocity += angularVelocity.cross(mCenterOfMassWorld - oldCenterOfMass); + const Vector3& centerOfMassWorld = mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity); + linearVelocity += angularVelocity.cross(centerOfMassWorld - oldCenterOfMass); mWorld.mDynamicsComponents.setLinearVelocity(mEntity, linearVelocity); RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body, @@ -206,14 +304,14 @@ void RigidBody::setMass(decimal mass) { if (mType != BodyType::DYNAMIC) return; - mInitMass = mass; + mWorld.mDynamicsComponents.setInitMass(mEntity, mass); - if (mInitMass > decimal(0.0)) { - mMassInverse = decimal(1.0) / mInitMass; + if (mWorld.mDynamicsComponents.getInitMass(mEntity) > decimal(0.0)) { + mWorld.mDynamicsComponents.setMassInverse(mEntity, decimal(1.0) / mWorld.mDynamicsComponents.getInitMass(mEntity)); } else { - mInitMass = decimal(1.0); - mMassInverse = decimal(1.0); + mWorld.mDynamicsComponents.setInitMass(mEntity, decimal(1.0)); + mWorld.mDynamicsComponents.setMassInverse(mEntity, decimal(1.0)); } RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body, @@ -261,7 +359,8 @@ void RigidBody::updateInertiaTensorInverseWorld() { // TODO : Make sure we do this in a system Matrix3x3 orientation = mWorld.mTransformComponents.getTransform(mEntity).getOrientation().getMatrix(); - mInertiaTensorInverseWorld = orientation * mInertiaTensorLocalInverse * orientation.getTranspose(); + const Matrix3x3& inverseInertiaLocalTensor = mWorld.mDynamicsComponents.getInertiaTensorLocalInverse(mEntity); + mWorld.mDynamicsComponents.setInverseInertiaTensorWorld(mEntity, orientation * inverseInertiaLocalTensor * orientation.getTranspose()); } // Add a collision shape to the body. @@ -361,11 +460,11 @@ void RigidBody::removeCollisionShape(ProxyShape* proxyShape) { * @param isEnabled True if you want the gravity to be applied to this body */ void RigidBody::enableGravity(bool isEnabled) { - mIsGravityEnabled = isEnabled; + mWorld.mDynamicsComponents.setIsGravityEnabled(mEntity, isEnabled); RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(mID) + ": Set isGravityEnabled=" + - (mIsGravityEnabled ? "true" : "false")); + (isEnabled ? "true" : "false")); } // Set the linear damping factor. This is the ratio of the linear velocity @@ -375,10 +474,10 @@ void RigidBody::enableGravity(bool isEnabled) { */ void RigidBody::setLinearDamping(decimal linearDamping) { assert(linearDamping >= decimal(0.0)); - mLinearDamping = linearDamping; + mWorld.mDynamicsComponents.setLinearDamping(mEntity, linearDamping); RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body, - "Body " + std::to_string(mID) + ": Set linearDamping=" + std::to_string(mLinearDamping)); + "Body " + std::to_string(mID) + ": Set linearDamping=" + std::to_string(linearDamping)); } // Set the angular damping factor. This is the ratio of the angular velocity @@ -388,10 +487,10 @@ void RigidBody::setLinearDamping(decimal linearDamping) { */ void RigidBody::setAngularDamping(decimal angularDamping) { assert(angularDamping >= decimal(0.0)); - mAngularDamping = angularDamping; + mWorld.mDynamicsComponents.setAngularDamping(mEntity, angularDamping); RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body, - "Body " + std::to_string(mID) + ": Set angularDamping=" + std::to_string(mAngularDamping)); + "Body " + std::to_string(mID) + ": Set angularDamping=" + std::to_string(angularDamping)); } /// Update the transform of the body after a change of the center of mass @@ -401,7 +500,9 @@ void RigidBody::updateTransformWithCenterOfMass() { // Translate the body according to the translation of the center of mass position Transform& transform = mWorld.mTransformComponents.getTransform(mEntity); - transform.setPosition(mCenterOfMassWorld - transform.getOrientation() * mCenterOfMassLocal); + const Vector3& centerOfMassWorld = mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity); + const Vector3& centerOfMassLocal = mWorld.mDynamicsComponents.getCenterOfMassLocal(mEntity); + transform.setPosition(centerOfMassWorld - transform.getOrientation() * centerOfMassLocal); } // Set a new material for this rigid body @@ -466,15 +567,17 @@ void RigidBody::setAngularVelocity(const Vector3& angularVelocity) { */ void RigidBody::setTransform(const Transform& transform) { - const Vector3 oldCenterOfMass = mCenterOfMassWorld; + const Vector3 oldCenterOfMass = mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity); // Compute the new center of mass in world-space coordinates - mCenterOfMassWorld = transform * mCenterOfMassLocal; + const Vector3& centerOfMassLocal = mWorld.mDynamicsComponents.getCenterOfMassLocal(mEntity); + mWorld.mDynamicsComponents.setCenterOfMassWorld(mEntity, transform * centerOfMassLocal); // Update the linear velocity of the center of mass Vector3 linearVelocity = mWorld.mDynamicsComponents.getLinearVelocity(mEntity); const Vector3& angularVelocity = mWorld.mDynamicsComponents.getAngularVelocity(mEntity); - linearVelocity += angularVelocity.cross(mCenterOfMassWorld - oldCenterOfMass); + const Vector3& centerOfMassWorld = mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity); + linearVelocity += angularVelocity.cross(centerOfMassWorld - oldCenterOfMass); mWorld.mDynamicsComponents.setLinearVelocity(mEntity, linearVelocity); CollisionBody::setTransform(transform); @@ -490,11 +593,11 @@ void RigidBody::setTransform(const Transform& transform) { // the collision shapes attached to the body. void RigidBody::recomputeMassInformation() { - mInitMass = decimal(0.0); - mMassInverse = decimal(0.0); - if (!mIsInertiaTensorSetByUser) mInertiaTensorLocalInverse.setToZero(); - if (!mIsInertiaTensorSetByUser) mInertiaTensorInverseWorld.setToZero(); - if (!mIsCenterOfMassSetByUser) mCenterOfMassLocal.setToZero(); + mWorld.mDynamicsComponents.setInitMass(mEntity, decimal(0.0)); + mWorld.mDynamicsComponents.setMassInverse(mEntity, decimal(0.0)); + if (!mIsInertiaTensorSetByUser) mWorld.mDynamicsComponents.setInverseInertiaTensorLocal(mEntity, Matrix3x3::zero()); + if (!mIsInertiaTensorSetByUser) mWorld.mDynamicsComponents.setInverseInertiaTensorWorld(mEntity, Matrix3x3::zero()); + if (!mIsCenterOfMassSetByUser) mWorld.mDynamicsComponents.setCenterOfMassLocal(mEntity, Vector3::zero()); Matrix3x3 inertiaTensorLocal; inertiaTensorLocal.setToZero(); @@ -502,7 +605,7 @@ void RigidBody::recomputeMassInformation() { // If it is a STATIC or a KINEMATIC body if (mType == BodyType::STATIC || mType == BodyType::KINEMATIC) { - mCenterOfMassWorld = transform.getPosition(); + mWorld.mDynamicsComponents.setCenterOfMassWorld(mEntity, transform.getPosition()); return; } @@ -512,29 +615,30 @@ void RigidBody::recomputeMassInformation() { const List<Entity>& proxyShapesEntities = mWorld.mBodyComponents.getProxyShapes(mEntity); for (uint i=0; i < proxyShapesEntities.size(); i++) { ProxyShape* proxyShape = mWorld.mProxyShapesComponents.getProxyShape(proxyShapesEntities[i]); - mInitMass += proxyShape->getMass(); + mWorld.mDynamicsComponents.setInitMass(mEntity, mWorld.mDynamicsComponents.getInitMass(mEntity) + proxyShape->getMass()); if (!mIsCenterOfMassSetByUser) { - mCenterOfMassLocal += proxyShape->getLocalToBodyTransform().getPosition() * proxyShape->getMass(); + mWorld.mDynamicsComponents.setCenterOfMassLocal(mEntity, mWorld.mDynamicsComponents.getCenterOfMassLocal(mEntity) + + proxyShape->getLocalToBodyTransform().getPosition() * proxyShape->getMass()); } } - if (mInitMass > decimal(0.0)) { - mMassInverse = decimal(1.0) / mInitMass; + if (mWorld.mDynamicsComponents.getInitMass(mEntity) > decimal(0.0)) { + mWorld.mDynamicsComponents.setMassInverse(mEntity, decimal(1.0) / mWorld.mDynamicsComponents.getInitMass(mEntity)); } else { - mCenterOfMassWorld = transform.getPosition(); + mWorld.mDynamicsComponents.setCenterOfMassWorld(mEntity, transform.getPosition()); return; } // Compute the center of mass - const Vector3 oldCenterOfMass = mCenterOfMassWorld; + const Vector3 oldCenterOfMass = mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity); if (!mIsCenterOfMassSetByUser) { - mCenterOfMassLocal *= mMassInverse; + mWorld.mDynamicsComponents.setCenterOfMassLocal(mEntity, mWorld.mDynamicsComponents.getCenterOfMassLocal(mEntity) * mWorld.mDynamicsComponents.getMassInverse(mEntity)); } - mCenterOfMassWorld = transform * mCenterOfMassLocal; + mWorld.mDynamicsComponents.setCenterOfMassWorld(mEntity, transform * mWorld.mDynamicsComponents.getCenterOfMassLocal(mEntity)); if (!mIsInertiaTensorSetByUser) { @@ -555,7 +659,7 @@ void RigidBody::recomputeMassInformation() { // Use the parallel axis theorem to convert the inertia tensor w.r.t the collision shape // center into a inertia tensor w.r.t to the body origin. - Vector3 offset = shapeTransform.getPosition() - mCenterOfMassLocal; + Vector3 offset = shapeTransform.getPosition() - mWorld.mDynamicsComponents.getCenterOfMassLocal(mEntity); decimal offsetSquare = offset.lengthSquare(); Matrix3x3 offsetMatrix; offsetMatrix[0].setAllValues(offsetSquare, decimal(0.0), decimal(0.0)); @@ -570,7 +674,7 @@ void RigidBody::recomputeMassInformation() { } // Compute the local inverse inertia tensor - mInertiaTensorLocalInverse = inertiaTensorLocal.getInverse(); + mWorld.mDynamicsComponents.setInverseInertiaTensorLocal(mEntity, inertiaTensorLocal.getInverse()); } // Update the world inverse inertia tensor @@ -579,7 +683,7 @@ void RigidBody::recomputeMassInformation() { // Update the linear velocity of the center of mass Vector3 linearVelocity = mWorld.mDynamicsComponents.getLinearVelocity(mEntity); Vector3 angularVelocity = mWorld.mDynamicsComponents.getAngularVelocity(mEntity); - linearVelocity += angularVelocity.cross(mCenterOfMassWorld - oldCenterOfMass); + linearVelocity += angularVelocity.cross(mWorld.mDynamicsComponents.getCenterOfMassWorld(mEntity) - oldCenterOfMass); mWorld.mDynamicsComponents.setLinearVelocity(mEntity, linearVelocity); } @@ -599,16 +703,48 @@ Vector3 RigidBody::getAngularVelocity() const { return mWorld.mDynamicsComponents.getAngularVelocity(mEntity); } +// Return true if the gravity needs to be applied to this rigid body +/** + * @return True if the gravity is applied to the body + */ +bool RigidBody::isGravityEnabled() const { + return mWorld.mDynamicsComponents.getIsGravityEnabled(mEntity); +} + +// Apply an external torque to the body. +/// If the body is sleeping, calling this method will wake it up. Note that the +/// force will we added to the sum of the applied torques and that this sum will be +/// reset to zero at the end of each call of the DynamicsWorld::update() method. +/// You can only apply a force to a dynamic body otherwise, this method will do nothing. +/** + * @param torque The external torque to apply on the body + */ +void RigidBody::applyTorque(const Vector3& torque) { + + // If it is not a dynamic body, we do nothing + if (mType != BodyType::DYNAMIC) return; + + // Awake the body if it was sleeping + if (mIsSleeping) { + setIsSleeping(false); + } + + // Add the torque + const Vector3& externalTorque = mWorld.mDynamicsComponents.getExternalTorque(mEntity); + mWorld.mDynamicsComponents.setExternalTorque(mEntity, externalTorque + torque); +} + // Set the variable to know whether or not the body is sleeping void RigidBody::setIsSleeping(bool isSleeping) { CollisionBody::setIsSleeping(isSleeping); if (isSleeping) { + mWorld.mDynamicsComponents.setLinearVelocity(mEntity, Vector3::zero()); mWorld.mDynamicsComponents.setAngularVelocity(mEntity, Vector3::zero()); - mExternalForce.setToZero(); - mExternalTorque.setToZero(); + mWorld.mDynamicsComponents.setExternalForce(mEntity, Vector3::zero()); + mWorld.mDynamicsComponents.setExternalTorque(mEntity, Vector3::zero()); } } diff --git a/src/body/RigidBody.h b/src/body/RigidBody.h index 5946e990..1f8e16bb 100644 --- a/src/body/RigidBody.h +++ b/src/body/RigidBody.h @@ -50,62 +50,17 @@ class MemoryManager; */ class RigidBody : public CollisionBody { - private : - - /// Index of the body in arrays for contact/constraint solver - uint mArrayIndex; - protected : // -------------------- Attributes -------------------- // - /// Intial mass of the body - decimal mInitMass; - - /// Center of mass of the body in local-space coordinates. - /// The center of mass can therefore be different from the body origin - Vector3 mCenterOfMassLocal; - - /// Center of mass of the body in world-space coordinates - Vector3 mCenterOfMassWorld; - - /// Linear velocity of the body - //Vector3 mLinearVelocity; - - /// Angular velocity of the body - //Vector3 mAngularVelocity; - - /// Current external force on the body - Vector3 mExternalForce; - - /// Current external torque on the body - Vector3 mExternalTorque; - /// Inverse Local inertia tensor of the body (in local-space) set /// by the user with respect to the center of mass of the body Matrix3x3 mUserInertiaTensorLocalInverse; - /// Inverse of the inertia tensor of the body - Matrix3x3 mInertiaTensorLocalInverse; - - /// Inverse of the world inertia tensor of the body - Matrix3x3 mInertiaTensorInverseWorld; - - /// Inverse of the mass of the body - decimal mMassInverse; - - /// True if the gravity needs to be applied to this rigid body - bool mIsGravityEnabled; - /// Material properties of the rigid body Material mMaterial; - /// Linear velocity damping factor - decimal mLinearDamping; - - /// Angular velocity damping factor - decimal mAngularDamping; - /// First element of the linked list of joints involving this body JointListElement* mJointsList; @@ -252,43 +207,6 @@ class RigidBody : public CollisionBody { friend class FixedJoint; }; -// Method that return the mass of the body -/** - * @return The mass (in kilograms) of the body - */ -inline decimal RigidBody::getMass() const { - return mInitMass; -} - -// Get the inverse local inertia tensor of the body (in body coordinates) -inline const Matrix3x3& RigidBody::getInverseInertiaTensorLocal() const { - return mInertiaTensorLocalInverse; -} - -// Return the inverse of the inertia tensor in world coordinates. -/// The inertia tensor I_w in world coordinates is computed with the -/// local inverse inertia tensor I_b^-1 in body coordinates -/// by I_w = R * I_b^-1 * R^T -/// where R is the rotation matrix (and R^T its transpose) of the -/// current orientation quaternion of the body -/** - * @return The 3x3 inverse inertia tensor matrix of the body in world-space - * coordinates - */ -inline Matrix3x3 RigidBody::getInertiaTensorInverseWorld() const { - - // Compute and return the inertia tensor in world coordinates - return mInertiaTensorInverseWorld; -} - -// Return true if the gravity needs to be applied to this rigid body -/** - * @return True if the gravity is applied to the body - */ -inline bool RigidBody::isGravityEnabled() const { - return mIsGravityEnabled; -} - // Return a reference to the material properties of the rigid body /** * @return A reference to the material of the body @@ -297,22 +215,6 @@ inline Material& RigidBody::getMaterial() { return mMaterial; } -// Return the linear velocity damping factor -/** - * @return The linear damping factor of this body - */ -inline decimal RigidBody::getLinearDamping() const { - return mLinearDamping; -} - -// Return the angular velocity damping factor -/** - * @return The angular damping factor of this body - */ -inline decimal RigidBody::getAngularDamping() const { - return mAngularDamping; -} - // Return the first element of the linked list of joints involving this body /** * @return The first element of the linked-list of all the joints involving this body @@ -329,76 +231,6 @@ inline JointListElement* RigidBody::getJointsList() { return mJointsList; } -// Apply an external force to the body at its center of mass. -/// If the body is sleeping, calling this method will wake it up. Note that the -/// force will we added to the sum of the applied forces and that this sum will be -/// reset to zero at the end of each call of the DynamicsWorld::update() method. -/// You can only apply a force to a dynamic body otherwise, this method will do nothing. -/** - * @param force The external force to apply on the center of mass of the body - */ -inline void RigidBody::applyForceToCenterOfMass(const Vector3& force) { - - // If it is not a dynamic body, we do nothing - if (mType != BodyType::DYNAMIC) return; - - // Awake the body if it was sleeping - if (mIsSleeping) { - setIsSleeping(false); - } - - // Add the force - mExternalForce += force; -} - -// Apply an external force to the body at a given point (in world-space coordinates). -/// If the point is not at the center of mass of the body, it will also -/// generate some torque and therefore, change the angular velocity of the body. -/// If the body is sleeping, calling this method will wake it up. Note that the -/// force will we added to the sum of the applied forces and that this sum will be -/// reset to zero at the end of each call of the DynamicsWorld::update() method. -/// You can only apply a force to a dynamic body otherwise, this method will do nothing. -/** - * @param force The force to apply on the body - * @param point The point where the force is applied (in world-space coordinates) - */ -inline void RigidBody::applyForce(const Vector3& force, const Vector3& point) { - - // If it is not a dynamic body, we do nothing - if (mType != BodyType::DYNAMIC) return; - - // Awake the body if it was sleeping - if (mIsSleeping) { - setIsSleeping(false); - } - - // Add the force and torque - mExternalForce += force; - mExternalTorque += (point - mCenterOfMassWorld).cross(force); -} - -// Apply an external torque to the body. -/// If the body is sleeping, calling this method will wake it up. Note that the -/// force will we added to the sum of the applied torques and that this sum will be -/// reset to zero at the end of each call of the DynamicsWorld::update() method. -/// You can only apply a force to a dynamic body otherwise, this method will do nothing. -/** - * @param torque The external torque to apply on the body - */ -inline void RigidBody::applyTorque(const Vector3& torque) { - - // If it is not a dynamic body, we do nothing - if (mType != BodyType::DYNAMIC) return; - - // Awake the body if it was sleeping - if (mIsSleeping) { - setIsSleeping(false); - } - - // Add the torque - mExternalTorque += torque; -} - } #endif diff --git a/src/collision/CollisionCallback.cpp b/src/collision/CollisionCallback.cpp index e6ee5ba3..0162edfa 100644 --- a/src/collision/CollisionCallback.cpp +++ b/src/collision/CollisionCallback.cpp @@ -25,59 +25,71 @@ // Libraries #include "collision/CollisionCallback.h" -#include "engine/OverlappingPair.h" -#include "memory/MemoryAllocator.h" -#include "collision/ContactManifold.h" -#include "memory/MemoryManager.h" +#include "collision/ContactPair.h" +#include "constraint/ContactPoint.h" +#include "engine/CollisionWorld.h" // We want to use the ReactPhysics3D namespace using namespace reactphysics3d; // Constructor -CollisionCallback::CollisionCallbackInfo::CollisionCallbackInfo(OverlappingPair* pair, MemoryManager& memoryManager) : - contactManifoldElements(nullptr), body1(pair->getShape1()->getBody()), - body2(pair->getShape2()->getBody()), - proxyShape1(pair->getShape1()), proxyShape2(pair->getShape2()), - mMemoryManager(memoryManager) { +CollisionCallback::ContactPoint::ContactPoint(const reactphysics3d::ContactPoint& contactPoint) : mContactPoint(contactPoint) { - assert(pair != nullptr); - - const ContactManifoldSet& manifoldSet = pair->getContactManifoldSet(); - - // For each contact manifold in the set of manifolds in the pair - ContactManifold* contactManifold = manifoldSet.getContactManifolds(); - assert(contactManifold != nullptr); - while (contactManifold != nullptr) { - - assert(contactManifold->getNbContactPoints() > 0); - - // Add the contact manifold at the beginning of the linked - // list of contact manifolds of the first body - ContactManifoldListElement* element = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, - sizeof(ContactManifoldListElement))) - ContactManifoldListElement(contactManifold, - contactManifoldElements); - contactManifoldElements = element; - - contactManifold = contactManifold->getNext(); - } } -// Destructor -CollisionCallback::CollisionCallbackInfo::~CollisionCallbackInfo() { +// Contact Pair Constructor +CollisionCallback::ContactPair::ContactPair(const reactphysics3d::ContactPair& contactPair, + List<reactphysics3d::ContactPoint>* contactPoints, CollisionWorld& world) + :mContactPair(contactPair), mContactPoints(contactPoints), + mWorld(world) { - // Release memory allocator for the contact manifold list elements - ContactManifoldListElement* element = contactManifoldElements; - while (element != nullptr) { - - ContactManifoldListElement* nextElement = element->getNext(); - - // Delete and release memory - element->~ContactManifoldListElement(); - mMemoryManager.release(MemoryManager::AllocationType::Pool, element, - sizeof(ContactManifoldListElement)); - - element = nextElement; - } } +// Return a pointer to the first body in contact +CollisionBody* CollisionCallback::ContactPair::getBody1() const { + return static_cast<CollisionBody*>(mWorld.mBodyComponents.getBody(mContactPair.body1Entity)); +} + +// Return a pointer to the second body in contact +CollisionBody* CollisionCallback::ContactPair::getBody2() const { + return static_cast<CollisionBody*>(mWorld.mBodyComponents.getBody(mContactPair.body2Entity)); +} + +// Return a pointer to the first proxy-shape in contact (in body 1) +ProxyShape* CollisionCallback::ContactPair::getProxyShape1() const { + return mWorld.mProxyShapesComponents.getProxyShape(mContactPair.proxyShape1Entity); +} + +// Return a pointer to the second proxy-shape in contact (in body 1) +ProxyShape* CollisionCallback::ContactPair::getProxyShape2() const { + return mWorld.mProxyShapesComponents.getProxyShape(mContactPair.proxyShape2Entity); +} + +// CollisionCallbackInfo Constructor +CollisionCallback::CallbackData::CallbackData(List<reactphysics3d::ContactPair>* contactPairs, List<ContactManifold>* manifolds, + List<reactphysics3d::ContactPoint>* contactPoints, CollisionWorld& world) + :mContactPairs(contactPairs), mContactManifolds(manifolds), mContactPoints(contactPoints), mWorld(world) { + +} + +// Return a given contact point of the contact pair +/// Note that the returned ContactPoint object is only valid during the call of the CollisionCallback::onContact() +/// method. Therefore, you need to get contact data from it and make a copy. Do not make a copy of the ContactPoint +/// object itself because it won't be valid after the CollisionCallback::onContact() call. +CollisionCallback::ContactPoint CollisionCallback::ContactPair::getContactPoint(uint index) const { + + assert(index < getNbContactPoints()); + + return CollisionCallback::ContactPoint((*mContactPoints)[mContactPair.contactPointsIndex + index]); +} + +// Return a given contact pair +/// Note that the returned ContactPair object is only valid during the call of the CollisionCallback::onContact() +/// method. Therefore, you need to get contact data from it and make a copy. Do not make a copy of the ContactPair +/// object itself because it won't be valid after the CollisionCallback::onContact() call. +CollisionCallback::ContactPair CollisionCallback::CallbackData::getContactPair(uint index) const { + + assert(index < getNbContactPairs()); + + return CollisionCallback::ContactPair((*mContactPairs)[index], mContactPoints, mWorld); +} diff --git a/src/collision/CollisionCallback.h b/src/collision/CollisionCallback.h index bd9e7703..87e416ff 100644 --- a/src/collision/CollisionCallback.h +++ b/src/collision/CollisionCallback.h @@ -26,6 +26,11 @@ #ifndef REACTPHYSICS3D_COLLISION_CALLBACK_H #define REACTPHYSICS3D_COLLISION_CALLBACK_H +// Libraries +#include "containers/List.h" +#include "collision/ContactPair.h" +#include "constraint/ContactPoint.h" + /// ReactPhysics3D namespace namespace reactphysics3d { @@ -36,10 +41,11 @@ struct ContactManifoldListElement; class CollisionBody; class ProxyShape; class MemoryManager; +class CollisionCallbackInfo; // Class CollisionCallback /** - * This class can be used to register a callback for collision test queries. + * This abstract class can be used to register a callback for collision test queries. * You should implement your own class inherited from this one and implement * the notifyContact() method. This method will called each time a contact * point is reported. @@ -48,47 +54,221 @@ class CollisionCallback { public: - // Structure CollisionCallbackInfo + // Class ContactPoint /** - * This structure represents the contact information between two collision - * shapes of two bodies + * This class represents a contact point between two bodies of the physics world. */ - struct CollisionCallbackInfo { + class ContactPoint { + + private: + + // -------------------- Attributes -------------------- // + + const reactphysics3d::ContactPoint& mContactPoint; + + // -------------------- Methods -------------------- // + + /// Constructor + ContactPoint(const reactphysics3d::ContactPoint& contactPoint); public: - /// Pointer to the first element of the linked-list that contains contact manifolds - ContactManifoldListElement* contactManifoldElements; + // -------------------- Methods -------------------- // - /// Pointer to the first body of the contact - CollisionBody* body1; + /// Copy constructor + ContactPoint(const ContactPoint& contactPoint) = default; - /// Pointer to the second body of the contact - CollisionBody* body2; + /// Assignment operator + ContactPoint& operator=(const ContactPoint& contactPoint) = default; - /// Pointer to the proxy shape of first body - const ProxyShape* proxyShape1; + /// Destructor + ~ContactPoint() = default; - /// Pointer to the proxy shape of second body - const ProxyShape* proxyShape2; + /// Return the penetration depth + decimal getPenetrationDepth() const; - /// Memory manager - MemoryManager& mMemoryManager; + /// Return the world-space contact normal + const Vector3& getWorldNormal() const; - // Constructor - CollisionCallbackInfo(OverlappingPair* pair, MemoryManager& memoryManager); + /// Return the contact point on the first proxy shape in the local-space of the first proxy shape + const Vector3& getLocalPointOnShape1() const; - // Destructor - ~CollisionCallbackInfo(); + /// Return the contact point on the second proxy shape in the local-space of the second proxy shape + const Vector3& getLocalPointOnShape2() const; + + // -------------------- Friendship -------------------- // + + friend class CollisionCallback; + }; + + // Class ContactPair + /** + * This class represents the contact between two bodies of the physics world. + * A contact pair contains a list of contact points. + */ + class ContactPair { + + private: + + // -------------------- Attributes -------------------- // + + const reactphysics3d::ContactPair& mContactPair; + + /// Pointer to the contact points + List<reactphysics3d::ContactPoint>* mContactPoints; + + /// Reference to the physics world + CollisionWorld& mWorld; + + // -------------------- Methods -------------------- // + + /// Constructor + ContactPair(const reactphysics3d::ContactPair& contactPair, List<reactphysics3d::ContactPoint>* contactPoints, + CollisionWorld& world); + + public: + + // -------------------- Methods -------------------- // + + /// Copy constructor + ContactPair(const ContactPair& contactPair) = default; + + /// Assignment operator + ContactPair& operator=(const ContactPair& contactPair) = default; + + /// Destructor + ~ContactPair() = default; + + /// Return the number of contact points in the contact pair + uint getNbContactPoints() const; + + /// Return a given contact point + ContactPoint getContactPoint(uint index) const; + + /// Return a pointer to the first body in contact + CollisionBody* getBody1() const; + + /// Return a pointer to the second body in contact + CollisionBody* getBody2() const; + + /// Return a pointer to the first proxy-shape in contact (in body 1) + ProxyShape* getProxyShape1() const; + + /// Return a pointer to the second proxy-shape in contact (in body 2) + ProxyShape* getProxyShape2() const; + + // -------------------- Friendship -------------------- // + + friend class CollisionCallback; + }; + + // Class CallbackData + /** + * This class contains data about contacts between bodies + */ + class CallbackData { + + private: + + // -------------------- Attributes -------------------- // + + /// Pointer to the list of contact pairs + List<reactphysics3d::ContactPair>* mContactPairs; + + /// Pointer to the list of contact manifolds + List<ContactManifold>* mContactManifolds; + + /// Pointer to the contact points + List<reactphysics3d::ContactPoint>* mContactPoints; + + /// Reference to the physics world + CollisionWorld& mWorld; + + // -------------------- Methods -------------------- // + + /// Constructor + CallbackData(List<reactphysics3d::ContactPair>* contactPairs, List<ContactManifold>* manifolds, + List<reactphysics3d::ContactPoint>* contactPoints, CollisionWorld& world); + + /// Deleted copy constructor + CallbackData(const CallbackData& callbackData) = delete; + + /// Deleted assignment operator + CallbackData& operator=(const CallbackData& callbackData) = delete; + + /// Destructor + ~CallbackData() = default; + + public: + + // -------------------- Methods -------------------- // + + /// Return the number of contact pairs + uint getNbContactPairs() const; + + /// Return a given contact pair + ContactPair getContactPair(uint index) const; + + // -------------------- Friendship -------------------- // + + friend class CollisionDetection; }; /// Destructor virtual ~CollisionCallback() = default; - /// This method will be called for each reported contact point - virtual void notifyContact(const CollisionCallbackInfo& collisionCallbackInfo)=0; + /// This method is called when some contacts occur + virtual void onContact(const CallbackData& callbackData)=0; }; +// Return the number of contact pairs (there is a single contact pair between two bodies in contact) +/** + * @return The number of contact pairs + */ +inline uint CollisionCallback::CallbackData::getNbContactPairs() const { + return mContactPairs->size(); +} + +// Return the number of contact points in the contact pair +/** + * @return The number of contact points + */ +inline uint CollisionCallback::ContactPair::getNbContactPoints() const { + return mContactPair.nbToTalContactPoints; +} + +// Return the penetration depth between the two bodies in contact +/** + * @return The penetration depth (larger than zero) + */ +inline decimal CollisionCallback::ContactPoint::getPenetrationDepth() const { + return mContactPoint.getPenetrationDepth(); +} + +// Return the world-space contact normal (vector from first body toward second body) +/** + * @return The contact normal direction at the contact point (in world-space) + */ +inline const Vector3& CollisionCallback::ContactPoint::getWorldNormal() const { + return mContactPoint.getNormal(); +} + +// Return the contact point on the first proxy shape in the local-space of the first proxy shape +/** + * @return The contact point in the local-space of the first proxy-shape (from body1) in contact + */ +inline const Vector3& CollisionCallback::ContactPoint::getLocalPointOnShape1() const { + return mContactPoint.getLocalPointOnShape1(); +} + +// Return the contact point on the second proxy shape in the local-space of the second proxy shape +/** + * @return The contact point in the local-space of the second proxy-shape (from body2) in contact + */ +inline const Vector3& CollisionCallback::ContactPoint::getLocalPointOnShape2() const { + return mContactPoint.getLocalPointOnShape2(); +} + } #endif diff --git a/src/collision/CollisionDetection.cpp b/src/collision/CollisionDetection.cpp index 6738cae0..a1647ca9 100644 --- a/src/collision/CollisionDetection.cpp +++ b/src/collision/CollisionDetection.cpp @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * * Copyright (c) 2010-2018 Daniel Chappuis * ********************************************************************************* @@ -30,6 +30,8 @@ #include "body/Body.h" #include "collision/shapes/BoxShape.h" #include "collision/shapes/ConcaveShape.h" +#include "collision/ContactManifoldInfo.h" +#include "constraint/ContactPoint.h" #include "body/RigidBody.h" #include "configuration.h" #include "collision/CollisionCallback.h" @@ -40,7 +42,10 @@ #include "utils/Profiler.h" #include "engine/EventListener.h" #include "collision/RaycastInfo.h" +#include "engine/Islands.h" +#include "containers/Pair.h" #include <cassert> +#include <iostream> // We want to use the ReactPhysics3D namespace using namespace reactphysics3d; @@ -50,12 +55,23 @@ using namespace std; // Constructor CollisionDetection::CollisionDetection(CollisionWorld* world, ProxyShapeComponents& proxyShapesComponents, TransformComponents& transformComponents, DynamicsComponents& dynamicsComponents, MemoryManager& memoryManager) - : mMemoryManager(memoryManager), mProxyShapesComponents(proxyShapesComponents), mTransformComponents(transformComponents), + : mMemoryManager(memoryManager), mProxyShapesComponents(proxyShapesComponents), mCollisionDispatch(mMemoryManager.getPoolAllocator()), mWorld(world), mOverlappingPairs(mMemoryManager.getPoolAllocator()), - mBroadPhaseSystem(*this, mProxyShapesComponents, mTransformComponents, dynamicsComponents), + mBroadPhaseSystem(*this, mProxyShapesComponents, transformComponents, dynamicsComponents), mNoCollisionPairs(mMemoryManager.getPoolAllocator()), mMapBroadPhaseIdToProxyShapeEntity(memoryManager.getPoolAllocator()), - mNarrowPhaseInput(mMemoryManager.getSingleFrameAllocator()) { + mNarrowPhaseInput(mMemoryManager.getSingleFrameAllocator()), mPotentialContactPoints(mMemoryManager.getSingleFrameAllocator()), + // TODO : We should probably use single frame allocator for mPotentialContactPoints, mPotentialContactManifolds, mMapPairIdToOverlappingPairContacts + mPotentialContactManifolds(mMemoryManager.getSingleFrameAllocator()), mContactPairs1(mMemoryManager.getPoolAllocator()), + mContactPairs2(mMemoryManager.getPoolAllocator()), mPreviousContactPairs(&mContactPairs1), mCurrentContactPairs(&mContactPairs2), + mMapPairIdToContactPairIndex1(mMemoryManager.getPoolAllocator()), mMapPairIdToContactPairIndex2(mMemoryManager.getPoolAllocator()), + mPreviousMapPairIdToContactPairIndex(&mMapPairIdToContactPairIndex1), mCurrentMapPairIdToContactPairIndex(&mMapPairIdToContactPairIndex2), + mContactPairsIndicesOrderingForContacts(memoryManager.getSingleFrameAllocator()), + mContactManifolds1(mMemoryManager.getPoolAllocator()), mContactManifolds2(mMemoryManager.getPoolAllocator()), + mPreviousContactManifolds(&mContactManifolds1), mCurrentContactManifolds(&mContactManifolds2), + mContactPoints1(mMemoryManager.getPoolAllocator()), + mContactPoints2(mMemoryManager.getPoolAllocator()), mPreviousContactPoints(&mContactPoints1), + mCurrentContactPoints(&mContactPoints2), mMapBodyToContactPairs(mMemoryManager.getSingleFrameAllocator()) { #ifdef IS_PROFILING_ACTIVE @@ -75,7 +91,7 @@ void CollisionDetection::computeCollisionDetection() { computeBroadPhase(); // Compute the middle-phase collision detection - computeMiddlePhase(); + computeMiddlePhase(mOverlappingPairs, mNarrowPhaseInput); // Compute the narrow-phase collision detection computeNarrowPhase(); @@ -94,12 +110,40 @@ void CollisionDetection::computeBroadPhase() { // Create new overlapping pairs if necessary updateOverlappingPairs(overlappingNodes); + + // Remove non overlapping pairs + removeNonOverlappingPairs(); +} + +// Remove pairs that are not overlapping anymore +void CollisionDetection::removeNonOverlappingPairs() { + + // For each possible collision pair of bodies + for (auto it = mOverlappingPairs.begin(); it != mOverlappingPairs.end(); ) { + + OverlappingPair* pair = it->second; + + ProxyShape* shape1 = pair->getShape1(); + ProxyShape* shape2 = pair->getShape2(); + + // Check if the two shapes are still overlapping. Otherwise, we destroy the overlapping pair + if (!mBroadPhaseSystem.testOverlappingShapes(shape1, shape2)) { + + // Destroy the overlapping pair + pair->~OverlappingPair(); + + mWorld->mMemoryManager.release(MemoryManager::AllocationType::Pool, pair, sizeof(OverlappingPair)); + it = mOverlappingPairs.remove(it); + continue; + } + else { + ++it; + } + } } // Take a list of overlapping nodes in the broad-phase and create new overlapping pairs if necessary -void CollisionDetection::updateOverlappingPairs(List<Pair<int, int>>& overlappingNodes) { - - List<OverlappingPair*> newOverlappingPairs(mMemoryManager.getPoolAllocator(), overlappingNodes.size()); +void CollisionDetection::updateOverlappingPairs(const List<Pair<int, int>>& overlappingNodes) { // For each overlapping pair of nodes for (uint i=0; i < overlappingNodes.size(); i++) { @@ -151,73 +195,52 @@ void CollisionDetection::updateOverlappingPairs(List<Pair<int, int>>& overlappin // Add the new overlapping pair mOverlappingPairs.add(Pair<Pair<uint, uint>, OverlappingPair*>(pairID, newPair)); - newOverlappingPairs.add(newPair); } } } } } - - // For each new overlapping pair - for (uint i=0; i < newOverlappingPairs.size(); i++) { - - // Wake up the two bodies of the new overlapping pair - mWorld->notifyBodyDisabled(newOverlappingPairs[i]->getShape1()->getBody()->getEntity(), false); - mWorld->notifyBodyDisabled(newOverlappingPairs[i]->getShape1()->getBody()->getEntity(), false); - } } // Compute the middle-phase collision detection -void CollisionDetection::computeMiddlePhase() { +void CollisionDetection::computeMiddlePhase(OverlappingPairMap& overlappingPairs, NarrowPhaseInput& narrowPhaseInput) { RP3D_PROFILE("CollisionDetection::computeMiddlePhase()", mProfiler); // Reserve memory for the narrow-phase input using cached capacity from previous frame - mNarrowPhaseInput.reserveMemory(); + narrowPhaseInput.reserveMemory(); // For each possible collision pair of bodies - for (auto it = mOverlappingPairs.begin(); it != mOverlappingPairs.end(); ) { + for (auto it = overlappingPairs.begin(); it != overlappingPairs.end(); ++it) { OverlappingPair* pair = it->second; - // Make all the contact manifolds and contact points of the pair obsolete - pair->makeContactsObsolete(); - // Make all the last frame collision info obsolete pair->makeLastFrameCollisionInfosObsolete(); + const Entity proxyShape1Entity = pair->getShape1()->getEntity(); + const Entity proxyShape2Entity = pair->getShape2()->getEntity(); + ProxyShape* shape1 = pair->getShape1(); ProxyShape* shape2 = pair->getShape2(); - assert(shape1->getBroadPhaseId() != -1); - assert(shape2->getBroadPhaseId() != -1); - assert(shape1->getBroadPhaseId() != shape2->getBroadPhaseId()); - - // Check if the two shapes are still overlapping. Otherwise, we destroy the - // overlapping pair - if (!mBroadPhaseSystem.testOverlappingShapes(shape1, shape2)) { - - // Destroy the overlapping pair - pair->~OverlappingPair(); - - mWorld->mMemoryManager.release(MemoryManager::AllocationType::Pool, pair, sizeof(OverlappingPair)); - it = mOverlappingPairs.remove(it); - continue; - } - else { - ++it; - } + assert(mProxyShapesComponents.getBroadPhaseId(proxyShape1Entity) != -1); + assert(mProxyShapesComponents.getBroadPhaseId(proxyShape2Entity) != -1); + assert(mProxyShapesComponents.getBroadPhaseId(proxyShape1Entity) != mProxyShapesComponents.getBroadPhaseId(proxyShape2Entity)); // Check if the collision filtering allows collision between the two shapes - if (((shape1->getCollideWithMaskBits() & shape2->getCollisionCategoryBits()) != 0 && - (shape1->getCollisionCategoryBits() & shape2->getCollideWithMaskBits()) != 0)) { + if ((mProxyShapesComponents.getCollideWithMaskBits(proxyShape1Entity) & mProxyShapesComponents.getCollisionCategoryBits(proxyShape2Entity)) != 0 && + (mProxyShapesComponents.getCollisionCategoryBits(proxyShape1Entity) & mProxyShapesComponents.getCollideWithMaskBits(proxyShape2Entity)) != 0) { CollisionBody* const body1 = shape1->getBody(); CollisionBody* const body2 = shape2->getBody(); - // Check that at least one body is awake and not static - bool isBody1Active = !body1->isSleeping() && body1->getType() != BodyType::STATIC; - bool isBody2Active = !body2->isSleeping() && body2->getType() != BodyType::STATIC; + const Entity body1Entity = body1->getEntity(); + const Entity body2Entity = body2->getEntity(); + + // Check that at least one body is enabled (active and awake) and not static + bool isBody1Active = !mWorld->mBodyComponents.getIsEntityDisabled(body1Entity) && body1->getType() != BodyType::STATIC; + bool isBody2Active = !mWorld->mBodyComponents.getIsEntityDisabled(body2Entity) && body2->getType() != BodyType::STATIC; if (!isBody1Active && !isBody2Active) continue; // Check if the bodies are in the set of bodies that cannot collide between each other @@ -236,7 +259,7 @@ void CollisionDetection::computeMiddlePhase() { // No middle-phase is necessary, simply create a narrow phase info // for the narrow-phase collision detection - mNarrowPhaseInput.addNarrowPhaseTest(pair, shape1->getCollisionShape(), shape2->getCollisionShape(), + narrowPhaseInput.addNarrowPhaseTest(pair, shape1->getCollisionShape(), shape2->getCollisionShape(), shape1->getLocalToWorldTransform(), shape2->getLocalToWorldTransform(), algorithmType, mMemoryManager.getSingleFrameAllocator()); @@ -244,7 +267,7 @@ void CollisionDetection::computeMiddlePhase() { // Concave vs Convex algorithm else if ((!isShape1Convex && isShape2Convex) || (!isShape2Convex && isShape1Convex)) { - computeConvexVsConcaveMiddlePhase(pair, mMemoryManager.getSingleFrameAllocator(), mNarrowPhaseInput); + computeConvexVsConcaveMiddlePhase(pair, mMemoryManager.getSingleFrameAllocator(), narrowPhaseInput); } // Concave vs Concave shape else { @@ -312,8 +335,8 @@ void CollisionDetection::computeConvexVsConcaveMiddlePhase(OverlappingPair* pair } // Execute the narrow-phase collision detection algorithm on batches -bool CollisionDetection::testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool stopFirstContactFound, - bool reportContacts, MemoryAllocator& allocator) { +bool CollisionDetection::testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool reportContacts, + bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& allocator) { bool contactFound = false; @@ -335,35 +358,37 @@ bool CollisionDetection::testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseI // Compute the narrow-phase collision detection for each kind of collision shapes if (sphereVsSphereBatch.getNbObjects() > 0) { - contactFound |= sphereVsSphereAlgo->testCollision(sphereVsSphereBatch, 0, sphereVsSphereBatch.getNbObjects(), reportContacts, stopFirstContactFound, allocator); - if (stopFirstContactFound && contactFound) return true; + contactFound |= sphereVsSphereAlgo->testCollision(sphereVsSphereBatch, 0, sphereVsSphereBatch.getNbObjects(), reportContacts, allocator); } if (sphereVsCapsuleBatch.getNbObjects() > 0) { - contactFound |= sphereVsCapsuleAlgo->testCollision(sphereVsCapsuleBatch, 0, sphereVsCapsuleBatch.getNbObjects(), reportContacts, stopFirstContactFound, allocator); - if (stopFirstContactFound && contactFound) return true; + contactFound |= sphereVsCapsuleAlgo->testCollision(sphereVsCapsuleBatch, 0, sphereVsCapsuleBatch.getNbObjects(), reportContacts, allocator); } if (capsuleVsCapsuleBatch.getNbObjects() > 0) { - contactFound |= capsuleVsCapsuleAlgo->testCollision(capsuleVsCapsuleBatch, 0, capsuleVsCapsuleBatch.getNbObjects(), reportContacts, stopFirstContactFound, allocator); - if (stopFirstContactFound && contactFound) return true; + contactFound |= capsuleVsCapsuleAlgo->testCollision(capsuleVsCapsuleBatch, 0, capsuleVsCapsuleBatch.getNbObjects(), reportContacts, allocator); } if (sphereVsConvexPolyhedronBatch.getNbObjects() > 0) { - contactFound |= sphereVsConvexPolyAlgo->testCollision(sphereVsConvexPolyhedronBatch, 0, sphereVsConvexPolyhedronBatch.getNbObjects(), reportContacts, stopFirstContactFound, allocator); - if (stopFirstContactFound && contactFound) return true; + contactFound |= sphereVsConvexPolyAlgo->testCollision(sphereVsConvexPolyhedronBatch, 0, sphereVsConvexPolyhedronBatch.getNbObjects(), reportContacts, clipWithPreviousAxisIfStillColliding, allocator); } if (capsuleVsConvexPolyhedronBatch.getNbObjects() > 0) { - contactFound |= capsuleVsConvexPolyAlgo->testCollision(capsuleVsConvexPolyhedronBatch, 0, capsuleVsConvexPolyhedronBatch.getNbObjects(), reportContacts, stopFirstContactFound, allocator); - if (stopFirstContactFound && contactFound) return true; + contactFound |= capsuleVsConvexPolyAlgo->testCollision(capsuleVsConvexPolyhedronBatch, 0, capsuleVsConvexPolyhedronBatch.getNbObjects(), reportContacts, clipWithPreviousAxisIfStillColliding, allocator); } if (convexPolyhedronVsConvexPolyhedronBatch.getNbObjects() > 0) { - contactFound |= convexPolyVsConvexPolyAlgo->testCollision(convexPolyhedronVsConvexPolyhedronBatch, 0, convexPolyhedronVsConvexPolyhedronBatch.getNbObjects(), reportContacts, stopFirstContactFound, allocator); - if (stopFirstContactFound && contactFound) return true; + contactFound |= convexPolyVsConvexPolyAlgo->testCollision(convexPolyhedronVsConvexPolyhedronBatch, 0, convexPolyhedronVsConvexPolyhedronBatch.getNbObjects(), reportContacts, clipWithPreviousAxisIfStillColliding, allocator); } return contactFound; } // Process the potential contacts after narrow-phase collision detection -void CollisionDetection::processAllPotentialContacts(NarrowPhaseInput& narrowPhaseInput, bool updateLastFrameInfo) { +void CollisionDetection::processAllPotentialContacts(NarrowPhaseInput& narrowPhaseInput, bool updateLastFrameInfo, + List<ContactPointInfo>& potentialContactPoints, + Map<OverlappingPair::OverlappingPairId, uint>* mapPairIdToContactPairIndex, + List<ContactManifoldInfo>& potentialContactManifolds, + List<ContactPair>* contactPairs, + Map<Entity, List<uint>>& mapBodyToContactPairs) { + + assert(contactPairs->size() == 0); + assert(mapPairIdToContactPairIndex->size() == 0); // get the narrow-phase batches to test for collision NarrowPhaseInfoBatch& sphereVsSphereBatch = narrowPhaseInput.getSphereVsSphereBatch(); @@ -374,12 +399,18 @@ void CollisionDetection::processAllPotentialContacts(NarrowPhaseInput& narrowPha NarrowPhaseInfoBatch& convexPolyhedronVsConvexPolyhedronBatch = narrowPhaseInput.getConvexPolyhedronVsConvexPolyhedronBatch(); // Process the potential contacts - processPotentialContacts(sphereVsSphereBatch, updateLastFrameInfo); - processPotentialContacts(sphereVsCapsuleBatch, updateLastFrameInfo); - processPotentialContacts(capsuleVsCapsuleBatch, updateLastFrameInfo); - processPotentialContacts(sphereVsConvexPolyhedronBatch, updateLastFrameInfo); - processPotentialContacts(capsuleVsConvexPolyhedronBatch, updateLastFrameInfo); - processPotentialContacts(convexPolyhedronVsConvexPolyhedronBatch, updateLastFrameInfo); + processPotentialContacts(sphereVsSphereBatch, updateLastFrameInfo, potentialContactPoints, mapPairIdToContactPairIndex, + potentialContactManifolds, contactPairs, mapBodyToContactPairs); + processPotentialContacts(sphereVsCapsuleBatch, updateLastFrameInfo, potentialContactPoints, mapPairIdToContactPairIndex, + potentialContactManifolds, contactPairs, mapBodyToContactPairs); + processPotentialContacts(capsuleVsCapsuleBatch, updateLastFrameInfo, potentialContactPoints, mapPairIdToContactPairIndex, + potentialContactManifolds, contactPairs, mapBodyToContactPairs); + processPotentialContacts(sphereVsConvexPolyhedronBatch, updateLastFrameInfo, potentialContactPoints, mapPairIdToContactPairIndex, + potentialContactManifolds, contactPairs, mapBodyToContactPairs); + processPotentialContacts(capsuleVsConvexPolyhedronBatch, updateLastFrameInfo, potentialContactPoints, mapPairIdToContactPairIndex, + potentialContactManifolds, contactPairs, mapBodyToContactPairs); + processPotentialContacts(convexPolyhedronVsConvexPolyhedronBatch, updateLastFrameInfo, potentialContactPoints, mapPairIdToContactPairIndex, + potentialContactManifolds, contactPairs, mapBodyToContactPairs); } // Compute the narrow-phase collision detection @@ -389,23 +420,376 @@ void CollisionDetection::computeNarrowPhase() { MemoryAllocator& allocator = mMemoryManager.getSingleFrameAllocator(); + // Swap the previous and current contacts lists + swapPreviousAndCurrentContacts(); + // Test the narrow-phase collision detection on the batches to be tested - testNarrowPhaseCollision(mNarrowPhaseInput, false, true, allocator); + testNarrowPhaseCollision(mNarrowPhaseInput, true, true, allocator); // Process all the potential contacts after narrow-phase collision - processAllPotentialContacts(mNarrowPhaseInput, true); + processAllPotentialContacts(mNarrowPhaseInput, true, mPotentialContactPoints, mCurrentMapPairIdToContactPairIndex, + mPotentialContactManifolds, mCurrentContactPairs, mMapBodyToContactPairs); // Reduce the number of contact points in the manifolds - reduceContactManifolds(mOverlappingPairs); + reducePotentialContactManifolds(mCurrentContactPairs, mPotentialContactManifolds, mPotentialContactPoints); - // Add all the contact manifolds (between colliding bodies) to the bodies - addAllContactManifoldsToBodies(); + assert(mCurrentContactManifolds->size() == 0); + assert(mCurrentContactPoints->size() == 0); + + mContactPairsIndicesOrderingForContacts.reserve(mCurrentContactPairs->size()); + + mNarrowPhaseInput.clear(); +} + +// Compute the narrow-phase collision detection for the testOverlap() methods. +/// This method returns true if contacts are found. +bool CollisionDetection::computeNarrowPhaseOverlapSnapshot(NarrowPhaseInput& narrowPhaseInput, OverlapCallback* callback) { + + RP3D_PROFILE("CollisionDetection::computeNarrowPhaseOverlapSnapshot()", mProfiler); + + MemoryAllocator& allocator = mMemoryManager.getPoolAllocator(); + + // Test the narrow-phase collision detection on the batches to be tested + bool collisionFound = testNarrowPhaseCollision(narrowPhaseInput, false, false, allocator); + if (collisionFound && callback != nullptr) { + + // Compute the overlapping bodies + List<Pair<Entity, Entity>> overlapBodies(allocator); + computeSnapshotContactPairs(narrowPhaseInput, overlapBodies); + + // Report overlapping bodies + OverlapCallback::CallbackData callbackData(overlapBodies, *mWorld); + (*callback).onOverlap(callbackData); + } + + return collisionFound; +} + +// Process the potential overlapping bodies for the testOverlap() methods +void CollisionDetection::computeSnapshotContactPairs(NarrowPhaseInput& narrowPhaseInput, List<Pair<Entity, Entity>>& overlapPairs) const { + + // get the narrow-phase batches to test for collision + NarrowPhaseInfoBatch& sphereVsSphereBatch = narrowPhaseInput.getSphereVsSphereBatch(); + NarrowPhaseInfoBatch& sphereVsCapsuleBatch = narrowPhaseInput.getSphereVsCapsuleBatch(); + NarrowPhaseInfoBatch& capsuleVsCapsuleBatch = narrowPhaseInput.getCapsuleVsCapsuleBatch(); + NarrowPhaseInfoBatch& sphereVsConvexPolyhedronBatch = narrowPhaseInput.getSphereVsConvexPolyhedronBatch(); + NarrowPhaseInfoBatch& capsuleVsConvexPolyhedronBatch = narrowPhaseInput.getCapsuleVsConvexPolyhedronBatch(); + NarrowPhaseInfoBatch& convexPolyhedronVsConvexPolyhedronBatch = narrowPhaseInput.getConvexPolyhedronVsConvexPolyhedronBatch(); + + // Process the potential contacts + computeSnapshotContactPairs(sphereVsSphereBatch, overlapPairs); + computeSnapshotContactPairs(sphereVsCapsuleBatch, overlapPairs); + computeSnapshotContactPairs(capsuleVsCapsuleBatch, overlapPairs); + computeSnapshotContactPairs(sphereVsConvexPolyhedronBatch, overlapPairs); + computeSnapshotContactPairs(capsuleVsConvexPolyhedronBatch, overlapPairs); + computeSnapshotContactPairs(convexPolyhedronVsConvexPolyhedronBatch, overlapPairs); +} + +// Convert the potential overlapping bodies for the testOverlap() methods +void CollisionDetection::computeSnapshotContactPairs(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, List<Pair<Entity, Entity>>& overlapPairs) const { + + RP3D_PROFILE("CollisionDetection::computeSnapshotContactPairs()", mProfiler); + + // For each narrow phase info object + for(uint i=0; i < narrowPhaseInfoBatch.getNbObjects(); i++) { + + // If there is a contact + if (narrowPhaseInfoBatch.isColliding[i]) { + + // Add the pair of bodies in contact into the list + overlapPairs.add(Pair<Entity, Entity>(narrowPhaseInfoBatch.overlappingPairs[i]->getShape1()->getBody()->getEntity(), + narrowPhaseInfoBatch.overlappingPairs[i]->getShape2()->getBody()->getEntity())); + } + + narrowPhaseInfoBatch.resetContactPoints(i); + } +} + +// Compute the narrow-phase collision detection for the testCollision() methods. +// This method returns true if contacts are found. +bool CollisionDetection::computeNarrowPhaseCollisionSnapshot(NarrowPhaseInput& narrowPhaseInput, CollisionCallback& callback) { + + RP3D_PROFILE("CollisionDetection::computeNarrowPhaseCollisionSnapshot()", mProfiler); + + MemoryAllocator& allocator = mMemoryManager.getPoolAllocator(); + + // Test the narrow-phase collision detection on the batches to be tested + bool collisionFound = testNarrowPhaseCollision(narrowPhaseInput, true, false, allocator); + + // If collision has been found, create contacts + if (collisionFound) { + + List<ContactPointInfo> potentialContactPoints(allocator); + List<ContactManifoldInfo> potentialContactManifolds(allocator); + Map<OverlappingPair::OverlappingPairId, uint> mapPairIdToContactPairIndex(allocator); + List<ContactPair> contactPairs(allocator); + List<ContactManifold> contactManifolds(allocator); + List<ContactPoint> contactPoints(allocator); + Map<Entity, List<uint>> mapBodyToContactPairs(allocator); + + // Process all the potential contacts after narrow-phase collision + processAllPotentialContacts(narrowPhaseInput, true, potentialContactPoints, &mapPairIdToContactPairIndex, potentialContactManifolds, + &contactPairs, mapBodyToContactPairs); + + // Reduce the number of contact points in the manifolds + reducePotentialContactManifolds(&contactPairs, potentialContactManifolds, potentialContactPoints); + + // Create the actual contact manifolds and contact points + createSnapshotContacts(contactPairs, contactManifolds, contactPoints, potentialContactManifolds, + potentialContactPoints); + + // Report the contacts to the user + reportContacts(callback, &contactPairs, &contactManifolds, &contactPoints); + } + + return collisionFound; +} + +// Swap the previous and current contacts lists +void CollisionDetection::swapPreviousAndCurrentContacts() { + + if (mPreviousContactPairs == &mContactPairs1) { + + mPreviousContactPairs = &mContactPairs2; + mPreviousContactManifolds = &mContactManifolds2; + mPreviousContactPoints = &mContactPoints2; + mPreviousMapPairIdToContactPairIndex = &mMapPairIdToContactPairIndex2; + + mCurrentContactPairs = &mContactPairs1; + mCurrentContactManifolds = &mContactManifolds1; + mCurrentContactPoints = &mContactPoints1; + mCurrentMapPairIdToContactPairIndex = &mMapPairIdToContactPairIndex1; + } + else { + + mPreviousContactPairs = &mContactPairs1; + mPreviousContactManifolds = &mContactManifolds1; + mPreviousContactPoints = &mContactPoints1; + mPreviousMapPairIdToContactPairIndex = &mMapPairIdToContactPairIndex1; + + mCurrentContactPairs = &mContactPairs2; + mCurrentContactManifolds = &mContactManifolds2; + mCurrentContactPoints = &mContactPoints2; + mCurrentMapPairIdToContactPairIndex = &mMapPairIdToContactPairIndex2; + } +} + +// Create the actual contact manifolds and contacts points +void CollisionDetection::createContacts() { + + RP3D_PROFILE("CollisionDetection::createContacts()", mProfiler); + + assert(mCurrentContactPairs->size() == mContactPairsIndicesOrderingForContacts.size()); + + mCurrentContactManifolds->reserve(mCurrentContactPairs->size()); + mCurrentContactPoints->reserve(mCurrentContactManifolds->size()); + + // For each contact pair + for (uint p=0; p < mContactPairsIndicesOrderingForContacts.size(); p++) { + + ContactPair& contactPair = (*mCurrentContactPairs)[mContactPairsIndicesOrderingForContacts[p]]; + assert(contactPair.potentialContactManifoldsIndices.size() > 0); + + contactPair.contactManifoldsIndex = mCurrentContactManifolds->size(); + contactPair.nbContactManifolds = contactPair.potentialContactManifoldsIndices.size(); + contactPair.contactPointsIndex = mCurrentContactPoints->size(); + + // For each potential contact manifold of the pair + for (uint m=0; m < contactPair.potentialContactManifoldsIndices.size(); m++) { + + ContactManifoldInfo& potentialManifold = mPotentialContactManifolds[contactPair.potentialContactManifoldsIndices[m]]; + + // Start index and number of contact points for this manifold + const uint contactPointsIndex = mCurrentContactPoints->size(); + const int8 nbContactPoints = static_cast<int8>(potentialManifold.potentialContactPointsIndices.size()); + contactPair.nbToTalContactPoints += nbContactPoints; + + // We create a new contact manifold + ContactManifold contactManifold(contactPair.body1Entity, contactPair.body2Entity, contactPair.proxyShape1Entity, + contactPair.proxyShape2Entity, contactPointsIndex, nbContactPoints); + + // Add the contact manifold + mCurrentContactManifolds->add(contactManifold); + + assert(potentialManifold.potentialContactPointsIndices.size() > 0); + + // For each contact point of the manifold + for (uint c=0; c < potentialManifold.potentialContactPointsIndices.size(); c++) { + + ContactPointInfo& potentialContactPoint = mPotentialContactPoints[potentialManifold.potentialContactPointsIndices[c]]; + + // Create a new contact point + ContactPoint contactPoint(potentialContactPoint, mWorld->mConfig); + + // Add the contact point + mCurrentContactPoints->add(contactPoint); + } + } + } + + // Initialize the current contacts with the contacts from the previous frame (for warmstarting) + initContactsWithPreviousOnes(); // Report contacts to the user - reportAllContacts(); + if (mWorld->mEventListener != nullptr) { + reportContacts(*(mWorld->mEventListener), mCurrentContactPairs, mCurrentContactManifolds, mCurrentContactPoints); + } - // Clear the list of narrow-phase infos - mNarrowPhaseInput.clear(); + // Reset the potential contacts + mPotentialContactPoints.clear(true); + mPotentialContactManifolds.clear(true); + mContactPairsIndicesOrderingForContacts.clear(true); +} + +// Create the actual contact manifolds and contacts points for testCollision() methods +void CollisionDetection::createSnapshotContacts(List<ContactPair>& contactPairs, + List<ContactManifold>& contactManifolds, + List<ContactPoint>& contactPoints, + List<ContactManifoldInfo>& potentialContactManifolds, + List<ContactPointInfo>& potentialContactPoints) { + + RP3D_PROFILE("CollisionDetection::createSnapshotContacts()", mProfiler); + + contactManifolds.reserve(contactPairs.size()); + contactPoints.reserve(contactManifolds.size()); + + // For each contact pair + for (uint p=0; p < contactPairs.size(); p++) { + + ContactPair& contactPair = contactPairs[p]; + assert(contactPair.potentialContactManifoldsIndices.size() > 0); + + contactPair.contactManifoldsIndex = contactManifolds.size(); + contactPair.nbContactManifolds = contactPair.potentialContactManifoldsIndices.size(); + contactPair.contactPointsIndex = contactPoints.size(); + + // For each potential contact manifold of the pair + for (uint m=0; m < contactPair.potentialContactManifoldsIndices.size(); m++) { + + ContactManifoldInfo& potentialManifold = potentialContactManifolds[contactPair.potentialContactManifoldsIndices[m]]; + + // Start index and number of contact points for this manifold + const uint contactPointsIndex = contactPoints.size(); + const int8 nbContactPoints = static_cast<int8>(potentialManifold.potentialContactPointsIndices.size()); + contactPair.nbToTalContactPoints += nbContactPoints; + + // We create a new contact manifold + ContactManifold contactManifold(contactPair.body1Entity, contactPair.body2Entity, contactPair.proxyShape1Entity, + contactPair.proxyShape2Entity, contactPointsIndex, nbContactPoints); + + // Add the contact manifold + contactManifolds.add(contactManifold); + + assert(potentialManifold.potentialContactPointsIndices.size() > 0); + + // For each contact point of the manifold + for (uint c=0; c < potentialManifold.potentialContactPointsIndices.size(); c++) { + + ContactPointInfo& potentialContactPoint = potentialContactPoints[potentialManifold.potentialContactPointsIndices[c]]; + + // Create a new contact point + ContactPoint contactPoint(potentialContactPoint, mWorld->mConfig); + + // Add the contact point + contactPoints.add(contactPoint); + } + } + } +} + +// Initialize the current contacts with the contacts from the previous frame (for warmstarting) +void CollisionDetection::initContactsWithPreviousOnes() { + + // For each contact pair of the current frame + for (uint i=0; i < mCurrentContactPairs->size(); i++) { + + ContactPair& currentContactPair = (*mCurrentContactPairs)[i]; + + // Find the corresponding contact pair in the previous frame (if any) + auto itPrevContactPair = mPreviousMapPairIdToContactPairIndex->find(currentContactPair.pairId); + + // If we have found a corresponding contact pair in the previous frame + if (itPrevContactPair != mPreviousMapPairIdToContactPairIndex->end()) { + + const uint previousContactPairIndex = itPrevContactPair->second; + ContactPair& previousContactPair = (*mPreviousContactPairs)[previousContactPairIndex]; + + // --------------------- Contact Manifolds --------------------- // + + const uint contactManifoldsIndex = currentContactPair.contactManifoldsIndex; + const uint nbContactManifolds = currentContactPair.nbContactManifolds; + + // For each current contact manifold of the contact pair + for (uint m=contactManifoldsIndex; m < contactManifoldsIndex + nbContactManifolds; m++) { + + assert(m < mCurrentContactManifolds->size()); + ContactManifold& currentContactManifold = (*mCurrentContactManifolds)[m]; + assert(currentContactManifold.mNbContactPoints > 0); + ContactPoint& currentContactPoint = (*mCurrentContactPoints)[currentContactManifold.mContactPointsIndex]; + const Vector3& currentContactPointNormal = currentContactPoint.getNormal(); + + // Find a similar contact manifold among the contact manifolds from the previous frame (for warmstarting) + const uint previousContactManifoldIndex = previousContactPair.contactManifoldsIndex; + const uint previousNbContactManifolds = previousContactPair.nbContactManifolds; + for (uint p=previousContactManifoldIndex; p < previousContactManifoldIndex + previousNbContactManifolds; p++) { + + ContactManifold& previousContactManifold = (*mPreviousContactManifolds)[p]; + assert(previousContactManifold.mNbContactPoints > 0); + ContactPoint& previousContactPoint = (*mPreviousContactPoints)[previousContactManifold.mContactPointsIndex]; + + // If the previous contact manifold has a similar contact normal with the current manifold + if (previousContactPoint.getNormal().dot(currentContactPointNormal) >= mWorld->mConfig.cosAngleSimilarContactManifold) { + + // Transfer data from the previous contact manifold to the current one + currentContactManifold.mFrictionVector1 = previousContactManifold.mFrictionVector1; + currentContactManifold.mFrictionVector2 = previousContactManifold.mFrictionVector2; + currentContactManifold.mFrictionImpulse1 = previousContactManifold.mFrictionImpulse1; + currentContactManifold.mFrictionImpulse2 = previousContactManifold.mFrictionImpulse2; + currentContactManifold.mFrictionTwistImpulse = previousContactManifold.mFrictionTwistImpulse; + currentContactManifold.mRollingResistanceImpulse = previousContactManifold.mRollingResistanceImpulse; + + break; + } + } + } + + // --------------------- Contact Points --------------------- // + + const uint contactPointsIndex = currentContactPair.contactPointsIndex; + const uint nbTotalContactPoints = currentContactPair.nbToTalContactPoints; + + // For each current contact point of the contact pair + for (uint c=contactPointsIndex; c < contactPointsIndex + nbTotalContactPoints; c++) { + + assert(c < mCurrentContactPoints->size()); + ContactPoint& currentContactPoint = (*mCurrentContactPoints)[c]; + + // Find a similar contact point among the contact points from the previous frame (for warmstarting) + const uint previousContactPointsIndex = previousContactPair.contactPointsIndex; + const uint previousNbContactPoints = previousContactPair.nbToTalContactPoints; + for (uint p=previousContactPointsIndex; p < previousContactPointsIndex + previousNbContactPoints; p++) { + + ContactPoint& previousContactPoint = (*mPreviousContactPoints)[p]; + + // If the previous contact point is very close to th current one + const decimal distSquare = (currentContactPoint.getLocalPointOnShape1() - previousContactPoint.getLocalPointOnShape1()).lengthSquare(); + if (distSquare <= mWorld->mConfig.persistentContactDistanceThreshold * mWorld->mConfig.persistentContactDistanceThreshold) { + + // Transfer data from the previous contact point to the current one + currentContactPoint.setPenetrationImpulse(previousContactPoint.getPenetrationImpulse()); + currentContactPoint.setIsRestingContact(previousContactPoint.getIsRestingContact()); + + break; + } + } + } + } + } + + mPreviousContactPoints->clear(); + mPreviousContactManifolds->clear(); + mPreviousContactPairs->clear(); + mPreviousMapPairIdToContactPairIndex->clear(); } // Remove a body from the collision detection @@ -440,19 +824,6 @@ void CollisionDetection::removeProxyCollisionShape(ProxyShape* proxyShape) { mBroadPhaseSystem.removeProxyCollisionShape(proxyShape); } -void CollisionDetection::addAllContactManifoldsToBodies() { - - RP3D_PROFILE("CollisionDetection::addAllContactManifoldsToBodies()", mProfiler); - - // For each overlapping pairs in contact during the narrow-phase - for (auto it = mOverlappingPairs.begin(); it != mOverlappingPairs.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); - } -} - // Ray casting method void CollisionDetection::raycast(RaycastCallback* raycastCallback, const Ray& ray, @@ -467,46 +838,15 @@ void CollisionDetection::raycast(RaycastCallback* raycastCallback, mBroadPhaseSystem.raycast(ray, rayCastTest, raycastWithCategoryMaskBits); } -// Add a contact manifold to the linked list of contact manifolds of the two bodies involved -// in the corresponding contact -void CollisionDetection::addContactManifoldToBody(OverlappingPair* pair) { +// Convert the potential contact into actual contacts +void CollisionDetection::processPotentialContacts(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, bool updateLastFrameInfo, + List<ContactPointInfo>& potentialContactPoints, + Map<OverlappingPair::OverlappingPairId, uint>* mapPairIdToContactPairIndex, + List<ContactManifoldInfo>& potentialContactManifolds, + List<ContactPair>* contactPairs, + Map<Entity, List<uint>>& mapBodyToContactPairs) { - assert(pair != nullptr); - - CollisionBody* body1 = pair->getShape1()->getBody(); - CollisionBody* body2 = pair->getShape2()->getBody(); - const ContactManifoldSet& manifoldSet = pair->getContactManifoldSet(); - - // For each contact manifold in the set of manifolds in the pair - ContactManifold* contactManifold = manifoldSet.getContactManifolds(); - while (contactManifold != nullptr) { - - assert(contactManifold->getNbContactPoints() > 0); - - // Add the contact manifold at the beginning of the linked - // list of contact manifolds of the first body - ContactManifoldListElement* listElement1 = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, - sizeof(ContactManifoldListElement))) - ContactManifoldListElement(contactManifold, - body1->mContactManifoldsList); - body1->mContactManifoldsList = listElement1; - - // Add the contact manifold at the beginning of the linked - // list of the contact manifolds of the second body - ContactManifoldListElement* listElement2 = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, - sizeof(ContactManifoldListElement))) - ContactManifoldListElement(contactManifold, - body2->mContactManifoldsList); - body2->mContactManifoldsList = listElement2; - - contactManifold = contactManifold->getNext(); - } -} - -/// Convert the potential contact into actual contacts -void CollisionDetection::processPotentialContacts(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, bool updateLastFrameInfo) { - - RP3D_PROFILE("CollisionDetection::processAllPotentialContacts()", mProfiler); + RP3D_PROFILE("CollisionDetection::processPotentialContacts()", mProfiler); // For each narrow phase info object for(uint i=0; i < narrowPhaseInfoBatch.getNbObjects(); i++) { @@ -518,494 +858,564 @@ void CollisionDetection::processPotentialContacts(NarrowPhaseInfoBatch& narrowPh narrowPhaseInfoBatch.lastFrameCollisionInfos[i]->isValid = true; } - if (narrowPhaseInfoBatch.isColliding[i]) { + // Add the potential contacts + for (uint j=0; j < narrowPhaseInfoBatch.contactPoints[i].size(); j++) { - assert(narrowPhaseInfoBatch.contactPoints[i].size() > 0); + assert(narrowPhaseInfoBatch.isColliding[i]); - // Transfer the contact points from the narrow phase info to the overlapping pair - narrowPhaseInfoBatch.overlappingPairs[i]->addPotentialContactPoints(narrowPhaseInfoBatch, i); + const ContactPointInfo& contactPoint = *(narrowPhaseInfoBatch.contactPoints[i][j]); - // Remove the contacts points from the narrow phase info object. - narrowPhaseInfoBatch.resetContactPoints(i); + // Add the contact point to the list of potential contact points + const uint contactPointIndex = static_cast<uint>(potentialContactPoints.size()); + + // TODO : We should probably use single frame allocator here for potentialContactPoints + // If so, do not forget to call potentialContactPoints.clear(true) at the end of frame + potentialContactPoints.add(contactPoint); + + bool similarManifoldFound = false; + + // If there is already a contact pair for this overlapping pair + OverlappingPair::OverlappingPairId pairId = narrowPhaseInfoBatch.overlappingPairs[i]->getId(); + auto it = mapPairIdToContactPairIndex->find(pairId); + ContactPair* pairContact = nullptr; + if (it != mapPairIdToContactPairIndex->end()) { + + assert(it->first == pairId); + + const uint pairContactIndex = it->second; + pairContact = &((*contactPairs)[pairContactIndex]); + + assert(pairContact->potentialContactManifoldsIndices.size() > 0); + + // For each contact manifold of the overlapping pair + for (uint m=0; m < pairContact->potentialContactManifoldsIndices.size(); m++) { + + uint contactManifoldIndex = pairContact->potentialContactManifoldsIndices[m]; + + // Get the first contact point of the current manifold + assert(potentialContactManifolds[contactManifoldIndex].potentialContactPointsIndices.size() > 0); + const uint manifoldContactPointIndex = potentialContactManifolds[contactManifoldIndex].potentialContactPointsIndices[0]; + const ContactPointInfo& manifoldContactPoint = potentialContactPoints[manifoldContactPointIndex]; + + // If we have found a corresponding manifold for the new contact point + // (a manifold with a similar contact normal direction) + if (manifoldContactPoint.normal.dot(contactPoint.normal) >= mWorld->mConfig.cosAngleSimilarContactManifold) { + + // Add the contact point to the manifold + potentialContactManifolds[contactManifoldIndex].potentialContactPointsIndices.add(contactPointIndex); + + similarManifoldFound = true; + + break; + } + } + } + + // If we have not found a manifold with a similar contact normal for the contact point + if (!similarManifoldFound) { + + // Create a new contact manifold for the overlapping pair + // TODO : We should probably use single frame allocator here + // If so, do not forget to call potentialContactPoints.clear(true) at the end of frame + ContactManifoldInfo contactManifoldInfo(pairId, mMemoryManager.getPoolAllocator()); + + // Add the contact point to the manifold + contactManifoldInfo.potentialContactPointsIndices.add(contactPointIndex); + + // If the overlapping pair contact does not exists yet + if (pairContact == nullptr) { + + Entity body1Entity = narrowPhaseInfoBatch.overlappingPairs[i]->getShape1()->getBody()->getEntity(); + Entity body2Entity = narrowPhaseInfoBatch.overlappingPairs[i]->getShape2()->getBody()->getEntity(); + + assert(!mWorld->mBodyComponents.getIsEntityDisabled(body1Entity) || !mWorld->mBodyComponents.getIsEntityDisabled(body2Entity)); + + // TODO : We should probably use a single frame allocator here + const uint newContactPairIndex = contactPairs->size(); + ContactPair overlappingPairContact(pairId, body1Entity, body2Entity, + narrowPhaseInfoBatch.overlappingPairs[i]->getShape1()->getEntity(), + narrowPhaseInfoBatch.overlappingPairs[i]->getShape2()->getEntity(), + newContactPairIndex, mMemoryManager.getPoolAllocator()); + contactPairs->add(overlappingPairContact); + pairContact = &((*contactPairs)[newContactPairIndex]); + mapPairIdToContactPairIndex->add(Pair<OverlappingPair::OverlappingPairId, uint>(pairId, newContactPairIndex)); + + auto itbodyContactPairs = mapBodyToContactPairs.find(body1Entity); + if (itbodyContactPairs != mapBodyToContactPairs.end()) { + itbodyContactPairs->second.add(newContactPairIndex); + } + else { + List<uint> contactPairs(mMemoryManager.getPoolAllocator(), 1); + contactPairs.add(newContactPairIndex); + mapBodyToContactPairs.add(Pair<Entity, List<uint>>(body1Entity, contactPairs)); + } + itbodyContactPairs = mapBodyToContactPairs.find(body2Entity); + if (itbodyContactPairs != mapBodyToContactPairs.end()) { + itbodyContactPairs->second.add(newContactPairIndex); + } + else { + List<uint> contactPairs(mMemoryManager.getPoolAllocator(), 1); + contactPairs.add(newContactPairIndex); + mapBodyToContactPairs.add(Pair<Entity, List<uint>>(body2Entity, contactPairs)); + } + } + + assert(pairContact != nullptr); + + // Add the potential contact manifold + uint contactManifoldIndex = static_cast<uint>(potentialContactManifolds.size()); + potentialContactManifolds.add(contactManifoldInfo); + + // Add the contact manifold to the overlapping pair contact + assert(pairContact->pairId == contactManifoldInfo.pairId); + pairContact->potentialContactManifoldsIndices.add(contactManifoldIndex); + } + + assert(pairContact->potentialContactManifoldsIndices.size() > 0); } + + narrowPhaseInfoBatch.resetContactPoints(i); } } // Clear the obsolete manifolds and contact points and reduce the number of contacts points of the remaining manifolds -void CollisionDetection::reduceContactManifolds(const OverlappingPairMap& overlappingPairs) { +void CollisionDetection::reducePotentialContactManifolds(List<ContactPair>* contactPairs, + List<ContactManifoldInfo>& potentialContactManifolds, + const List<ContactPointInfo>& potentialContactPoints) const { - // For each overlapping pairs in contact during the narrow-phase - for (auto it = overlappingPairs.begin(); it != overlappingPairs.end(); ++it) { + RP3D_PROFILE("CollisionDetection::reducePotentialContactManifolds()", mProfiler); - OverlappingPair* pair = it->second; + // Reduce the number of potential contact manifolds in a contact pair + for (uint i=0; i < contactPairs->size(); i++) { - // Clear the obsolete contact manifolds and contact points - pair->clearObsoleteManifoldsAndContactPoints(); + ContactPair& contactPair = (*contactPairs)[i]; - // Reduce the contact manifolds and contact points if there are too many of them - pair->reduceContactManifolds(); + assert(contactPair.potentialContactManifoldsIndices.size() > 0); + + // While there are too many manifolds in the contact pair + while(contactPair.potentialContactManifoldsIndices.size() > mWorld->mConfig.nbMaxContactManifolds) { + + // Look for a manifold with the smallest contact penetration depth. + decimal minDepth = DECIMAL_LARGEST; + int minDepthManifoldIndex = -1; + for (uint j=0; j < contactPair.potentialContactManifoldsIndices.size(); j++) { + + ContactManifoldInfo& manifold = potentialContactManifolds[contactPair.potentialContactManifoldsIndices[j]]; + + // Get the largest contact point penetration depth of the manifold + const decimal depth = computePotentialManifoldLargestContactDepth(manifold, potentialContactPoints); + + if (depth < minDepth) { + minDepth = depth; + minDepthManifoldIndex = static_cast<int>(j); + } + } + + // Remove the non optimal manifold + assert(minDepthManifoldIndex >= 0); + contactPair.potentialContactManifoldsIndices.removeAt(minDepthManifoldIndex); + } } -} + // Reduce the number of potential contact points in the manifolds + for (uint i=0; i < contactPairs->size(); i++) { -// Report contacts for all the colliding overlapping pairs -void CollisionDetection::reportAllContacts() { + const ContactPair& pairContact = (*contactPairs)[i]; - RP3D_PROFILE("CollisionDetection::reportAllContacts()", mProfiler); + // For each potential contact manifold + for (uint j=0; j < pairContact.potentialContactManifoldsIndices.size(); j++) { - // For each overlapping pairs in contact during the narrow-phase - for (auto it = mOverlappingPairs.begin(); it != mOverlappingPairs.end(); ++it) { + ContactManifoldInfo& manifold = potentialContactManifolds[pairContact.potentialContactManifoldsIndices[j]]; - OverlappingPair* pair = it->second; + // If there are two many contact points in the manifold + if (manifold.potentialContactPointsIndices.size() > MAX_CONTACT_POINTS_IN_MANIFOLD) { - // If there is a user callback - if (mWorld->mEventListener != nullptr && pair->hasContacts()) { + Transform shape1LocalToWorldTransoform = mOverlappingPairs[manifold.pairId]->getShape1()->getLocalToWorldTransform(); - CollisionCallback::CollisionCallbackInfo collisionInfo(pair, mMemoryManager); + // Reduce the number of contact points in the manifold + reduceContactPoints(manifold, shape1LocalToWorldTransoform, potentialContactPoints); + } - // Trigger a callback event to report the new contact to the user - mWorld->mEventListener->newContact(collisionInfo); + assert(manifold.potentialContactPointsIndices.size() <= MAX_CONTACT_POINTS_IN_MANIFOLD); } } } -// Compute the middle-phase collision detection between two proxy shapes -void CollisionDetection::computeMiddlePhaseForProxyShapes(OverlappingPair* pair, NarrowPhaseInput& outNarrowPhaseInput) { +// Return the largest depth of all the contact points of a potential manifold +decimal CollisionDetection::computePotentialManifoldLargestContactDepth(const ContactManifoldInfo& manifold, + const List<ContactPointInfo>& potentialContactPoints) const { - ProxyShape* shape1 = pair->getShape1(); - ProxyShape* shape2 = pair->getShape2(); + decimal largestDepth = 0.0f; - // ------------------------------------------------------- + assert(manifold.potentialContactPointsIndices.size() > 0); - const bool isShape1Convex = shape1->getCollisionShape()->isConvex(); - const bool isShape2Convex = shape2->getCollisionShape()->isConvex(); + for (uint i=0; i < manifold.potentialContactPointsIndices.size(); i++) { + decimal depth = potentialContactPoints[manifold.potentialContactPointsIndices[i]].penetrationDepth; - pair->makeLastFrameCollisionInfosObsolete(); - - // If both shapes are convex - if ((isShape1Convex && isShape2Convex)) { - - // Select the narrow phase algorithm to use according to the two collision shapes - NarrowPhaseAlgorithmType algorithmType = mCollisionDispatch.selectNarrowPhaseAlgorithm(shape1->getCollisionShape()->getType(), - shape2->getCollisionShape()->getType()); - // No middle-phase is necessary, simply create a narrow phase info - // for the narrow-phase collision detection - outNarrowPhaseInput.addNarrowPhaseTest(pair, shape1->getCollisionShape(), shape2->getCollisionShape(), - shape1->getLocalToWorldTransform(), shape2->getLocalToWorldTransform(), - algorithmType, mMemoryManager.getPoolAllocator()); - - } - // Concave vs Convex algorithm - else if ((!isShape1Convex && isShape2Convex) || (!isShape2Convex && isShape1Convex)) { - - // Run the middle-phase collision detection algorithm to find the triangles of the concave - // shape we need to use during the narrow-phase collision detection - computeConvexVsConcaveMiddlePhase(pair, mMemoryManager.getPoolAllocator(), outNarrowPhaseInput); + if (depth > largestDepth) { + largestDepth = depth; + } } - pair->clearObsoleteLastFrameCollisionInfos(); + return largestDepth; } -// Report all the bodies that overlap with the aabb in parameter -void CollisionDetection::testAABBOverlap(const AABB& aabb, OverlapCallback* overlapCallback, - unsigned short categoryMaskBits) { - assert(overlapCallback != nullptr); +// Reduce the number of contact points of a potential contact manifold +// This is based on the technique described by Dirk Gregorius in his +// "Contacts Creation" GDC presentation. This method will reduce the number of +// contact points to a maximum of 4 points (but it can be less). +void CollisionDetection::reduceContactPoints(ContactManifoldInfo& manifold, const Transform& shape1ToWorldTransform, + const List<ContactPointInfo>& potentialContactPoints) const { - Set<bodyindex> reportedBodies(mMemoryManager.getPoolAllocator()); + assert(manifold.potentialContactPointsIndices.size() > MAX_CONTACT_POINTS_IN_MANIFOLD); - // Ask the broad-phase to get all the overlapping shapes - List<int> overlappingNodes(mMemoryManager.getPoolAllocator()); - mBroadPhaseSystem.reportAllShapesOverlappingWithAABB(aabb, overlappingNodes); + // The following algorithm only works to reduce to a maximum of 4 contact points + assert(MAX_CONTACT_POINTS_IN_MANIFOLD == 4); - // For each overlaping proxy shape - for (uint i=0; i < overlappingNodes.size(); i++) { + // List of the candidate contact points indices in the manifold. Every time that we have found a + // point we want to keep, we will remove it from this list + List<uint> candidatePointsIndices(manifold.potentialContactPointsIndices); - // Get the overlapping proxy shape - const int broadPhaseId = overlappingNodes[i]; - ProxyShape* proxyShape = mBroadPhaseSystem.getProxyShapeForBroadPhaseId(broadPhaseId); + int8 nbReducedPoints = 0; - CollisionBody* overlapBody = proxyShape->getBody(); + uint pointsToKeepIndices[MAX_CONTACT_POINTS_IN_MANIFOLD]; + for (int8 i=0; i<MAX_CONTACT_POINTS_IN_MANIFOLD; i++) { + pointsToKeepIndices[i] = 0; + } - // If the proxy shape is from a body that we have not already reported collision - if (reportedBodies.find(overlapBody->getId()) == reportedBodies.end()) { + // Compute the initial contact point we need to keep. + // The first point we keep is always the point in a given + // constant direction (in order to always have same contact points + // between frames for better stability) - // Check if the collision filtering allows collision between the two shapes - if ((proxyShape->getCollisionCategoryBits() & categoryMaskBits) != 0) { + const Transform worldToShape1Transform = shape1ToWorldTransform.getInverse(); - // Add the body into the set of reported bodies - reportedBodies.add(overlapBody->getId()); + // Compute the contact normal of the manifold (we use the first contact point) + // in the local-space of the first collision shape + const Vector3 contactNormalShape1Space = worldToShape1Transform.getOrientation() * potentialContactPoints[candidatePointsIndices[0]].normal; - // Notify the overlap to the user - overlapCallback->notifyOverlap(overlapBody); + // Compute a search direction + const Vector3 searchDirection(1, 1, 1); + decimal maxDotProduct = DECIMAL_SMALLEST; + uint elementIndexToKeep = 0; + for (uint i=0; i < candidatePointsIndices.size(); i++) { + + const ContactPointInfo& element = potentialContactPoints[candidatePointsIndices[i]]; + decimal dotProduct = searchDirection.dot(element.localPoint1); + if (dotProduct > maxDotProduct) { + maxDotProduct = dotProduct; + elementIndexToKeep = i; + nbReducedPoints = 1; + } + } + pointsToKeepIndices[0] = candidatePointsIndices[elementIndexToKeep]; + candidatePointsIndices.removeAt(elementIndexToKeep); + assert(nbReducedPoints == 1); + + // Compute the second contact point we need to keep. + // The second point we keep is the one farthest away from the first point. + + decimal maxDistance = decimal(0.0); + elementIndexToKeep = 0; + for (uint i=0; i < candidatePointsIndices.size(); i++) { + + const ContactPointInfo& element = potentialContactPoints[candidatePointsIndices[i]]; + const ContactPointInfo& pointToKeep0 = potentialContactPoints[pointsToKeepIndices[0]]; + + assert(candidatePointsIndices[i] != pointsToKeepIndices[0]); + + const decimal distance = (pointToKeep0.localPoint1 - element.localPoint1).lengthSquare(); + if (distance >= maxDistance) { + maxDistance = distance; + elementIndexToKeep = i; + nbReducedPoints = 2; + } + + } + pointsToKeepIndices[1] = candidatePointsIndices[elementIndexToKeep]; + candidatePointsIndices.removeAt(elementIndexToKeep); + assert(nbReducedPoints == 2); + + // Compute the third contact point we need to keep. + // The third point is the one producing the triangle with the larger area + // with first and second point. + + // We compute the most positive or most negative triangle area (depending on winding) + uint thirdPointMaxAreaIndex = 0; + uint thirdPointMinAreaIndex = 0; + decimal minArea = decimal(0.0); + decimal maxArea = decimal(0.0); + bool isPreviousAreaPositive = true; + for (uint i=0; i < candidatePointsIndices.size(); i++) { + + const ContactPointInfo& element = potentialContactPoints[candidatePointsIndices[i]]; + const ContactPointInfo& pointToKeep0 = potentialContactPoints[pointsToKeepIndices[0]]; + const ContactPointInfo& pointToKeep1 = potentialContactPoints[pointsToKeepIndices[1]]; + + assert(candidatePointsIndices[i] != pointsToKeepIndices[0]); + assert(candidatePointsIndices[i] != pointsToKeepIndices[1]); + + const Vector3 newToFirst = pointToKeep0.localPoint1 - element.localPoint1; + const Vector3 newToSecond = pointToKeep1.localPoint1 - element.localPoint1; + + // Compute the triangle area + decimal area = newToFirst.cross(newToSecond).dot(contactNormalShape1Space); + + if (area >= maxArea) { + maxArea = area; + thirdPointMaxAreaIndex = i; + } + if (area <= minArea) { + minArea = area; + thirdPointMinAreaIndex = i; + } + } + assert(minArea <= decimal(0.0)); + assert(maxArea >= decimal(0.0)); + if (maxArea > (-minArea)) { + isPreviousAreaPositive = true; + pointsToKeepIndices[2] = candidatePointsIndices[thirdPointMaxAreaIndex]; + candidatePointsIndices.removeAt(thirdPointMaxAreaIndex); + } + else { + isPreviousAreaPositive = false; + pointsToKeepIndices[2] = candidatePointsIndices[thirdPointMinAreaIndex]; + candidatePointsIndices.removeAt(thirdPointMinAreaIndex); + } + nbReducedPoints = 3; + + // Compute the 4th point by choosing the triangle that add the most + // triangle area to the previous triangle and has opposite sign area (opposite winding) + + decimal largestArea = decimal(0.0); // Largest area (positive or negative) + elementIndexToKeep = 0; + nbReducedPoints = 4; + decimal area; + + // For each remaining candidate points + for (uint i=0; i < candidatePointsIndices.size(); i++) { + + const ContactPointInfo& element = potentialContactPoints[candidatePointsIndices[i]]; + + assert(candidatePointsIndices[i] != pointsToKeepIndices[0]); + assert(candidatePointsIndices[i] != pointsToKeepIndices[1]); + assert(candidatePointsIndices[i] != pointsToKeepIndices[2]); + + // For each edge of the triangle made by the first three points + for (uint j=0; j<3; j++) { + + uint edgeVertex1Index = j; + uint edgeVertex2Index = j < 2 ? j + 1 : 0; + + const ContactPointInfo& pointToKeepEdgeV1 = potentialContactPoints[pointsToKeepIndices[edgeVertex1Index]]; + const ContactPointInfo& pointToKeepEdgeV2 = potentialContactPoints[pointsToKeepIndices[edgeVertex2Index]]; + + const Vector3 newToFirst = pointToKeepEdgeV1.localPoint1 - element.localPoint1; + const Vector3 newToSecond = pointToKeepEdgeV2.localPoint1 - element.localPoint1; + + // Compute the triangle area + area = newToFirst.cross(newToSecond).dot(contactNormalShape1Space); + + // We are looking at the triangle with maximal area (positive or negative). + // If the previous area is positive, we are looking at negative area now. + // If the previous area is negative, we are looking at the positive area now. + if (isPreviousAreaPositive && area <= largestArea) { + largestArea = area; + elementIndexToKeep = i; + } + else if (!isPreviousAreaPositive && area >= largestArea) { + largestArea = area; + elementIndexToKeep = i; } } } + pointsToKeepIndices[3] = candidatePointsIndices[elementIndexToKeep]; + candidatePointsIndices.removeAt(elementIndexToKeep); + + // Only keep the four selected contact points in the manifold + manifold.potentialContactPointsIndices.clear(); + manifold.potentialContactPointsIndices.add(pointsToKeepIndices[0]); + manifold.potentialContactPointsIndices.add(pointsToKeepIndices[1]); + manifold.potentialContactPointsIndices.add(pointsToKeepIndices[2]); + manifold.potentialContactPointsIndices.add(pointsToKeepIndices[3]); } -// Return true if two bodies overlap +// Report contacts +void CollisionDetection::reportContacts() { + + if (mWorld->mEventListener != nullptr) { + reportContacts(*(mWorld->mEventListener), mCurrentContactPairs, mCurrentContactManifolds, + mCurrentContactPoints); + } +} + +// Report all contacts +void CollisionDetection::reportContacts(CollisionCallback& callback, List<ContactPair>* contactPairs, + List<ContactManifold>* manifolds, List<ContactPoint>* contactPoints) { + + RP3D_PROFILE("CollisionDetection::reportContacts()", mProfiler); + + // If there are contacts + if (contactPairs->size() > 0) { + + CollisionCallback::CallbackData callbackData(contactPairs, manifolds, contactPoints, *mWorld); + + // Call the callback method to report the contacts + callback.onContact(callbackData); + } +} + +// Return true if two bodies overlap (collide) bool CollisionDetection::testOverlap(CollisionBody* body1, CollisionBody* body2) { NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); - // For each proxy shape proxy shape of the first body - const List<Entity>& body1ProxyShapesEntities = mWorld->mBodyComponents.getProxyShapes(body1->getEntity()); - const List<Entity>& body2ProxyShapesEntities = mWorld->mBodyComponents.getProxyShapes(body2->getEntity()); - for (uint i=0; i < body1ProxyShapesEntities.size(); i++) { + // Compute the broad-phase collision detection + computeBroadPhase(); - ProxyShape* body1ProxyShape = mWorld->mProxyShapesComponents.getProxyShape(body1ProxyShapesEntities[i]); - - AABB aabb1 = body1ProxyShape->getWorldAABB(); - - // For each proxy shape of the second body - for (uint j=0; j < body2ProxyShapesEntities.size(); j++) { - - ProxyShape* body2ProxyShape = mWorld->mProxyShapesComponents.getProxyShape(body2ProxyShapesEntities[j]); - - AABB aabb2 = body2ProxyShape->getWorldAABB(); - - // Test if the AABBs of the two proxy shapes overlap - if (aabb1.testCollision(aabb2)) { - - // Create a temporary overlapping pair - OverlappingPair pair(body1ProxyShape, body2ProxyShape, mMemoryManager.getPoolAllocator(), - mMemoryManager.getPoolAllocator(), mWorld->mConfig); - - // Compute the middle-phase collision detection between the two shapes - computeMiddlePhaseForProxyShapes(&pair, narrowPhaseInput); - - } - } - } - - // Test narrow-phase collision - bool isCollisionFound = testNarrowPhaseCollision(narrowPhaseInput, true, false, mMemoryManager.getPoolAllocator()); - - // No overlap has been found - return isCollisionFound; -} - -// Report all the bodies that overlap with the body in parameter -void CollisionDetection::testOverlap(CollisionBody* body, OverlapCallback* overlapCallback, - unsigned short categoryMaskBits) { - - assert(overlapCallback != nullptr); - - Set<bodyindex> reportedBodies(mMemoryManager.getPoolAllocator()); - NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); - - // For each proxy shape proxy shape of the body - const List<Entity>& proxyShapesEntities = mWorld->mBodyComponents.getProxyShapes(body->getEntity()); - for (uint i=0; i < proxyShapesEntities.size(); i++) { - - ProxyShape* bodyProxyShape = mWorld->mProxyShapesComponents.getProxyShape(proxyShapesEntities[i]); - - if (bodyProxyShape->getBroadPhaseId() != -1) { - - // Get the AABB of the shape - const AABB& shapeAABB = mBroadPhaseSystem.getFatAABB(bodyProxyShape->getBroadPhaseId()); - - // Ask the broad-phase to get all the overlapping shapes - List<int> overlappingNodes(mMemoryManager.getPoolAllocator()); - mBroadPhaseSystem.reportAllShapesOverlappingWithAABB(shapeAABB, overlappingNodes); - - const bodyindex bodyId = body->getId(); - - // For each overlaping proxy shape - for (uint i=0; i < overlappingNodes.size(); i++) { - - // Get the overlapping proxy shape - const int broadPhaseId = overlappingNodes[i]; - ProxyShape* proxyShape = mBroadPhaseSystem.getProxyShapeForBroadPhaseId(broadPhaseId); - - // If the proxy shape is from a body that we have not already reported collision and the - // two proxy collision shapes are not from the same body - if (reportedBodies.find(proxyShape->getBody()->getId()) == reportedBodies.end() && - proxyShape->getBody()->getId() != bodyId) { - - // Check if the collision filtering allows collision between the two shapes - if ((proxyShape->getCollisionCategoryBits() & categoryMaskBits) != 0) { - - // Create a temporary overlapping pair - OverlappingPair pair(bodyProxyShape, proxyShape, mMemoryManager.getPoolAllocator(), - mMemoryManager.getPoolAllocator(), mWorld->mConfig); - - // Compute the middle-phase collision detection between the two shapes - computeMiddlePhaseForProxyShapes(&pair, narrowPhaseInput); - - // Test narrow-phase collision - if (testNarrowPhaseCollision(narrowPhaseInput, true, false, mMemoryManager.getPoolAllocator())) { - - CollisionBody* overlapBody = proxyShape->getBody(); - - // Add the body into the set of reported bodies - reportedBodies.add(overlapBody->getId()); - - // Notify the overlap to the user - overlapCallback->notifyOverlap(overlapBody); - } - - narrowPhaseInput.clear(); - } - } - } - } - } -} - -// Test and report collisions between two bodies -void CollisionDetection::testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback* collisionCallback) { - - assert(collisionCallback != nullptr); - - NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); + // Filter the overlapping pairs to get only the ones with the selected body involved OverlappingPairMap overlappingPairs(mMemoryManager.getPoolAllocator()); + filterOverlappingPairs(body1->getEntity(), body2->getEntity(), overlappingPairs); - // For each proxy shape proxy shape of the first body - const List<Entity>& body1ProxyShapesEntities = mWorld->mBodyComponents.getProxyShapes(body1->getEntity()); - const List<Entity>& body2ProxyShapesEntities = mWorld->mBodyComponents.getProxyShapes(body2->getEntity()); - for (uint i=0; i < body1ProxyShapesEntities.size(); i++) { + if (overlappingPairs.size() > 0) { - ProxyShape* body1ProxyShape = mWorld->mProxyShapesComponents.getProxyShape(body1ProxyShapesEntities[i]); + // Compute the middle-phase collision detection + computeMiddlePhase(overlappingPairs, narrowPhaseInput); - AABB aabb1 = body1ProxyShape->getWorldAABB(); - - // For each proxy shape of the second body - for (uint j=0; j < body2ProxyShapesEntities.size(); j++) { - - ProxyShape* body2ProxyShape = mWorld->mProxyShapesComponents.getProxyShape(body2ProxyShapesEntities[i]); - - AABB aabb2 = body2ProxyShape->getWorldAABB(); - - // Test if the AABBs of the two proxy shapes overlap - if (aabb1.testCollision(aabb2)) { - - OverlappingPair* pair; - const Pair<uint, uint> pairID = OverlappingPair::computeID(body1ProxyShape->getBroadPhaseId(), body2ProxyShape->getBroadPhaseId()); - - // Try to retrieve a corresponding copy of the overlapping pair (if it exists) - auto itPair = overlappingPairs.find(pairID); - - // If a copy of the overlapping pair does not exist yet - if (itPair == overlappingPairs.end()) { - - // Create a temporary copy of the overlapping pair - pair = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(OverlappingPair))) - OverlappingPair(body1ProxyShape, body2ProxyShape, mMemoryManager.getPoolAllocator(), - mMemoryManager.getPoolAllocator(), mWorld->mConfig); - - overlappingPairs.add(Pair<Pair<uint, uint>, OverlappingPair*>(pairID, pair)); - } - else { // If a temporary copy of this overlapping pair already exists - - // Retrieve the existing copy of the overlapping pair - pair = itPair->second; - } - - // Compute the middle-phase collision detection between the two shapes - computeMiddlePhaseForProxyShapes(pair, narrowPhaseInput); - } - } + // Compute the narrow-phase collision detection + return computeNarrowPhaseOverlapSnapshot(narrowPhaseInput, nullptr); } - // Test narrow-phase collision - testNarrowPhaseCollision(narrowPhaseInput, false, true, mMemoryManager.getPoolAllocator()); - - // Process the potential contacts - processAllPotentialContacts(narrowPhaseInput, false); - - // Reduce the number of contact points in the manifolds - reduceContactManifolds(overlappingPairs); - - // For each overlapping pair - for (auto it = overlappingPairs.begin(); it != overlappingPairs.end(); ++it) { - - OverlappingPair* pair = it->second; - - if (pair->hasContacts()) { - - // Report the contacts to the user - CollisionCallback::CollisionCallbackInfo collisionInfo(pair, mMemoryManager); - collisionCallback->notifyContact(collisionInfo); - } - - // Destroy the temporary overlapping pair - pair->~OverlappingPair(); - mMemoryManager.release(MemoryManager::AllocationType::Pool, pair, sizeof(OverlappingPair)); - } + return false; } -// Test and report collisions between a body and all the others bodies of the world -void CollisionDetection::testCollision(CollisionBody* body, CollisionCallback* callback, unsigned short categoryMaskBits) { - - assert(callback != nullptr); +// Report all the bodies that overlap (collide) in the world +void CollisionDetection::testOverlap(OverlapCallback& callback) { NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); - OverlappingPairMap overlappingPairs(mMemoryManager.getPoolAllocator()); - - // For each proxy shape proxy shape of the body - const List<Entity>& proxyShapesEntities = mWorld->mBodyComponents.getProxyShapes(body->getEntity()); - for (uint i=0; i < proxyShapesEntities.size(); i++) { - - ProxyShape* bodyProxyShape = mWorld->mProxyShapesComponents.getProxyShape(proxyShapesEntities[i]); - - if (bodyProxyShape->getBroadPhaseId() != -1) { - - // Get the AABB of the shape - const AABB& shapeAABB = mBroadPhaseSystem.getFatAABB(bodyProxyShape->getBroadPhaseId()); - - // Ask the broad-phase to get all the overlapping shapes - List<int> overlappingNodes(mMemoryManager.getPoolAllocator()); - mBroadPhaseSystem.reportAllShapesOverlappingWithAABB(shapeAABB, overlappingNodes); - - const bodyindex bodyId = body->getId(); - - // For each overlaping proxy shape - for (uint i=0; i < overlappingNodes.size(); i++) { - - // Get the overlapping proxy shape - const int broadPhaseId = overlappingNodes[i]; - ProxyShape* proxyShape = mBroadPhaseSystem.getProxyShapeForBroadPhaseId(broadPhaseId); - - // If the two proxy collision shapes are not from the same body - if (proxyShape->getBody()->getId() != bodyId) { - - // Check if the collision filtering allows collision between the two shapes - if ((proxyShape->getCollisionCategoryBits() & categoryMaskBits) != 0) { - - OverlappingPair* pair; - const Pair<uint, uint> pairID = OverlappingPair::computeID(bodyProxyShape->getBroadPhaseId(), proxyShape->getBroadPhaseId()); - - // Try to retrieve a corresponding copy of the overlapping pair (if it exists) - auto itPair = overlappingPairs.find(pairID); - - // If a copy of the overlapping pair does not exist yet - if (itPair == overlappingPairs.end()) { - - // Create a temporary overlapping pair - pair = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(OverlappingPair))) - OverlappingPair(bodyProxyShape, proxyShape, mMemoryManager.getPoolAllocator(), - mMemoryManager.getPoolAllocator(), mWorld->mConfig); - - overlappingPairs.add(Pair<Pair<uint, uint>, OverlappingPair*>(pairID, pair)); - } - else { // If a temporary copy of this overlapping pair already exists - - // Retrieve the existing copy of the overlapping pair - pair = itPair->second; - } - - // Compute the middle-phase collision detection between the two shapes - computeMiddlePhaseForProxyShapes(pair, narrowPhaseInput); - } - } - } - } - } - - // Test narrow-phase collision - testNarrowPhaseCollision(narrowPhaseInput, false, true, mMemoryManager.getPoolAllocator()); - - // Process the potential contacts - processAllPotentialContacts(narrowPhaseInput, false); - - // Reduce the number of contact points in the manifolds - reduceContactManifolds(overlappingPairs); - - // For each overlapping pair - for (auto it = overlappingPairs.begin(); it != overlappingPairs.end(); ++it) { - - OverlappingPair* pair = it->second; - - if (pair->hasContacts()) { - - // Report the contacts to the user - CollisionCallback::CollisionCallbackInfo collisionInfo(pair, mMemoryManager); - callback->notifyContact(collisionInfo); - } - - // Destroy the temporary overlapping pair - pair->~OverlappingPair(); - mMemoryManager.release(MemoryManager::AllocationType::Pool, pair, sizeof(OverlappingPair)); - } -} - -// Test and report collisions between all shapes of the world -void CollisionDetection::testCollision(CollisionCallback* callback) { - - assert(callback != nullptr); // Compute the broad-phase collision detection computeBroadPhase(); + // Compute the middle-phase collision detection + computeMiddlePhase(mOverlappingPairs, narrowPhaseInput); + + // Compute the narrow-phase collision detection and report overlapping shapes + computeNarrowPhaseOverlapSnapshot(narrowPhaseInput, &callback); +} + +// Report all the bodies that overlap (collide) with the body in parameter +void CollisionDetection::testOverlap(CollisionBody* body, OverlapCallback& callback) { + NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); + + // Compute the broad-phase collision detection + computeBroadPhase(); + + // Filter the overlapping pairs to get only the ones with the selected body involved OverlappingPairMap overlappingPairs(mMemoryManager.getPoolAllocator()); + filterOverlappingPairs(body->getEntity(), overlappingPairs); + + if (overlappingPairs.size() > 0) { + + // Compute the middle-phase collision detection + computeMiddlePhase(overlappingPairs, narrowPhaseInput); + + // Compute the narrow-phase collision detection + computeNarrowPhaseOverlapSnapshot(narrowPhaseInput, &callback); + } +} + +// Test collision and report contacts between two bodies. +void CollisionDetection::testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback) { + + NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); + + // Compute the broad-phase collision detection + computeBroadPhase(); + + // Filter the overlapping pairs to get only the ones with the selected body involved + OverlappingPairMap overlappingPairs(mMemoryManager.getPoolAllocator()); + filterOverlappingPairs(body1->getEntity(), body2->getEntity(), overlappingPairs); + + if (overlappingPairs.size() > 0) { + + // Compute the middle-phase collision detection + computeMiddlePhase(overlappingPairs, narrowPhaseInput); + + // Compute the narrow-phase collision detection and report contacts + computeNarrowPhaseCollisionSnapshot(narrowPhaseInput, callback); + } +} + +// Test collision and report all the contacts involving the body in parameter +void CollisionDetection::testCollision(CollisionBody* body, CollisionCallback& callback) { + + NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); + + // Compute the broad-phase collision detection + computeBroadPhase(); + + // Filter the overlapping pairs to get only the ones with the selected body involved + OverlappingPairMap overlappingPairs(mMemoryManager.getPoolAllocator()); + filterOverlappingPairs(body->getEntity(), overlappingPairs); + + if (overlappingPairs.size() > 0) { + + // Compute the middle-phase collision detection + computeMiddlePhase(overlappingPairs, narrowPhaseInput); + + // Compute the narrow-phase collision detection and report contacts + computeNarrowPhaseCollisionSnapshot(narrowPhaseInput, callback); + } +} + +// Test collision and report contacts between each colliding bodies in the world +void CollisionDetection::testCollision(CollisionCallback& callback) { + + NarrowPhaseInput narrowPhaseInput(mMemoryManager.getPoolAllocator()); + + // Compute the broad-phase collision detection + computeBroadPhase(); + + // Compute the middle-phase collision detection + computeMiddlePhase(mOverlappingPairs, narrowPhaseInput); + + // Compute the narrow-phase collision detection and report contacts + computeNarrowPhaseCollisionSnapshot(narrowPhaseInput, callback); +} + +// Filter the overlapping pairs to keep only the pairs where a given body is involved +void CollisionDetection::filterOverlappingPairs(Entity bodyEntity, OverlappingPairMap& outFilteredPairs) const { // For each possible collision pair of bodies for (auto it = mOverlappingPairs.begin(); it != mOverlappingPairs.end(); ++it) { - OverlappingPair* originalPair = it->second; + OverlappingPair* pair = it->second; - OverlappingPair* pair; - const Pair<uint, uint> pairID = OverlappingPair::computeID(originalPair->getShape1()->getBroadPhaseId(), originalPair->getShape2()->getBroadPhaseId()); + if (pair->getShape1()->getBody()->getEntity() == bodyEntity || pair->getShape2()->getBody()->getEntity() == bodyEntity) { - // Try to retrieve a corresponding copy of the overlapping pair (if it exists) - auto itPair = overlappingPairs.find(pairID); - - // If a copy of the overlapping pair does not exist yet - if (itPair == overlappingPairs.end()) { - - // Create a temporary overlapping pair - pair = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(OverlappingPair))) - OverlappingPair(originalPair->getShape1(), originalPair->getShape2(), mMemoryManager.getPoolAllocator(), - mMemoryManager.getPoolAllocator(), mWorld->mConfig); - - overlappingPairs.add(Pair<Pair<uint, uint>, OverlappingPair*>(pairID, pair)); - } - else { // If a temporary copy of this overlapping pair already exists - - // Retrieve the existing copy of the overlapping pair - pair = itPair->second; - } - - ProxyShape* shape1 = pair->getShape1(); - ProxyShape* shape2 = pair->getShape2(); - - // Check if the collision filtering allows collision between the two shapes and - // that the two shapes are still overlapping. - if (((shape1->getCollideWithMaskBits() & shape2->getCollisionCategoryBits()) != 0 && - (shape1->getCollisionCategoryBits() & shape2->getCollideWithMaskBits()) != 0) && - mBroadPhaseSystem.testOverlappingShapes(shape1, shape2)) { - - // Compute the middle-phase collision detection between the two shapes - computeMiddlePhaseForProxyShapes(pair, narrowPhaseInput); + outFilteredPairs.add(Pair<Pair<uint, uint>, OverlappingPair*>(it->first, pair)); } } - // Test narrow-phase collision - testNarrowPhaseCollision(narrowPhaseInput, false, true, mMemoryManager.getPoolAllocator()); +} - // Process the potential contacts - processAllPotentialContacts(narrowPhaseInput, false); +// Filter the overlapping pairs to keep only the pairs where two given bodies are involved +void CollisionDetection::filterOverlappingPairs(Entity body1Entity, Entity body2Entity, OverlappingPairMap& outFilteredPairs) const { - // Reduce the number of contact points in the manifolds - reduceContactManifolds(overlappingPairs); - - // For each overlapping pair - for (auto it = overlappingPairs.begin(); it != overlappingPairs.end(); ++it) { + // For each possible collision pair of bodies + for (auto it = mOverlappingPairs.begin(); it != mOverlappingPairs.end(); ++it) { OverlappingPair* pair = it->second; - if (pair->hasContacts()) { + if ((pair->getShape1()->getBody()->getEntity() == body1Entity && pair->getShape2()->getBody()->getEntity() == body2Entity) || + (pair->getShape1()->getBody()->getEntity() == body2Entity && pair->getShape2()->getBody()->getEntity() == body1Entity)) { - // Report the contacts to the user - CollisionCallback::CollisionCallbackInfo collisionInfo(pair, mMemoryManager); - callback->notifyContact(collisionInfo); + outFilteredPairs.add(Pair<Pair<uint, uint>, OverlappingPair*>(it->first, pair)); } - - // Destroy the temporary overlapping pair - pair->~OverlappingPair(); - mMemoryManager.release(MemoryManager::AllocationType::Pool, pair, sizeof(OverlappingPair)); } + } // Return the world event listener diff --git a/src/collision/CollisionDetection.h b/src/collision/CollisionDetection.h index 8c1104ef..e4b16417 100644 --- a/src/collision/CollisionDetection.h +++ b/src/collision/CollisionDetection.h @@ -30,6 +30,11 @@ #include "body/CollisionBody.h" #include "systems/BroadPhaseSystem.h" #include "collision/shapes/CollisionShape.h" +#include "collision/ContactPointInfo.h" +#include "constraint/ContactPoint.h" +#include "collision/ContactManifoldInfo.h" +#include "collision/ContactManifold.h" +#include "collision/ContactPair.h" #include "engine/OverlappingPair.h" #include "collision/narrowphase/NarrowPhaseInput.h" #include "collision/narrowphase/CollisionDispatch.h" @@ -64,6 +69,11 @@ class CollisionDetection { using OverlappingPairMap = Map<Pair<uint, uint>, OverlappingPair*>; + // -------------------- Constants -------------------- // + + /// Maximum number of contact points in a reduced contact manifold + const int8 MAX_CONTACT_POINTS_IN_MANIFOLD = 4; + // -------------------- Attributes -------------------- // /// Memory manager @@ -72,9 +82,6 @@ class CollisionDetection { /// Reference the proxy-shape components ProxyShapeComponents& mProxyShapesComponents; - /// Reference the transform components - TransformComponents& mTransformComponents; - /// Collision Detection Dispatch configuration CollisionDispatch mCollisionDispatch; @@ -96,6 +103,71 @@ class CollisionDetection { /// Narrow-phase collision detection input NarrowPhaseInput mNarrowPhaseInput; + /// List of the potential contact points + List<ContactPointInfo> mPotentialContactPoints; + + /// List of the potential contact manifolds + List<ContactManifoldInfo> mPotentialContactManifolds; + + /// First list of narrow-phase pair contacts + List<ContactPair> mContactPairs1; + + /// Second list of narrow-phase pair contacts + List<ContactPair> mContactPairs2; + + /// Pointer to the list of contact pairs of the previous frame (either mContactPairs1 or mContactPairs2) + List<ContactPair>* mPreviousContactPairs; + + /// Pointer to the list of contact pairs of the current frame (either mContactPairs1 or mContactPairs2) + List<ContactPair>* mCurrentContactPairs; + + /// First map of overlapping pair id to the index of the corresponding pair contact + Map<OverlappingPair::OverlappingPairId, uint> mMapPairIdToContactPairIndex1; + + /// Second map of overlapping pair id to the index of the corresponding pair contact + Map<OverlappingPair::OverlappingPairId, uint> mMapPairIdToContactPairIndex2; + + /// Pointer to the map of overlappingPairId to the index of contact pair of the previous frame + /// (either mMapPairIdToContactPairIndex1 or mMapPairIdToContactPairIndex2) + Map<OverlappingPair::OverlappingPairId, uint>* mPreviousMapPairIdToContactPairIndex; + + /// Pointer to the map of overlappingPairId to the index of contact pair of the current frame + /// (either mMapPairIdToContactPairIndex1 or mMapPairIdToContactPairIndex2) + Map<OverlappingPair::OverlappingPairId, uint>* mCurrentMapPairIdToContactPairIndex; + + /// List of the indices of the contact pairs (in mCurrentContacPairs array) with contact pairs of + /// same islands packed together linearly and contact pairs that are not part of islands at the end. + /// This is used when we create contact manifolds and contact points so that there are also packed + /// together linearly if they are part of the same island. + List<uint> mContactPairsIndicesOrderingForContacts; + + /// First list with the contact manifolds + List<ContactManifold> mContactManifolds1; + + /// Second list with the contact manifolds + List<ContactManifold> mContactManifolds2; + + /// Pointer to the list of contact manifolds from the previous frame (either mContactManifolds1 or mContactManifolds2) + List<ContactManifold>* mPreviousContactManifolds; + + /// Pointer to the list of contact manifolds from the current frame (either mContactManifolds1 or mContactManifolds2) + List<ContactManifold>* mCurrentContactManifolds; + + /// Second list of contact points (contact points from either the current frame of the previous frame) + List<ContactPoint> mContactPoints1; + + /// Second list of contact points (contact points from either the current frame of the previous frame) + List<ContactPoint> mContactPoints2; + + /// Pointer to the contact points of the previous frame (either mContactPoints1 or mContactPoints2) + List<ContactPoint>* mPreviousContactPoints; + + /// Pointer to the contact points of the current frame (either mContactPoints1 or mContactPoints2) + List<ContactPoint>* mCurrentContactPoints; + + /// Map a body entity to the list of contact pairs in which it is involved + Map<Entity, List<uint>> mMapBodyToContactPairs; + #ifdef IS_PROFILING_ACTIVE /// Pointer to the profiler @@ -109,48 +181,89 @@ class CollisionDetection { void computeBroadPhase(); /// Compute the middle-phase collision detection - void computeMiddlePhase(); + void computeMiddlePhase(OverlappingPairMap& overlappingPairs, NarrowPhaseInput& narrowPhaseInput); /// Compute the narrow-phase collision detection void computeNarrowPhase(); + /// Compute the narrow-phase collision detection for the testOverlap() methods. + bool computeNarrowPhaseOverlapSnapshot(NarrowPhaseInput& narrowPhaseInput, OverlapCallback* callback); + + /// Compute the narrow-phase collision detection for the testCollision() methods + bool computeNarrowPhaseCollisionSnapshot(NarrowPhaseInput& narrowPhaseInput, CollisionCallback& callback); + + /// Process the potential contacts after narrow-phase collision detection + void computeSnapshotContactPairs(NarrowPhaseInput& narrowPhaseInput, List<Pair<Entity, Entity>>& overlapPairs) const; + + /// Convert the potential contact into actual contacts + void computeSnapshotContactPairs(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, List<Pair<Entity, Entity>>& overlapPairs) const; + /// Take a list of overlapping nodes in the broad-phase and create new overlapping pairs if necessary - void updateOverlappingPairs(List<Pair<int, int> >& overlappingNodes); + void updateOverlappingPairs(const List<Pair<int, int>>& overlappingNodes); + + /// Remove pairs that are not overlapping anymore + void removeNonOverlappingPairs(); /// Execute the narrow-phase collision detection algorithm on batches - bool testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool stopFirstContactFound, - bool reportContacts, MemoryAllocator& allocator); - - /// Add a contact manifold to the linked list of contact manifolds of the two bodies - /// involved in the corresponding contact. - void addContactManifoldToBody(OverlappingPair* pair); - - /// Add all the contact manifold of colliding pairs to their bodies - void addAllContactManifoldsToBodies(); + bool testNarrowPhaseCollision(NarrowPhaseInput& narrowPhaseInput, bool reportContacts, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& allocator); /// Compute the concave vs convex middle-phase algorithm for a given pair of bodies void computeConvexVsConcaveMiddlePhase(OverlappingPair* pair, MemoryAllocator& allocator, NarrowPhaseInput& narrowPhaseInput); - /// Compute the middle-phase collision detection between two proxy shapes - void computeMiddlePhaseForProxyShapes(OverlappingPair* pair, NarrowPhaseInput& outNarrowPhaseInput); + /// Swap the previous and current contacts lists + void swapPreviousAndCurrentContacts(); /// Convert the potential contact into actual contacts void processPotentialContacts(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, - bool updateLastFrameInfo); + bool updateLastFrameInfo, List<ContactPointInfo>& potentialContactPoints, + Map<OverlappingPair::OverlappingPairId, uint>* mapPairIdToContactPairIndex, + List<ContactManifoldInfo>& potentialContactManifolds, List<ContactPair>* contactPairs, + Map<Entity, List<uint>>& mapBodyToContactPairs); /// Process the potential contacts after narrow-phase collision detection - void processAllPotentialContacts(NarrowPhaseInput& narrowPhaseInput, bool updateLastFrameInfo); + void processAllPotentialContacts(NarrowPhaseInput& narrowPhaseInput, bool updateLastFrameInfo, List<ContactPointInfo>& potentialContactPoints, + Map<OverlappingPair::OverlappingPairId, uint>* mapPairIdToContactPairIndex, + List<ContactManifoldInfo> &potentialContactManifolds, List<ContactPair>* contactPairs, + Map<Entity, List<uint>>& mapBodyToContactPairs); - /// Clear the obsolete manifolds and contact points and reduce the number of contacts points of the remaining manifolds - void reduceContactManifolds(const OverlappingPairMap& overlappingPairs); + /// Reduce the potential contact manifolds and contact points of the overlapping pair contacts + void reducePotentialContactManifolds(List<ContactPair>* contactPairs, List<ContactManifoldInfo>& potentialContactManifolds, + const List<ContactPointInfo>& potentialContactPoints) const; - /// Report contacts for all the colliding overlapping pairs - void reportAllContacts(); + /// Create the actual contact manifolds and contacts points (from potential contacts) for a given contact pair + void createContacts(); + + /// Create the actual contact manifolds and contacts points for testCollision() methods + void createSnapshotContacts(List<ContactPair>& contactPairs, List<ContactManifold> &contactManifolds, + List<ContactPoint>& contactPoints, + List<ContactManifoldInfo>& potentialContactManifolds, + List<ContactPointInfo>& potentialContactPoints); + + /// Initialize the current contacts with the contacts from the previous frame (for warmstarting) + void initContactsWithPreviousOnes(); + + /// Reduce the number of contact points of a potential contact manifold + void reduceContactPoints(ContactManifoldInfo& manifold, const Transform& shape1ToWorldTransform, + const List<ContactPointInfo>& potentialContactPoints) const; + + /// Report contacts + void reportContacts(CollisionCallback& callback, List<ContactPair>* contactPairs, + List<ContactManifold>* manifolds, List<ContactPoint>* contactPoints); + + /// Return the largest depth of all the contact points of a potential manifold + decimal computePotentialManifoldLargestContactDepth(const ContactManifoldInfo& manifold, + const List<ContactPointInfo>& potentialContactPoints) const; /// Process the potential contacts where one collion is a concave shape void processSmoothMeshContacts(OverlappingPair* pair); + /// Filter the overlapping pairs to keep only the pairs where a given body is involved + void filterOverlappingPairs(Entity bodyEntity, OverlappingPairMap& outFilteredPairs) const; + + /// Filter the overlapping pairs to keep only the pairs where two given bodies are involved + void filterOverlappingPairs(Entity body1Entity, Entity body2Entity, OverlappingPairMap& outFilteredPairs) const; + public : // -------------------- Methods -------------------- // @@ -193,6 +306,9 @@ class CollisionDetection { /// Ask for a collision shape to be tested again during broad-phase. void askForBroadPhaseCollisionCheck(ProxyShape* shape); + /// Report contacts + void reportContacts(); + /// Compute the collision detection void computeCollisionDetection(); @@ -200,23 +316,23 @@ class CollisionDetection { void raycast(RaycastCallback* raycastCallback, const Ray& ray, unsigned short raycastWithCategoryMaskBits) const; - /// Report all the bodies that overlap with the aabb in parameter - void testAABBOverlap(const AABB& aabb, OverlapCallback* overlapCallback, unsigned short categoryMaskBits = 0xFFFF); - - /// Return true if two bodies overlap + /// Return true if two bodies (collide) overlap bool testOverlap(CollisionBody* body1, CollisionBody* body2); - /// Report all the bodies that overlap with the body in parameter - void testOverlap(CollisionBody* body, OverlapCallback* overlapCallback, unsigned short categoryMaskBits = 0xFFFF); + /// Report all the bodies that overlap (collide) with the body in parameter + void testOverlap(CollisionBody* body, OverlapCallback& callback); - /// Test and report collisions between two bodies - void testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback* callback); + /// Report all the bodies that overlap (collide) in the world + void testOverlap(OverlapCallback& overlapCallback); - /// Test and report collisions between a body and all the others bodies of the world - void testCollision(CollisionBody* body, CollisionCallback* callback, unsigned short categoryMaskBits = 0xFFFF); + /// Test collision and report contacts between two bodies. + void testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback); - /// Test and report collisions between all shapes of the world - void testCollision(CollisionCallback* callback); + /// Test collision and report all the contacts involving the body in parameter + void testCollision(CollisionBody* body, CollisionCallback& callback); + + /// Test collision and report contacts between each colliding bodies in the world + void testCollision(CollisionCallback& callback); /// Return a reference to the memory manager MemoryManager& getMemoryManager() const; diff --git a/src/collision/ContactManifold.cpp b/src/collision/ContactManifold.cpp index 4e80a625..05232626 100644 --- a/src/collision/ContactManifold.cpp +++ b/src/collision/ContactManifold.cpp @@ -26,364 +26,23 @@ // Libraries #include "ContactManifold.h" #include "constraint/ContactPoint.h" +#include "collision/ContactManifoldInfo.h" using namespace reactphysics3d; // Constructor -ContactManifold::ContactManifold(ProxyShape* shape1, ProxyShape* shape2, - MemoryAllocator& memoryAllocator, const WorldSettings& worldSettings) - : mShape1(shape1), mShape2(shape2), mContactPoints(nullptr), - mNbContactPoints(0), mFrictionImpulse1(0.0), mFrictionImpulse2(0.0), - mFrictionTwistImpulse(0.0), mIsAlreadyInIsland(false), - mMemoryAllocator(memoryAllocator), mNext(nullptr), mPrevious(nullptr), mIsObsolete(false), - mWorldSettings(worldSettings) { - +ContactManifold::ContactManifold(Entity bodyEntity1, Entity bodyEntity2, Entity proxyShapeEntity1, Entity proxyShapeEntity2, + uint contactPointsIndex, int8 nbContactPoints) + :mContactPointsIndex(0), bodyEntity1(bodyEntity1), bodyEntity2(bodyEntity2), + proxyShapeEntity1(proxyShapeEntity1), proxyShapeEntity2(proxyShapeEntity2), mFrictionImpulse1(0), mFrictionImpulse2(0), + mFrictionTwistImpulse(0), mIsAlreadyInIsland(false) { + + + mContactPointsIndex = contactPointsIndex; + mNbContactPoints = nbContactPoints; } // Destructor ContactManifold::~ContactManifold() { - // Delete all the contact points - ContactPoint* contactPoint = mContactPoints; - while(contactPoint != nullptr) { - - ContactPoint* nextContactPoint = contactPoint->getNext(); - - // Delete the contact point - contactPoint->~ContactPoint(); - mMemoryAllocator.release(contactPoint, sizeof(ContactPoint)); - - contactPoint = nextContactPoint; - } -} - -// Remove a contact point -void ContactManifold::removeContactPoint(ContactPoint* contactPoint) { - - assert(mNbContactPoints > 0); - assert(mContactPoints != nullptr); - assert(contactPoint != nullptr); - - ContactPoint* previous = contactPoint->getPrevious(); - ContactPoint* next = contactPoint->getNext(); - - if (previous != nullptr) { - previous->setNext(next); - } - else { - mContactPoints = next; - } - - if (next != nullptr) { - next->setPrevious(previous); - } - - // Delete the contact point - contactPoint->~ContactPoint(); - mMemoryAllocator.release(contactPoint, sizeof(ContactPoint)); - - mNbContactPoints--; - assert(mNbContactPoints >= 0); -} - -// Return the largest depth of all the contact points -decimal ContactManifold::getLargestContactDepth() const { - decimal largestDepth = 0.0f; - - assert(mNbContactPoints > 0); - - ContactPoint* contactPoint = mContactPoints; - while(contactPoint != nullptr){ - decimal depth = contactPoint->getPenetrationDepth(); - if (depth > largestDepth) { - largestDepth = depth; - } - - contactPoint = contactPoint->getNext(); - } - - return largestDepth; -} - -// Add a contact point -void ContactManifold::addContactPoint(const ContactPointInfo* contactPointInfo) { - - // For each contact point in the manifold - bool isSimilarPointFound = false; - ContactPoint* oldContactPoint = mContactPoints; - while (oldContactPoint != nullptr) { - - assert(oldContactPoint != nullptr); - - // If the new contact point is similar (very close) to the old contact point - if (oldContactPoint->isSimilarWithContactPoint(contactPointInfo)) { - - // Replace (update) the old contact point with the new one - oldContactPoint->update(contactPointInfo); - isSimilarPointFound = true; - break; - } - - oldContactPoint = oldContactPoint->getNext(); - } - - // If we have not found a similar contact point - if (!isSimilarPointFound) { - - // Create the new contact point - ContactPoint* contactPoint = new (mMemoryAllocator.allocate(sizeof(ContactPoint))) ContactPoint(contactPointInfo, mWorldSettings); - - // Add the new contact point into the manifold - contactPoint->setNext(mContactPoints); - contactPoint->setPrevious(nullptr); - if (mContactPoints != nullptr) { - mContactPoints->setPrevious(contactPoint); - } - - mContactPoints = contactPoint; - - mNbContactPoints++; - } - - // The old manifold is no longer obsolete - mIsObsolete = false; -} - -// Set to true to make the manifold obsolete -void ContactManifold::setIsObsolete(bool isObsolete, bool setContactPoints) { - mIsObsolete = isObsolete; - - if (setContactPoints) { - ContactPoint* contactPoint = mContactPoints; - while (contactPoint != nullptr) { - contactPoint->setIsObsolete(isObsolete); - - contactPoint = contactPoint->getNext(); - } - } -} - -// Clear the obsolete contact points -void ContactManifold::clearObsoleteContactPoints() { - - assert(mContactPoints != nullptr); - - // For each contact point of the manifold - ContactPoint* contactPoint = mContactPoints; - while (contactPoint != nullptr) { - - ContactPoint* nextContactPoint = contactPoint->getNext(); - - // If the contact point is obsolete - if (contactPoint->getIsObsolete()) { - - // Remove the contact point - removeContactPoint(contactPoint); - } - - contactPoint = nextContactPoint; - } - - assert(mNbContactPoints > 0); - assert(mContactPoints != nullptr); -} - -// Reduce the number of contact points of the currently computed manifold -// This is based on the technique described by Dirk Gregorius in his -// "Contacts Creation" GDC presentation. This method will reduce the number of -// contact points to a maximum of 4 points (but it can be less). -void ContactManifold::reduce(const Transform& shape1ToWorldTransform) { - - assert(mContactPoints != nullptr); - - // The following algorithm only works to reduce to a maximum of 4 contact points - assert(MAX_CONTACT_POINTS_IN_MANIFOLD == 4); - - // If there are too many contact points in the manifold - if (mNbContactPoints > MAX_CONTACT_POINTS_IN_MANIFOLD) { - - uint nbReducedPoints = 0; - - ContactPoint* pointsToKeep[MAX_CONTACT_POINTS_IN_MANIFOLD]; - for (int i=0; i<MAX_CONTACT_POINTS_IN_MANIFOLD; i++) { - pointsToKeep[i] = nullptr; - } - - // Compute the initial contact point we need to keep. - // The first point we keep is always the point in a given - // constant direction (in order to always have same contact points - // between frames for better stability) - - const Transform worldToShape1Transform = shape1ToWorldTransform.getInverse(); - - // Compute the contact normal of the manifold (we use the first contact point) - // in the local-space of the first collision shape - const Vector3 contactNormalShape1Space = worldToShape1Transform.getOrientation() * mContactPoints->getNormal(); - - // Compute a search direction - const Vector3 searchDirection(1, 1, 1); - ContactPoint* element = mContactPoints; - pointsToKeep[0] = element; - decimal maxDotProduct = searchDirection.dot(element->getLocalPointOnShape1()); - element = element->getNext(); - nbReducedPoints = 1; - while(element != nullptr) { - - decimal dotProduct = searchDirection.dot(element->getLocalPointOnShape1()); - if (dotProduct > maxDotProduct) { - maxDotProduct = dotProduct; - pointsToKeep[0] = element; - } - element = element->getNext(); - } - assert(pointsToKeep[0] != nullptr); - assert(nbReducedPoints == 1); - - // Compute the second contact point we need to keep. - // The second point we keep is the one farthest away from the first point. - - decimal maxDistance = decimal(0.0); - element = mContactPoints; - while(element != nullptr) { - - if (element == pointsToKeep[0]) { - element = element->getNext(); - continue; - } - - decimal distance = (pointsToKeep[0]->getLocalPointOnShape1() - element->getLocalPointOnShape1()).lengthSquare(); - if (distance >= maxDistance) { - maxDistance = distance; - pointsToKeep[1] = element; - nbReducedPoints = 2; - } - element = element->getNext(); - } - assert(pointsToKeep[1] != nullptr); - assert(nbReducedPoints == 2); - - // Compute the third contact point we need to keep. - // The second point is the one producing the triangle with the larger area - // with first and second point. - - // We compute the most positive or most negative triangle area (depending on winding) - ContactPoint* thirdPointMaxArea = nullptr; - ContactPoint* thirdPointMinArea = nullptr; - decimal minArea = decimal(0.0); - decimal maxArea = decimal(0.0); - bool isPreviousAreaPositive = true; - element = mContactPoints; - while(element != nullptr) { - - if (element == pointsToKeep[0] || element == pointsToKeep[1]) { - element = element->getNext(); - continue; - } - - const Vector3 newToFirst = pointsToKeep[0]->getLocalPointOnShape1() - element->getLocalPointOnShape1(); - const Vector3 newToSecond = pointsToKeep[1]->getLocalPointOnShape1() - element->getLocalPointOnShape1(); - - // Compute the triangle area - decimal area = newToFirst.cross(newToSecond).dot(contactNormalShape1Space); - - if (area >= maxArea) { - maxArea = area; - thirdPointMaxArea = element; - } - if (area <= minArea) { - minArea = area; - thirdPointMinArea = element; - } - element = element->getNext(); - } - assert(minArea <= decimal(0.0)); - assert(maxArea >= decimal(0.0)); - if (maxArea > (-minArea)) { - isPreviousAreaPositive = true; - pointsToKeep[2] = thirdPointMaxArea; - nbReducedPoints = 3; - } - else { - isPreviousAreaPositive = false; - pointsToKeep[2] = thirdPointMinArea; - nbReducedPoints = 3; - } - - // Compute the 4th point by choosing the triangle that add the most - // triangle area to the previous triangle and has opposite sign area (opposite winding) - - decimal largestArea = decimal(0.0); // Largest area (positive or negative) - element = mContactPoints; - - if (nbReducedPoints == 3) { - - // For each remaining point - while(element != nullptr) { - - if (element == pointsToKeep[0] || element == pointsToKeep[1] || element == pointsToKeep[2]) { - element = element->getNext(); - continue; - } - - // For each edge of the triangle made by the first three points - for (uint i=0; i<3; i++) { - - uint edgeVertex1Index = i; - uint edgeVertex2Index = i < 2 ? i + 1 : 0; - - const Vector3 newToFirst = pointsToKeep[edgeVertex1Index]->getLocalPointOnShape1() - element->getLocalPointOnShape1(); - const Vector3 newToSecond = pointsToKeep[edgeVertex2Index]->getLocalPointOnShape1() - element->getLocalPointOnShape1(); - - // Compute the triangle area - decimal area = newToFirst.cross(newToSecond).dot(contactNormalShape1Space); - - // We are looking at the triangle with maximal area (positive or negative). - // If the previous area is positive, we are looking at negative area now. - // If the previous area is negative, we are looking at the positive area now. - if (isPreviousAreaPositive && area <= largestArea) { - largestArea = area; - pointsToKeep[3] = element; - nbReducedPoints = 4; - } - else if (!isPreviousAreaPositive && area >= largestArea) { - largestArea = area; - pointsToKeep[3] = element; - nbReducedPoints = 4; - } - } - - element = element->getNext(); - } - } - - // Delete the contact points we do not want to keep from the linked list - element = mContactPoints; - ContactPoint* previousElement = nullptr; - while(element != nullptr) { - - bool deletePoint = true; - - // Skip the points we want to keep - for (uint i=0; i<nbReducedPoints; i++) { - - if (element == pointsToKeep[i]) { - - previousElement = element; - element = element->getNext(); - deletePoint = false; - } - } - - if (deletePoint) { - - ContactPoint* contactPointToDelete = element; - element = element->getNext(); - - removeContactPoint(contactPointToDelete); - } - } - - assert(nbReducedPoints > 0 && nbReducedPoints <= 4); - mNbContactPoints = nbReducedPoints; - } } diff --git a/src/collision/ContactManifold.h b/src/collision/ContactManifold.h index 4b368660..e0c86f19 100644 --- a/src/collision/ContactManifold.h +++ b/src/collision/ContactManifold.h @@ -40,44 +40,6 @@ class CollisionBody; class ContactPoint; class PoolAllocator; -// Structure ContactManifoldListElement -/** - * This structure represents a single element of a linked list of contact manifolds - */ -struct ContactManifoldListElement { - - private: - - // -------------------- Attributes -------------------- // - - /// Pointer to a contact manifold with contact points - ContactManifold* mContactManifold; - - /// Next element of the list - ContactManifoldListElement* mNext; - - public: - - // -------------------- Methods -------------------- // - - /// Constructor - ContactManifoldListElement(ContactManifold* contactManifold, - ContactManifoldListElement* next) - :mContactManifold(contactManifold), mNext(next) { - - } - - /// Return the contact manifold - ContactManifold* getContactManifold() { - return mContactManifold; - } - - /// Return the next element in the linked-list - ContactManifoldListElement* getNext() { - return mNext; - } -}; - // Class ContactManifold /** * This class represents a set of contact points between two bodies that @@ -91,6 +53,7 @@ struct ContactManifoldListElement { */ class ContactManifold { + // TODO : Check if we can use public fields in this class (maybe this class is used by users directly) private: // -------------------- Constants -------------------- // @@ -100,14 +63,22 @@ class ContactManifold { // -------------------- Attributes -------------------- // - /// Pointer to the first proxy shape of the contact - ProxyShape* mShape1; + // TODO : For each of the attributes, check if we need to keep it or not - /// Pointer to the second proxy shape of the contact - ProxyShape* mShape2; + /// Index of the first contact point of the manifold in the list of contact points + uint mContactPointsIndex; - /// Contact points in the manifold - ContactPoint* mContactPoints; + /// Entity of the first body in contact + Entity bodyEntity1; + + /// Entity of the second body in contact + Entity bodyEntity2; + + /// Entity of the first proxy-shape in contact + Entity proxyShapeEntity1; + + /// Entity of the second proxy-shape in contact + Entity proxyShapeEntity2; /// Number of contacts in the cache int8 mNbContactPoints; @@ -133,44 +104,11 @@ class ContactManifold { /// True if the contact manifold has already been added into an island bool mIsAlreadyInIsland; - /// Reference to the memory allocator - MemoryAllocator& mMemoryAllocator; - - /// Pointer to the next contact manifold in the linked-list - ContactManifold* mNext; - - /// Pointer to the previous contact manifold in linked-list - ContactManifold* mPrevious; - - /// True if the contact manifold is obsolete - bool mIsObsolete; - - /// World settings - const WorldSettings& mWorldSettings; - // -------------------- Methods -------------------- // /// Return true if the contact manifold has already been added into an island bool isAlreadyInIsland() const; - /// Set the pointer to the next element in the linked-list - void setNext(ContactManifold* nextManifold); - - /// Return true if the manifold is obsolete - bool getIsObsolete() const; - - /// Set to true to make the manifold obsolete - void setIsObsolete(bool isObselete, bool setContactPoints); - - /// Clear the obsolete contact points - void clearObsoleteContactPoints(); - - /// Return the contact normal direction Id of the manifold - short getContactNormalId() const; - - /// Return the largest depth of all the contact points - decimal getLargestContactDepth() const; - /// set the first friction vector at the center of the contact manifold void setFrictionVector1(const Vector3& mFrictionVector1); @@ -183,24 +121,12 @@ class ContactManifold { /// Set the second friction accumulated impulse void setFrictionImpulse2(decimal frictionImpulse2); - /// Add a contact point - void addContactPoint(const ContactPointInfo* contactPointInfo); - - /// Reduce the number of contact points of the currently computed manifold - void reduce(const Transform& shape1ToWorldTransform); - - /// Remove a contact point - void removeContactPoint(ContactPoint* contactPoint); - /// Set the friction twist accumulated impulse void setFrictionTwistImpulse(decimal frictionTwistImpulse); /// Set the accumulated rolling resistance impulse void setRollingResistanceImpulse(const Vector3& rollingResistanceImpulse); - /// Set the pointer to the previous element in the linked-list - void setPrevious(ContactManifold* previousManifold); - /// Return the first friction vector at the center of the contact manifold const Vector3& getFrictionVector1() const; @@ -221,42 +147,21 @@ class ContactManifold { // -------------------- Methods -------------------- // /// Constructor - ContactManifold(ProxyShape* shape1, ProxyShape* shape2, MemoryAllocator& memoryAllocator, - const WorldSettings& worldSettings); + ContactManifold(Entity bodyEntity1, Entity bodyEntity2, Entity proxyShapeEntity1, Entity proxyShapeEntity2, + uint contactPointsIndex, int8 nbContactPoints); /// Destructor ~ContactManifold(); - /// Deleted copy-constructor - ContactManifold(const ContactManifold& contactManifold) = delete; + /// Copy-constructor + ContactManifold(const ContactManifold& contactManifold) = default; - /// Deleted assignment operator - ContactManifold& operator=(const ContactManifold& contactManifold) = delete; - - /// Return a pointer to the first proxy shape of the contact - ProxyShape* getShape1() const; - - /// Return a pointer to the second proxy shape of the contact - ProxyShape* getShape2() const; - - /// Return a pointer to the first body of the contact manifold - CollisionBody* getBody1() const; - - /// Return a pointer to the second body of the contact manifold - CollisionBody* getBody2() const; + /// Assignment operator + ContactManifold& operator=(const ContactManifold& contactManifold) = default; /// Return the number of contact points in the manifold int8 getNbContactPoints() const; - /// Return a pointer to the first contact point of the manifold - ContactPoint* getContactPoints() const; - - /// Return a pointer to the previous element in the linked-list - ContactManifold* getPrevious() const; - - /// Return a pointer to the next element in the linked-list - ContactManifold* getNext() const; - // -------------------- Friendship -------------------- // friend class DynamicsWorld; @@ -264,28 +169,9 @@ class ContactManifold { friend class CollisionBody; friend class ContactManifoldSet; friend class ContactSolver; + friend class CollisionDetection; }; -// Return a pointer to the first proxy shape of the contact -inline ProxyShape* ContactManifold::getShape1() const { - return mShape1; -} - -// Return a pointer to the second proxy shape of the contact -inline ProxyShape* ContactManifold::getShape2() const { - return mShape2; -} - -// Return a pointer to the first body of the contact manifold -inline CollisionBody* ContactManifold::getBody1() const { - return mShape1->getBody(); -} - -// Return a pointer to the second body of the contact manifold -inline CollisionBody* ContactManifold::getBody2() const { - return mShape2->getBody(); -} - // Return the number of contact points in the manifold inline int8 ContactManifold::getNbContactPoints() const { return mNbContactPoints; @@ -346,41 +232,11 @@ inline void ContactManifold::setRollingResistanceImpulse(const Vector3& rollingR mRollingResistanceImpulse = rollingResistanceImpulse; } -// Return a pointer to the first contact point of the manifold -inline ContactPoint* ContactManifold::getContactPoints() const { - return mContactPoints; -} - // Return true if the contact manifold has already been added into an island inline bool ContactManifold::isAlreadyInIsland() const { return mIsAlreadyInIsland; } -// Return a pointer to the previous element in the linked-list -inline ContactManifold* ContactManifold::getPrevious() const { - return mPrevious; -} - -// Set the pointer to the previous element in the linked-list -inline void ContactManifold::setPrevious(ContactManifold* previousManifold) { - mPrevious = previousManifold; -} - -// Return a pointer to the next element in the linked-list -inline ContactManifold* ContactManifold::getNext() const { - return mNext; -} - -// Set the pointer to the next element in the linked-list -inline void ContactManifold::setNext(ContactManifold* nextManifold) { - mNext = nextManifold; -} - -// Return true if the manifold is obsolete -inline bool ContactManifold::getIsObsolete() const { - return mIsObsolete; -} - } #endif diff --git a/src/collision/ContactManifoldInfo.h b/src/collision/ContactManifoldInfo.h new file mode 100644 index 00000000..9d3fed31 --- /dev/null +++ b/src/collision/ContactManifoldInfo.h @@ -0,0 +1,67 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2018 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. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_CONTACT_MANIFOLD_INFO_H +#define REACTPHYSICS3D_CONTACT_MANIFOLD_INFO_H + +// Libraries +#include "mathematics/mathematics.h" +#include "configuration.h" +#include "engine/OverlappingPair.h" + +/// ReactPhysics3D namespace +namespace reactphysics3d { + +// Structure ContactManifoldInfo +/** + * This structure contains informations about a collision contact + * manifold computed during the narrow-phase collision detection. + */ +struct ContactManifoldInfo { + + public: + + // -------------------- Attributes -------------------- // + + /// Indices of the contact points in the mPotentialContactPoints array + List<uint> potentialContactPointsIndices; + + /// Overlapping pair id + OverlappingPair::OverlappingPairId pairId; + + // -------------------- Methods -------------------- // + + /// Constructor + ContactManifoldInfo(OverlappingPair::OverlappingPairId pairId, MemoryAllocator& allocator) + : potentialContactPointsIndices(allocator), pairId(pairId) { + + } + + +}; + +} + +#endif diff --git a/src/collision/ContactManifoldSet.cpp b/src/collision/ContactManifoldSet.cpp deleted file mode 100644 index a842fe64..00000000 --- a/src/collision/ContactManifoldSet.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/******************************************************************************** -* ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2018 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 "ContactManifoldSet.h" -#include "narrowphase/NarrowPhaseInfoBatch.h" -#include "constraint/ContactPoint.h" -#include "ProxyShape.h" -#include "collision/ContactManifold.h" - -using namespace reactphysics3d; - -// Constructor -ContactManifoldSet::ContactManifoldSet(ProxyShape* shape1, ProxyShape* shape2, - MemoryAllocator& memoryAllocator, const WorldSettings& worldSettings) - : mNbManifolds(0), mShape1(shape1), - mShape2(shape2), mMemoryAllocator(memoryAllocator), mManifolds(nullptr), mWorldSettings(worldSettings) { - - // Compute the maximum number of manifolds allowed between the two shapes - mNbMaxManifolds = computeNbMaxContactManifolds(shape1->getCollisionShape(), shape2->getCollisionShape()); -} - -// Destructor -ContactManifoldSet::~ContactManifoldSet() { - - // Clear all the contact manifolds - clear(); -} - -void ContactManifoldSet::addContactPoints(const NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex) { - - assert(narrowPhaseInfoBatch.contactPoints[batchIndex].size() > 0); - - // For each potential contact point to add - for (uint i=0; i < narrowPhaseInfoBatch.contactPoints[batchIndex].size(); i++) { - - ContactPointInfo* contactPoint = narrowPhaseInfoBatch.contactPoints[batchIndex][i]; - - // Look if the contact point correspond to an existing potential manifold - // (if the contact point normal is similar to the normal of an existing manifold) - ContactManifold* manifold = mManifolds; - bool similarManifoldFound = false; - while(manifold != nullptr) { - - // Get the first contact point - const ContactPoint* point = manifold->getContactPoints(); - assert(point != nullptr); - - // If we have found a corresponding manifold for the new contact point - // (a manifold with a similar contact normal direction) - if (point->getNormal().dot(contactPoint->normal) >= mWorldSettings.cosAngleSimilarContactManifold) { - - // Add the contact point to the manifold - manifold->addContactPoint(contactPoint); - - similarManifoldFound = true; - - break; - } - - manifold = manifold->getNext(); - } - - // If we have not found an existing manifold with a similar contact normal - if (!similarManifoldFound) { - - // Create a new contact manifold - ContactManifold* manifold = createManifold(); - - // Add the contact point to the manifold - manifold->addContactPoint(contactPoint); - } - } -} - -// Return the total number of contact points in the set of manifolds -int ContactManifoldSet::getTotalNbContactPoints() const { - int nbPoints = 0; - - ContactManifold* manifold = mManifolds; - while (manifold != nullptr) { - nbPoints += manifold->getNbContactPoints(); - - manifold = manifold->getNext(); - } - - return nbPoints; -} - -// Return the maximum number of contact manifolds allowed between to collision shapes -int ContactManifoldSet::computeNbMaxContactManifolds(const CollisionShape* shape1, const CollisionShape* shape2) { - - // If both shapes are convex - if (shape1->isConvex() && shape2->isConvex()) { - return mWorldSettings.nbMaxContactManifoldsConvexShape; - - } // If there is at least one concave shape - else { - return mWorldSettings.nbMaxContactManifoldsConcaveShape; - } -} - -// Remove a contact manifold that is the least optimal (smaller penetration depth) -void ContactManifoldSet::removeNonOptimalManifold() { - - assert(mNbManifolds > mNbMaxManifolds); - assert(mManifolds != nullptr); - - // Look for a manifold that is not new and with the smallest contact penetration depth. - // At the same time, we also look for a new manifold with the smallest contact penetration depth - // in case no old manifold exists. - ContactManifold* minDepthManifold = nullptr; - decimal minDepth = DECIMAL_LARGEST; - ContactManifold* manifold = mManifolds; - while (manifold != nullptr) { - - // Get the largest contact point penetration depth of the manifold - const decimal depth = manifold->getLargestContactDepth(); - - if (depth < minDepth) { - minDepth = depth; - minDepthManifold = manifold; - } - - manifold = manifold->getNext(); - } - - // Remove the non optimal manifold - assert(minDepthManifold != nullptr); - removeManifold(minDepthManifold); -} - -// Create a new contact manifold and add it to the set -ContactManifold* ContactManifoldSet::createManifold() { - - ContactManifold* manifold = new (mMemoryAllocator.allocate(sizeof(ContactManifold))) - ContactManifold(mShape1, mShape2, mMemoryAllocator, mWorldSettings); - manifold->setPrevious(nullptr); - manifold->setNext(mManifolds); - if (mManifolds != nullptr) { - mManifolds->setPrevious(manifold); - } - mManifolds = manifold; - - mNbManifolds++; - - return manifold; -} - -// Return the contact manifold with a similar contact normal. -// If no manifold has close enough contact normal, it returns nullptr -ContactManifold* ContactManifoldSet::selectManifoldWithSimilarNormal(const Vector3& contactNormal) const { - - ContactManifold* manifold = mManifolds; - - // Return the Id of the manifold with the same normal direction id (if exists) - while (manifold != nullptr) { - - // Get the first contact point of the current manifold - const ContactPoint* point = manifold->getContactPoints(); - assert(point != nullptr); - - // If the contact normal of the two manifolds are close enough - if (contactNormal.dot(point->getNormal()) >= mWorldSettings.cosAngleSimilarContactManifold) { - return manifold; - } - - manifold = manifold->getNext(); - } - - return nullptr; -} - -// Clear the contact manifold set -void ContactManifoldSet::clear() { - - ContactManifold* manifold = mManifolds; - while(manifold != nullptr) { - - ContactManifold* nextManifold = manifold->getNext(); - - // Delete the contact manifold - manifold->~ContactManifold(); - mMemoryAllocator.release(manifold, sizeof(ContactManifold)); - - manifold = nextManifold; - - mNbManifolds--; - } - - mManifolds = nullptr; - - assert(mNbManifolds == 0); -} - -// Remove a contact manifold from the set -void ContactManifoldSet::removeManifold(ContactManifold* manifold) { - - assert(mNbManifolds > 0); - assert(manifold != nullptr); - - ContactManifold* previous = manifold->getPrevious(); - ContactManifold* next = manifold->getNext(); - - if (previous != nullptr) { - previous->setNext(next); - } - else { - mManifolds = next; - } - - if (next != nullptr) { - next->setPrevious(previous); - } - - // Delete the contact manifold - manifold->~ContactManifold(); - mMemoryAllocator.release(manifold, sizeof(ContactManifold)); - mNbManifolds--; -} - -// Make all the contact manifolds and contact points obsolete -void ContactManifoldSet::makeContactsObsolete() { - - ContactManifold* manifold = mManifolds; - while (manifold != nullptr) { - - manifold->setIsObsolete(true, true); - - manifold = manifold->getNext(); - } -} - -// Clear the obsolete contact manifolds and contact points -void ContactManifoldSet::clearObsoleteManifoldsAndContactPoints() { - - ContactManifold* manifold = mManifolds; - while (manifold != nullptr) { - - // Get the next manifold in the linked-list - ContactManifold* nextManifold = manifold->getNext(); - - // If the manifold is obsolete - if (manifold->getIsObsolete()) { - - // Delete the contact manifold - removeManifold(manifold); - } - else { - - // Clear the obsolete contact points of the manifold - manifold->clearObsoleteContactPoints(); - } - - manifold = nextManifold; - } -} - -// Remove some contact manifolds and contact points if there are too many of them -void ContactManifoldSet::reduce() { - - // Remove non optimal contact manifold while there are too many manifolds in the set - while (mNbManifolds > mNbMaxManifolds) { - removeNonOptimalManifold(); - } - - // Reduce all the contact manifolds in case they have too many contact points - ContactManifold* manifold = mManifolds; - while (manifold != nullptr) { - manifold->reduce(mShape1->getLocalToWorldTransform()); - manifold = manifold->getNext(); - } -} diff --git a/src/collision/ContactManifoldSet.h b/src/collision/ContactManifoldSet.h deleted file mode 100644 index 8ba5017d..00000000 --- a/src/collision/ContactManifoldSet.h +++ /dev/null @@ -1,166 +0,0 @@ -/******************************************************************************** -* ReactPhysics3D physics library, http://www.reactphysics3d.com * -* Copyright (c) 2010-2018 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. * -* * -********************************************************************************/ - -#ifndef REACTPHYSICS3D_CONTACT_MANIFOLD_SET_H -#define REACTPHYSICS3D_CONTACT_MANIFOLD_SET_H - -// Libraries -#include "configuration.h" - -namespace reactphysics3d { - -// Declarations -class ContactManifold; -class ContactManifoldInfo; -class ProxyShape; -class MemoryAllocator; -struct WorldSettings; -struct NarrowPhaseInfoBatch; -struct Vector3; -class CollisionShape; -class Transform; - - -// Constants -const int MAX_MANIFOLDS_IN_CONTACT_MANIFOLD_SET = 3; // Maximum number of contact manifolds in the set -const int CONTACT_CUBEMAP_FACE_NB_SUBDIVISIONS = 3; // N Number for the N x N subdivisions of the cubemap - -// Class ContactManifoldSet -/** - * This class represents a set of one or several contact manifolds. Typically a - * convex/convex collision will have a set with a single manifold and a convex-concave - * collision can have more than one manifolds. Note that a contact manifold can - * contains several contact points. - */ -class ContactManifoldSet { - - private: - - // -------------------- Attributes -------------------- // - - /// Maximum number of contact manifolds in the set - int mNbMaxManifolds; - - /// Current number of contact manifolds in the set - int mNbManifolds; - - /// Pointer to the first proxy shape of the contact - ProxyShape* mShape1; - - /// Pointer to the second proxy shape of the contact - ProxyShape* mShape2; - - /// Reference to the memory allocator for the contact manifolds - MemoryAllocator& mMemoryAllocator; - - /// Contact manifolds of the set - ContactManifold* mManifolds; - - /// World settings - const WorldSettings& mWorldSettings; - - // -------------------- Methods -------------------- // - - /// Create a new contact manifold and add it to the set - ContactManifold* createManifold(); - - // Return the contact manifold with a similar contact normal. - ContactManifold* selectManifoldWithSimilarNormal(const Vector3& contactNormal) const; - - /// Remove a contact manifold that is the least optimal (smaller penetration depth) - void removeNonOptimalManifold(); - - /// Return the maximum number of contact manifolds allowed between to collision shapes - int computeNbMaxContactManifolds(const CollisionShape* shape1, const CollisionShape* shape2); - - /// Clear the contact manifold set - void clear(); - - /// Delete a contact manifold - void removeManifold(ContactManifold* manifold); - - public: - - // -------------------- Methods -------------------- // - - /// Constructor - ContactManifoldSet(ProxyShape* shape1, ProxyShape* shape2, - MemoryAllocator& memoryAllocator, const WorldSettings& worldSettings); - - /// Destructor - ~ContactManifoldSet(); - - /// Add the contact points from the narrow phase - void addContactPoints(const NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex); - - /// Return the first proxy shape - ProxyShape* getShape1() const; - - /// Return the second proxy shape - ProxyShape* getShape2() const; - - /// Return the number of manifolds in the set - int getNbContactManifolds() const; - - /// Return a pointer to the first element of the linked-list of contact manifolds - ContactManifold* getContactManifolds() const; - - /// Make all the contact manifolds and contact points obsolete - void makeContactsObsolete(); - - /// Return the total number of contact points in the set of manifolds - int getTotalNbContactPoints() const; - - /// Clear the obsolete contact manifolds and contact points - void clearObsoleteManifoldsAndContactPoints(); - - // Remove some contact manifolds and contact points if there are too many of them - void reduce(); -}; - -// Return the first proxy shape -inline ProxyShape* ContactManifoldSet::getShape1() const { - return mShape1; -} - -// Return the second proxy shape -inline ProxyShape* ContactManifoldSet::getShape2() const { - return mShape2; -} - -// Return the number of manifolds in the set -inline int ContactManifoldSet::getNbContactManifolds() const { - return mNbManifolds; -} - -// Return a pointer to the first element of the linked-list of contact manifolds -inline ContactManifold* ContactManifoldSet::getContactManifolds() const { - return mManifolds; -} - -} - -#endif - diff --git a/src/collision/ContactPair.h b/src/collision/ContactPair.h new file mode 100644 index 00000000..d8fbf46c --- /dev/null +++ b/src/collision/ContactPair.h @@ -0,0 +1,98 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2018 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. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_OVERLAPPING_PAIR_CONTACT_H +#define REACTPHYSICS3D_OVERLAPPING_PAIR_CONTACT_H + +// Libraries +#include "mathematics/mathematics.h" +#include "configuration.h" +#include "engine/OverlappingPair.h" + +/// ReactPhysics3D namespace +namespace reactphysics3d { + +// Structure ContactPair +/** + * This structure represents a pair of shapes that are in contact during narrow-phase. + */ +struct ContactPair { + + public: + + // -------------------- Attributes -------------------- // + + /// Overlapping pair Id + OverlappingPair::OverlappingPairId pairId; + + /// Indices of the potential contact manifolds + List<uint> potentialContactManifoldsIndices; + + /// Entity of the first body of the contact + Entity body1Entity; + + /// Entity of the second body of the contact + Entity body2Entity; + + /// Entity of the first proxy-shape of the contact + Entity proxyShape1Entity; + + /// Entity of the second proxy-shape of the contact + Entity proxyShape2Entity; + + /// True if the manifold is already in an island + bool isAlreadyInIsland; + + /// Index of the contact pair in the array of pairs + uint contactPairIndex; + + /// Index of the first contact manifold in the array + uint contactManifoldsIndex; + + /// Number of contact manifolds + int8 nbContactManifolds; + + /// Index of the first contact point in the array of contact points + uint contactPointsIndex; + + /// Total number of contact points in all the manifolds of the contact pair + uint nbToTalContactPoints; + + // -------------------- Methods -------------------- // + + /// Constructor + ContactPair(OverlappingPair::OverlappingPairId pairId, Entity body1Entity, Entity body2Entity, Entity proxyShape1Entity, + Entity proxyShape2Entity, uint contactPairIndex, MemoryAllocator& allocator) + : pairId(pairId), potentialContactManifoldsIndices(allocator), body1Entity(body1Entity), body2Entity(body2Entity), + proxyShape1Entity(proxyShape1Entity), proxyShape2Entity(proxyShape2Entity), + isAlreadyInIsland(false), contactPairIndex(contactPairIndex), contactManifoldsIndex(0), nbContactManifolds(0), + contactPointsIndex(0), nbToTalContactPoints(0) { + + } +}; + +} + +#endif diff --git a/src/collision/ContactPointInfo.h b/src/collision/ContactPointInfo.h index c672e208..6e81783c 100644 --- a/src/collision/ContactPointInfo.h +++ b/src/collision/ContactPointInfo.h @@ -65,9 +65,11 @@ struct ContactPointInfo { /// Contact point of body 2 in local space of body 2 Vector3 localPoint2; + // TODO : Remove this field /// Pointer to the next contact point info ContactPointInfo* next; + // TODO : Remove this field /// True if the contact point has already been inserted into a manifold bool isUsed; diff --git a/src/collision/OverlapCallback.cpp b/src/collision/OverlapCallback.cpp new file mode 100644 index 00000000..096f9421 --- /dev/null +++ b/src/collision/OverlapCallback.cpp @@ -0,0 +1,53 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2018 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 "collision/OverlapCallback.h" +#include "engine/CollisionWorld.h" + +// We want to use the ReactPhysics3D namespace +using namespace reactphysics3d; + +// Contact Pair Constructor +OverlapCallback::OverlapPair::OverlapPair(Pair<Entity, Entity>& overlapPair, CollisionWorld& world) + : mOverlapPair(overlapPair), mWorld(world) { + +} + +// Return a pointer to the first body in contact +CollisionBody* OverlapCallback::OverlapPair::getBody1() const { + return static_cast<CollisionBody*>(mWorld.mBodyComponents.getBody(mOverlapPair.first)); +} + +// Return a pointer to the second body in contact +CollisionBody* OverlapCallback::OverlapPair::getBody2() const { + return static_cast<CollisionBody*>(mWorld.mBodyComponents.getBody(mOverlapPair.second)); +} + +// CollisionCallbackData Constructor +OverlapCallback::CallbackData::CallbackData(List<Pair<Entity, Entity>>& overlapBodies, CollisionWorld& world) + :mOverlapBodies(overlapBodies), mWorld(world) { + +} diff --git a/src/collision/OverlapCallback.h b/src/collision/OverlapCallback.h index dd128394..c9e83250 100644 --- a/src/collision/OverlapCallback.h +++ b/src/collision/OverlapCallback.h @@ -26,32 +26,141 @@ #ifndef REACTPHYSICS3D_OVERLAP_CALLBACK_H #define REACTPHYSICS3D_OVERLAP_CALLBACK_H +// Libraries +#include "containers/List.h" + /// ReactPhysics3D namespace namespace reactphysics3d { // Declarations class CollisionBody; +class CollisionWorld; +class ProxyShape; +struct Entity; // Class OverlapCallback /** - * This class can be used to register a callback for collision overlap queries. - * You should implement your own class inherited from this one and implement - * the notifyOverlap() method. This method will called each time a contact - * point is reported. + * This class can be used to register a callback for collision overlap queries between bodies. + * You should implement your own class inherited from this one and implement the onOverlap() method. */ class OverlapCallback { public: + // Class OverlapPair + /** + * This class represents the contact between two proxy-shapes of the physics world. + */ + class OverlapPair { + + private: + + // -------------------- Attributes -------------------- // + + /// Pair of overlapping body entities + Pair<Entity, Entity>& mOverlapPair; + + /// Reference to the physics world + CollisionWorld& mWorld; + + // -------------------- Methods -------------------- // + + /// Constructor + OverlapPair(Pair<Entity, Entity>& overlapPair, CollisionWorld& world); + + public: + + // -------------------- Methods -------------------- // + + /// Copy constructor + OverlapPair(const OverlapPair& contactPair) = default; + + /// Assignment operator + OverlapPair& operator=(const OverlapPair& contactPair) = default; + + /// Destructor + ~OverlapPair() = default; + + /// Return a pointer to the first body in contact + CollisionBody* getBody1() const; + + /// Return a pointer to the second body in contact + CollisionBody* getBody2() const; + + // -------------------- Friendship -------------------- // + + friend class OverlapCallback; + }; + + // Class CallbackData + /** + * This class contains data about overlap between bodies + */ + class CallbackData { + + private: + + // -------------------- Attributes -------------------- // + + List<Pair<Entity, Entity>>& mOverlapBodies; + + /// Reference to the physics world + CollisionWorld& mWorld; + + // -------------------- Methods -------------------- // + + /// Constructor + CallbackData(List<Pair<Entity, Entity>>& overlapProxyShapes, CollisionWorld& world); + + /// Deleted copy constructor + CallbackData(const CallbackData& callbackData) = delete; + + /// Deleted assignment operator + CallbackData& operator=(const CallbackData& callbackData) = delete; + + /// Destructor + ~CallbackData() = default; + + public: + + // -------------------- Methods -------------------- // + + /// Return the number of overlapping pairs of bodies + uint getNbOverlappingPairs() const; + + /// Return a given overlapping pair of bodies + OverlapPair getOverlappingPair(uint index) const; + + // -------------------- Friendship -------------------- // + + friend class CollisionDetection; + }; + /// Destructor virtual ~OverlapCallback() { } - /// This method will be called for each reported overlapping bodies - virtual void notifyOverlap(CollisionBody* collisionBody)=0; + /// This method will be called to report bodies that overlap + virtual void onOverlap(CallbackData& callbackData)=0; }; +// Return the number of overlapping pairs of bodies +inline uint OverlapCallback::CallbackData::getNbOverlappingPairs() const { + return mOverlapBodies.size(); +} + +// Return a given overlapping pair of bodies +/// Note that the returned OverlapPair object is only valid during the call of the CollisionCallback::onOverlap() +/// method. Therefore, you need to get contact data from it and make a copy. Do not make a copy of the OverlapPair +/// object itself because it won't be valid after the CollisionCallback::onOverlap() call. +inline OverlapCallback::OverlapPair OverlapCallback::CallbackData::getOverlappingPair(uint index) const { + + assert(index < getNbOverlappingPairs()); + + return OverlapPair(mOverlapBodies[index], mWorld); +} + } #endif diff --git a/src/collision/ProxyShape.cpp b/src/collision/ProxyShape.cpp index d8dbeacd..cbb34788 100644 --- a/src/collision/ProxyShape.cpp +++ b/src/collision/ProxyShape.cpp @@ -78,6 +78,10 @@ bool ProxyShape::testPointInside(const Vector3& worldPoint) { */ void ProxyShape::setCollisionCategoryBits(unsigned short collisionCategoryBits) { + // TODO : Here we should probably remove all overlapping pairs with this shape in the + // broad-phase and add the shape in the "has moved" shape list so it is reevaluated + // with the new mask bits + mBody->mWorld.mProxyShapesComponents.setCollisionCategoryBits(mEntity, collisionCategoryBits); int broadPhaseId = mBody->mWorld.mProxyShapesComponents.getBroadPhaseId(mEntity); @@ -93,6 +97,10 @@ void ProxyShape::setCollisionCategoryBits(unsigned short collisionCategoryBits) */ void ProxyShape::setCollideWithMaskBits(unsigned short collideWithMaskBits) { + // TODO : Here we should probably remove all overlapping pairs with this shape in the + // broad-phase and add the shape in the "has moved" shape list so it is reevaluated + // with the new mask bits + mBody->mWorld.mProxyShapesComponents.setCollideWithMaskBits(mEntity, collideWithMaskBits); int broadPhaseId = mBody->mWorld.mProxyShapesComponents.getBroadPhaseId(mEntity); diff --git a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp index ecf69ee1..7b4831fc 100755 --- a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp +++ b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.cpp @@ -35,7 +35,7 @@ using namespace reactphysics3d; // This technique is based on the "Robust Contact Creation for Physics Simulations" presentation // by Dirk Gregorius. bool CapsuleVsCapsuleAlgorithm::testCollision(CapsuleVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound, MemoryAllocator& memoryAllocator) { + bool reportContacts, MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; @@ -154,9 +154,6 @@ bool CapsuleVsCapsuleAlgorithm::testCollision(CapsuleVsCapsuleNarrowPhaseInfoBat narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } continue; } } @@ -234,9 +231,6 @@ bool CapsuleVsCapsuleAlgorithm::testCollision(CapsuleVsCapsuleNarrowPhaseInfoBat narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } } } diff --git a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h index 32f76abe..d8b69bca 100644 --- a/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h +++ b/src/collision/narrowphase/CapsuleVsCapsuleAlgorithm.h @@ -68,8 +68,7 @@ class CapsuleVsCapsuleAlgorithm : public NarrowPhaseAlgorithm { /// Compute the narrow-phase collision detection between two capsules bool testCollision(CapsuleVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool stopFirstContactFound, - MemoryAllocator& memoryAllocator); + uint batchNbItems, bool reportContacts, MemoryAllocator& memoryAllocator); }; } diff --git a/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp b/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp index d425b80e..f42e8934 100644 --- a/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp +++ b/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.cpp @@ -41,14 +41,14 @@ using namespace reactphysics3d; // by Dirk Gregorius. bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound, + bool reportContacts, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; // First, we run the GJK algorithm GJKAlgorithm gjkAlgorithm; - SATAlgorithm satAlgorithm(memoryAllocator); + SATAlgorithm satAlgorithm(clipWithPreviousAxisIfStillColliding, memoryAllocator); #ifdef IS_PROFILING_ACTIVE @@ -166,9 +166,6 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar // Colision found narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } continue; } @@ -183,9 +180,6 @@ bool CapsuleVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& nar if (narrowPhaseInfoBatch.isColliding[batchIndex]) { isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } } } } diff --git a/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h b/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h index 70f223b5..53b5ca56 100644 --- a/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h +++ b/src/collision/narrowphase/CapsuleVsConvexPolyhedronAlgorithm.h @@ -71,8 +71,8 @@ class CapsuleVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm { /// Compute the narrow-phase collision detection between a capsule and a polyhedron bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool stopFirstContactFound, - MemoryAllocator& memoryAllocator); + uint batchNbItems, bool reportContacts, bool clipWithPreviousAxisIfStillColliding, + MemoryAllocator& memoryAllocator); }; } diff --git a/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp b/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp index 3ed15c74..570345fe 100644 --- a/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp +++ b/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.cpp @@ -37,11 +37,10 @@ using namespace reactphysics3d; // by Dirk Gregorius. bool ConvexPolyhedronVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound, - MemoryAllocator& memoryAllocator) { + bool reportContacts, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { // Run the SAT algorithm to find the separating axis and compute contact point - SATAlgorithm satAlgorithm(memoryAllocator); + SATAlgorithm satAlgorithm(clipWithPreviousAxisIfStillColliding, memoryAllocator); #ifdef IS_PROFILING_ACTIVE @@ -50,7 +49,7 @@ bool ConvexPolyhedronVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoB #endif bool isCollisionFound = satAlgorithm.testCollisionConvexPolyhedronVsConvexPolyhedron(narrowPhaseInfoBatch, batchStartIndex, - batchNbItems, reportContacts, stopFirstContactFound); + batchNbItems, reportContacts); for (uint batchIndex = batchStartIndex; batchIndex < batchStartIndex + batchNbItems; batchIndex++) { @@ -59,10 +58,6 @@ bool ConvexPolyhedronVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoB lastFrameCollisionInfo->wasUsingSAT = true; lastFrameCollisionInfo->wasUsingGJK = false; - - if (isCollisionFound && stopFirstContactFound) { - return isCollisionFound; - } } return isCollisionFound; diff --git a/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h b/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h index 7f9fccd2..6e43e76b 100644 --- a/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h +++ b/src/collision/narrowphase/ConvexPolyhedronVsConvexPolyhedronAlgorithm.h @@ -65,9 +65,8 @@ class ConvexPolyhedronVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm ConvexPolyhedronVsConvexPolyhedronAlgorithm& operator=(const ConvexPolyhedronVsConvexPolyhedronAlgorithm& algorithm) = delete; /// Compute the narrow-phase collision detection between two convex polyhedra - bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool stopFirstContactFound, - MemoryAllocator& memoryAllocator); + bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool reportContacts, + bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator); }; } diff --git a/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp b/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp index 61b2fb5b..ed989870 100644 --- a/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp +++ b/src/collision/narrowphase/NarrowPhaseInfoBatch.cpp @@ -28,6 +28,7 @@ #include "collision/ContactPointInfo.h" #include "collision/shapes/TriangleShape.h" #include "engine/OverlappingPair.h" +#include <iostream> using namespace reactphysics3d; diff --git a/src/collision/narrowphase/SAT/SATAlgorithm.cpp b/src/collision/narrowphase/SAT/SATAlgorithm.cpp index 3c467ea3..d8d18876 100644 --- a/src/collision/narrowphase/SAT/SATAlgorithm.cpp +++ b/src/collision/narrowphase/SAT/SATAlgorithm.cpp @@ -43,7 +43,8 @@ using namespace reactphysics3d; const decimal SATAlgorithm::SAME_SEPARATING_AXIS_BIAS = decimal(0.001); // Constructor -SATAlgorithm::SATAlgorithm(MemoryAllocator& memoryAllocator) : mMemoryAllocator(memoryAllocator) { +SATAlgorithm::SATAlgorithm(bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) + : mClipWithPreviousAxisIfStillColliding(clipWithPreviousAxisIfStillColliding), mMemoryAllocator(memoryAllocator) { #ifdef IS_PROFILING_ACTIVE mProfiler = nullptr; @@ -54,7 +55,7 @@ SATAlgorithm::SATAlgorithm(MemoryAllocator& memoryAllocator) : mMemoryAllocator( // Test collision between a sphere and a convex mesh bool SATAlgorithm::testCollisionSphereVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound) const { + bool reportContacts) const { bool isCollisionFound = false; @@ -136,9 +137,6 @@ bool SATAlgorithm::testCollisionSphereVsConvexPolyhedron(NarrowPhaseInfoBatch& n narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } } return isCollisionFound; @@ -476,7 +474,7 @@ bool SATAlgorithm::isMinkowskiFaceCapsuleVsEdge(const Vector3& capsuleSegment, c } // Test collision between two convex polyhedrons -bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool reportContacts, bool stopFirstContactFound) const { +bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool reportContacts) const { RP3D_PROFILE("SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron()", mProfiler); @@ -528,7 +526,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn } // The two shapes were overlapping in the previous frame and still seem to overlap in this one - if (lastFrameCollisionInfo->wasColliding && penetrationDepth > decimal(0.0)) { + if (lastFrameCollisionInfo->wasColliding && mClipWithPreviousAxisIfStillColliding && penetrationDepth > decimal(0.0)) { minPenetrationDepth = penetrationDepth; minFaceIndex = lastFrameCollisionInfo->satMinAxisFaceIndex; @@ -548,9 +546,6 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn // Therefore, we can return without running the whole SAT algorithm narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } continue; } @@ -571,7 +566,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn } // The two shapes were overlapping in the previous frame and still seem to overlap in this one - if (lastFrameCollisionInfo->wasColliding && penetrationDepth > decimal(0.0)) { + if (lastFrameCollisionInfo->wasColliding && mClipWithPreviousAxisIfStillColliding && penetrationDepth > decimal(0.0)) { minPenetrationDepth = penetrationDepth; minFaceIndex = lastFrameCollisionInfo->satMinAxisFaceIndex; @@ -591,9 +586,6 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn // Therefore, we can return without running the whole SAT algorithm narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } continue; } @@ -632,7 +624,7 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn } // If the shapes were overlapping on the previous axis and still seem to overlap in this frame - if (lastFrameCollisionInfo->wasColliding && penetrationDepth > decimal(0.0)) { + if (lastFrameCollisionInfo->wasColliding && mClipWithPreviousAxisIfStillColliding && penetrationDepth > decimal(0.0)) { // Compute the closest points between the two edges (in the local-space of poylhedron 2) Vector3 closestPointPolyhedron1Edge, closestPointPolyhedron2Edge; @@ -683,9 +675,6 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn // we return without running the whole SAT algorithm narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } continue; } @@ -878,9 +867,6 @@ bool SATAlgorithm::testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseIn narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } } return isCollisionFound; diff --git a/src/collision/narrowphase/SAT/SATAlgorithm.h b/src/collision/narrowphase/SAT/SATAlgorithm.h index 1eb1b923..2d7afd4a 100644 --- a/src/collision/narrowphase/SAT/SATAlgorithm.h +++ b/src/collision/narrowphase/SAT/SATAlgorithm.h @@ -60,6 +60,20 @@ class SATAlgorithm { /// make sure the contact manifold does not change too much between frames. static const decimal SAME_SEPARATING_AXIS_BIAS; + /// True means that if two shapes were colliding last time (previous frame) and are still colliding + /// we use the previous (minimum penetration depth) axis to clip the colliding features and we don't + /// recompute a new (minimum penetration depth) axis. This value must be true for a dynamic simulation + /// because it uses temporal coherence and clip the colliding features with the previous + /// axis (this is good for stability). However, when we use the testCollision() methods, the penetration + /// depths might be very large and we always want the current true axis with minimum penetration depth. + /// In this case, this value must be set to false. Consider the following situation. Two shapes start overlaping + /// with "x" being the axis of minimum penetration depth. Then, if the shapes move but are still penetrating, + /// it is possible that the axis of minimum penetration depth changes for axis "y" for instance. If this value + /// if true, we will always use the axis of the previous collision test and therefore always report that the + /// penetrating axis is "x" even if it has changed to axis "y" during the collision. This is not what we want + /// when we call the testCollision() methods. + bool mClipWithPreviousAxisIfStillColliding; + /// Memory allocator MemoryAllocator& mMemoryAllocator; @@ -126,7 +140,7 @@ class SATAlgorithm { // -------------------- Methods -------------------- // /// Constructor - SATAlgorithm(MemoryAllocator& memoryAllocator); + SATAlgorithm(bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator); /// Destructor ~SATAlgorithm() = default; @@ -140,7 +154,7 @@ class SATAlgorithm { /// Test collision between a sphere and a convex mesh bool testCollisionSphereVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound) const; + bool reportContacts) const; /// Test collision between a capsule and a convex mesh bool testCollisionCapsuleVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex, bool reportContacts) const; @@ -158,7 +172,7 @@ class SATAlgorithm { /// Test collision between two convex meshes bool testCollisionConvexPolyhedronVsConvexPolyhedron(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool stopFirstContactFound) const; + uint batchNbItems, bool reportContacts) const; #ifdef IS_PROFILING_ACTIVE diff --git a/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp b/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp index 68a02509..3abb1f09 100755 --- a/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp +++ b/src/collision/narrowphase/SphereVsCapsuleAlgorithm.cpp @@ -36,7 +36,7 @@ using namespace reactphysics3d; // This technique is based on the "Robust Contact Creation for Physics Simulations" presentation // by Dirk Gregorius. bool SphereVsCapsuleAlgorithm::testCollision(SphereVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound, MemoryAllocator& memoryAllocator) { + bool reportContacts, MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; @@ -137,9 +137,6 @@ bool SphereVsCapsuleAlgorithm::testCollision(SphereVsCapsuleNarrowPhaseInfoBatch narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } continue; } } diff --git a/src/collision/narrowphase/SphereVsCapsuleAlgorithm.h b/src/collision/narrowphase/SphereVsCapsuleAlgorithm.h index 7394bfb2..25926fce 100644 --- a/src/collision/narrowphase/SphereVsCapsuleAlgorithm.h +++ b/src/collision/narrowphase/SphereVsCapsuleAlgorithm.h @@ -68,8 +68,7 @@ class SphereVsCapsuleAlgorithm : public NarrowPhaseAlgorithm { /// Compute the narrow-phase collision detection between a sphere and a capsule bool testCollision(SphereVsCapsuleNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool stopFirstContactFound, - MemoryAllocator& memoryAllocator); + uint batchNbItems, bool reportContacts, MemoryAllocator& memoryAllocator); }; } diff --git a/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp b/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp index a8e638be..b8fb779f 100644 --- a/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp +++ b/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.cpp @@ -36,7 +36,7 @@ using namespace reactphysics3d; // This technique is based on the "Robust Contact Creation for Physics Simulations" presentation // by Dirk Gregorius. bool SphereVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound, MemoryAllocator& memoryAllocator) { + bool reportContacts, bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator) { // First, we run the GJK algorithm GJKAlgorithm gjkAlgorithm; @@ -73,9 +73,6 @@ bool SphereVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narr // Return true narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } continue; } @@ -83,7 +80,7 @@ bool SphereVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narr if (gjkResults[batchIndex] == GJKAlgorithm::GJKResult::INTERPENETRATE) { // Run the SAT algorithm to find the separating axis and compute contact point - SATAlgorithm satAlgorithm(memoryAllocator); + SATAlgorithm satAlgorithm(clipWithPreviousAxisIfStillColliding, memoryAllocator); #ifdef IS_PROFILING_ACTIVE @@ -91,15 +88,11 @@ bool SphereVsConvexPolyhedronAlgorithm::testCollision(NarrowPhaseInfoBatch& narr #endif - isCollisionFound |= satAlgorithm.testCollisionSphereVsConvexPolyhedron(narrowPhaseInfoBatch, batchIndex, 1, reportContacts, stopFirstContactFound); + isCollisionFound |= satAlgorithm.testCollisionSphereVsConvexPolyhedron(narrowPhaseInfoBatch, batchIndex, 1, reportContacts); lastFrameCollisionInfo->wasUsingGJK = false; lastFrameCollisionInfo->wasUsingSAT = true; - if (isCollisionFound && stopFirstContactFound) { - return isCollisionFound; - } - continue; } } diff --git a/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h b/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h index 040caec6..793dc48a 100644 --- a/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h +++ b/src/collision/narrowphase/SphereVsConvexPolyhedronAlgorithm.h @@ -71,9 +71,8 @@ class SphereVsConvexPolyhedronAlgorithm : public NarrowPhaseAlgorithm { SphereVsConvexPolyhedronAlgorithm& operator=(const SphereVsConvexPolyhedronAlgorithm& algorithm) = delete; /// Compute the narrow-phase collision detection between a sphere and a convex polyhedron - bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool stopFirstContactFound, - MemoryAllocator& memoryAllocator); + bool testCollision(NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, bool reportContacts, + bool clipWithPreviousAxisIfStillColliding, MemoryAllocator& memoryAllocator); }; } diff --git a/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp b/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp index 52bf5d96..a6d5a61d 100755 --- a/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp +++ b/src/collision/narrowphase/SphereVsSphereAlgorithm.cpp @@ -32,7 +32,7 @@ using namespace reactphysics3d; bool SphereVsSphereAlgorithm::testCollision(SphereVsSphereNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, uint batchNbItems, - bool reportContacts, bool stopFirstContactFound, MemoryAllocator& memoryAllocator) { + bool reportContacts, MemoryAllocator& memoryAllocator) { bool isCollisionFound = false; @@ -94,9 +94,6 @@ bool SphereVsSphereAlgorithm::testCollision(SphereVsSphereNarrowPhaseInfoBatch& narrowPhaseInfoBatch.isColliding[batchIndex] = true; isCollisionFound = true; - if (stopFirstContactFound) { - return isCollisionFound; - } } } diff --git a/src/collision/narrowphase/SphereVsSphereAlgorithm.h b/src/collision/narrowphase/SphereVsSphereAlgorithm.h index e0f4c5be..5e9554b4 100644 --- a/src/collision/narrowphase/SphereVsSphereAlgorithm.h +++ b/src/collision/narrowphase/SphereVsSphereAlgorithm.h @@ -67,8 +67,7 @@ class SphereVsSphereAlgorithm : public NarrowPhaseAlgorithm { /// Compute a contact info if the two bounding volume collide bool testCollision(SphereVsSphereNarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchStartIndex, - uint batchNbItems, bool reportContacts, bool stopFirstContactFound, - MemoryAllocator& memoryAllocator); + uint batchNbItems, bool reportContacts, MemoryAllocator& memoryAllocator); }; } diff --git a/src/components/Components.h b/src/components/Components.h index e4dc213e..320104a3 100644 --- a/src/components/Components.h +++ b/src/components/Components.h @@ -121,6 +121,9 @@ class Components { /// Return the number of enabled components uint32 getNbEnabledComponents() const; + + /// Return the index in the arrays for a given entity + uint32 getEntityIndex(Entity entity) const; }; // Return true if an entity is sleeping @@ -144,6 +147,11 @@ inline uint32 Components::getNbEnabledComponents() const { return mDisabledStartIndex; } +// Return the index in the arrays for a given entity +inline uint32 Components::getEntityIndex(Entity entity) const { + assert(hasComponent(entity)); + return mMapEntityToComponentIndex[entity]; +} } #endif diff --git a/src/components/DynamicsComponents.cpp b/src/components/DynamicsComponents.cpp index e23b3cfb..8350ae2f 100644 --- a/src/components/DynamicsComponents.cpp +++ b/src/components/DynamicsComponents.cpp @@ -35,7 +35,11 @@ using namespace reactphysics3d; // Constructor DynamicsComponents::DynamicsComponents(MemoryAllocator& allocator) - :Components(allocator, sizeof(Entity) + sizeof(Vector3) + sizeof (Vector3)) { + :Components(allocator, sizeof(Entity) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + + sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(Vector3) + sizeof(decimal) + + sizeof(decimal) + sizeof(decimal) + sizeof(decimal) + sizeof(Matrix3x3) + sizeof(Matrix3x3) + + sizeof(Vector3) + sizeof(Quaternion) + sizeof(Vector3) + sizeof(Vector3) + sizeof(bool) + + sizeof(bool)) { // Allocate memory for the components data allocate(INIT_NB_ALLOCATED_COMPONENTS); @@ -57,6 +61,24 @@ void DynamicsComponents::allocate(uint32 nbComponentsToAllocate) { Entity* newBodies = static_cast<Entity*>(newBuffer); Vector3* newLinearVelocities = reinterpret_cast<Vector3*>(newBodies + nbComponentsToAllocate); Vector3* newAngularVelocities = reinterpret_cast<Vector3*>(newLinearVelocities + nbComponentsToAllocate); + Vector3* newConstrainedLinearVelocities = reinterpret_cast<Vector3*>(newAngularVelocities + nbComponentsToAllocate); + Vector3* newConstrainedAngularVelocities = reinterpret_cast<Vector3*>(newConstrainedLinearVelocities + nbComponentsToAllocate); + Vector3* newSplitLinearVelocities = reinterpret_cast<Vector3*>(newConstrainedAngularVelocities + nbComponentsToAllocate); + Vector3* newSplitAngularVelocities = reinterpret_cast<Vector3*>(newSplitLinearVelocities + nbComponentsToAllocate); + Vector3* newExternalForces = reinterpret_cast<Vector3*>(newSplitAngularVelocities + nbComponentsToAllocate); + Vector3* newExternalTorques = reinterpret_cast<Vector3*>(newExternalForces + nbComponentsToAllocate); + decimal* newLinearDampings = reinterpret_cast<decimal*>(newExternalTorques + nbComponentsToAllocate); + decimal* newAngularDampings = reinterpret_cast<decimal*>(newLinearDampings + nbComponentsToAllocate); + decimal* newInitMasses = reinterpret_cast<decimal*>(newAngularDampings + nbComponentsToAllocate); + decimal* newInverseMasses = reinterpret_cast<decimal*>(newInitMasses + nbComponentsToAllocate); + Matrix3x3* newInertiaTensorLocalInverses = reinterpret_cast<Matrix3x3*>(newInverseMasses + nbComponentsToAllocate); + Matrix3x3* newInertiaTensorWorldInverses = reinterpret_cast<Matrix3x3*>(newInertiaTensorLocalInverses + nbComponentsToAllocate); + Vector3* newConstrainedPositions = reinterpret_cast<Vector3*>(newInertiaTensorWorldInverses + nbComponentsToAllocate); + Quaternion* newConstrainedOrientations = reinterpret_cast<Quaternion*>(newConstrainedPositions + nbComponentsToAllocate); + Vector3* newCentersOfMassLocal = reinterpret_cast<Vector3*>(newConstrainedOrientations + nbComponentsToAllocate); + Vector3* newCentersOfMassWorld = reinterpret_cast<Vector3*>(newCentersOfMassLocal + nbComponentsToAllocate); + bool* newIsGravityEnabled = reinterpret_cast<bool*>(newCentersOfMassWorld + nbComponentsToAllocate); + bool* newIsAlreadyInIsland = reinterpret_cast<bool*>(newIsGravityEnabled + nbComponentsToAllocate); // If there was already components before if (mNbComponents > 0) { @@ -65,6 +87,24 @@ void DynamicsComponents::allocate(uint32 nbComponentsToAllocate) { memcpy(newBodies, mBodies, mNbComponents * sizeof(Entity)); memcpy(newLinearVelocities, mLinearVelocities, mNbComponents * sizeof(Vector3)); memcpy(newAngularVelocities, mAngularVelocities, mNbComponents * sizeof(Vector3)); + memcpy(newConstrainedLinearVelocities, mConstrainedLinearVelocities, mNbComponents * sizeof(Vector3)); + memcpy(newConstrainedAngularVelocities, mConstrainedAngularVelocities, mNbComponents * sizeof(Vector3)); + memcpy(newSplitLinearVelocities, mSplitLinearVelocities, mNbComponents * sizeof(Vector3)); + memcpy(newSplitAngularVelocities, mSplitAngularVelocities, mNbComponents * sizeof(Vector3)); + memcpy(newExternalForces, mExternalForces, mNbComponents * sizeof(Vector3)); + memcpy(newExternalTorques, mExternalTorques, mNbComponents * sizeof(Vector3)); + memcpy(newLinearDampings, mLinearDampings, mNbComponents * sizeof(decimal)); + memcpy(newAngularDampings, mAngularDampings, mNbComponents * sizeof(decimal)); + memcpy(newInitMasses, mInitMasses, mNbComponents * sizeof(decimal)); + memcpy(newInverseMasses, mInverseMasses, mNbComponents * sizeof(decimal)); + memcpy(newInertiaTensorLocalInverses, mInverseInertiaTensorsLocal, mNbComponents * sizeof(Matrix3x3)); + memcpy(newInertiaTensorWorldInverses, mInverseInertiaTensorsWorld, mNbComponents * sizeof(Matrix3x3)); + memcpy(newConstrainedPositions, mConstrainedPositions, mNbComponents * sizeof(Vector3)); + memcpy(newConstrainedOrientations, mConstrainedOrientations, mNbComponents * sizeof(Quaternion)); + memcpy(newCentersOfMassLocal, mCentersOfMassLocal, mNbComponents * sizeof(Vector3)); + memcpy(newCentersOfMassWorld, mCentersOfMassWorld, mNbComponents * sizeof(Vector3)); + memcpy(newIsGravityEnabled, mIsGravityEnabled, mNbComponents * sizeof(bool)); + memcpy(newIsAlreadyInIsland, mIsAlreadyInIsland, mNbComponents * sizeof(bool)); // Deallocate previous memory mMemoryAllocator.release(mBuffer, mNbAllocatedComponents * mComponentDataSize); @@ -74,6 +114,24 @@ void DynamicsComponents::allocate(uint32 nbComponentsToAllocate) { mBodies = newBodies; mLinearVelocities = newLinearVelocities; mAngularVelocities = newAngularVelocities; + mConstrainedLinearVelocities = newConstrainedLinearVelocities; + mConstrainedAngularVelocities = newConstrainedAngularVelocities; + mSplitLinearVelocities = newSplitLinearVelocities; + mSplitAngularVelocities = newSplitAngularVelocities; + mExternalForces = newExternalForces; + mExternalTorques = newExternalTorques; + mLinearDampings = newLinearDampings; + mAngularDampings = newAngularDampings; + mInitMasses = newInitMasses; + mInverseMasses = newInverseMasses; + mInverseInertiaTensorsLocal = newInertiaTensorLocalInverses; + mInverseInertiaTensorsWorld = newInertiaTensorWorldInverses; + mConstrainedPositions = newConstrainedPositions; + mConstrainedOrientations = newConstrainedOrientations; + mCentersOfMassLocal = newCentersOfMassLocal; + mCentersOfMassWorld = newCentersOfMassWorld; + mIsGravityEnabled = newIsGravityEnabled; + mIsAlreadyInIsland = newIsAlreadyInIsland; mNbAllocatedComponents = nbComponentsToAllocate; } @@ -85,8 +143,26 @@ void DynamicsComponents::addComponent(Entity bodyEntity, bool isSleeping, const // Insert the new component data new (mBodies + index) Entity(bodyEntity); - new (mLinearVelocities + index) Vector3(component.linearVelocity); - new (mAngularVelocities + index) Vector3(component.angularVelocity); + new (mLinearVelocities + index) Vector3(0, 0, 0); + new (mAngularVelocities + index) Vector3(0, 0, 0); + new (mConstrainedLinearVelocities + index) Vector3(0, 0, 0); + new (mConstrainedAngularVelocities + index) Vector3(0, 0, 0); + new (mSplitLinearVelocities + index) Vector3(0, 0, 0); + new (mSplitAngularVelocities + index) Vector3(0, 0, 0); + new (mExternalForces + index) Vector3(0, 0, 0); + new (mExternalTorques + index) Vector3(0, 0, 0); + mLinearDampings[index] = decimal(0.0); + mAngularDampings[index] = decimal(0.0); + mInitMasses[index] = decimal(1.0); + mInverseMasses[index] = decimal(1.0); + new (mInverseInertiaTensorsLocal + index) Matrix3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0); + new (mInverseInertiaTensorsWorld + index) Matrix3x3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0); + new (mConstrainedPositions + index) Vector3(0, 0, 0); + new (mConstrainedOrientations + index) Quaternion(0, 0, 0, 1); + new (mCentersOfMassLocal + index) Vector3(0, 0, 0); + new (mCentersOfMassWorld + index) Vector3(component.worldPosition); + mIsGravityEnabled[index] = true; + mIsAlreadyInIsland[index] = false; // Map the entity with the new component lookup index mMapEntityToComponentIndex.add(Pair<Entity, uint32>(bodyEntity, index)); @@ -107,6 +183,24 @@ void DynamicsComponents::moveComponentToIndex(uint32 srcIndex, uint32 destIndex) new (mBodies + destIndex) Entity(mBodies[srcIndex]); new (mLinearVelocities + destIndex) Vector3(mLinearVelocities[srcIndex]); new (mAngularVelocities + destIndex) Vector3(mAngularVelocities[srcIndex]); + new (mConstrainedLinearVelocities + destIndex) Vector3(mConstrainedLinearVelocities[srcIndex]); + new (mConstrainedAngularVelocities + destIndex) Vector3(mConstrainedAngularVelocities[srcIndex]); + new (mSplitLinearVelocities + destIndex) Vector3(mSplitLinearVelocities[srcIndex]); + new (mSplitAngularVelocities + destIndex) Vector3(mSplitAngularVelocities[srcIndex]); + new (mExternalForces + destIndex) Vector3(mExternalForces[srcIndex]); + new (mExternalTorques + destIndex) Vector3(mExternalTorques[srcIndex]); + mLinearDampings[destIndex] = mLinearDampings[srcIndex]; + mAngularDampings[destIndex] = mAngularDampings[srcIndex]; + mInitMasses[destIndex] = mInitMasses[srcIndex]; + mInverseMasses[destIndex] = mInverseMasses[srcIndex]; + new (mInverseInertiaTensorsLocal + destIndex) Matrix3x3(mInverseInertiaTensorsLocal[srcIndex]); + new (mInverseInertiaTensorsWorld + destIndex) Matrix3x3(mInverseInertiaTensorsWorld[srcIndex]); + new (mConstrainedPositions + destIndex) Vector3(mConstrainedPositions[srcIndex]); + new (mConstrainedOrientations + destIndex) Quaternion(mConstrainedOrientations[srcIndex]); + new (mCentersOfMassLocal + destIndex) Vector3(mCentersOfMassLocal[srcIndex]); + new (mCentersOfMassWorld + destIndex) Vector3(mCentersOfMassWorld[srcIndex]); + mIsGravityEnabled[destIndex] = mIsGravityEnabled[srcIndex]; + mIsAlreadyInIsland[destIndex] = mIsAlreadyInIsland[srcIndex]; // Destroy the source component destroyComponent(srcIndex); @@ -129,6 +223,24 @@ void DynamicsComponents::swapComponents(uint32 index1, uint32 index2) { Entity entity1(mBodies[index1]); Vector3 linearVelocity1(mLinearVelocities[index1]); Vector3 angularVelocity1(mAngularVelocities[index1]); + Vector3 constrainedLinearVelocity1(mConstrainedLinearVelocities[index1]); + Vector3 constrainedAngularVelocity1(mConstrainedAngularVelocities[index1]); + Vector3 splitLinearVelocity1(mSplitLinearVelocities[index1]); + Vector3 splitAngularVelocity1(mSplitAngularVelocities[index1]); + Vector3 externalForce1(mExternalForces[index1]); + Vector3 externalTorque1(mExternalTorques[index1]); + decimal linearDamping1 = mLinearDampings[index1]; + decimal angularDamping1 = mAngularDampings[index1]; + decimal initMass1 = mInitMasses[index1]; + decimal inverseMass1 = mInverseMasses[index1]; + Matrix3x3 inertiaTensorLocalInverse1 = mInverseInertiaTensorsLocal[index1]; + Matrix3x3 inertiaTensorWorldInverse1 = mInverseInertiaTensorsWorld[index1]; + Vector3 constrainedPosition1 = mConstrainedPositions[index1]; + Quaternion constrainedOrientation1 = mConstrainedOrientations[index1]; + Vector3 centerOfMassLocal1 = mCentersOfMassLocal[index1]; + Vector3 centerOfMassWorld1 = mCentersOfMassWorld[index1]; + bool isGravityEnabled1 = mIsGravityEnabled[index1]; + bool isAlreadyInIsland1 = mIsAlreadyInIsland[index1]; // Destroy component 1 destroyComponent(index1); @@ -139,6 +251,24 @@ void DynamicsComponents::swapComponents(uint32 index1, uint32 index2) { new (mBodies + index2) Entity(entity1); new (mLinearVelocities + index2) Vector3(linearVelocity1); new (mAngularVelocities + index2) Vector3(angularVelocity1); + new (mConstrainedLinearVelocities + index2) Vector3(constrainedLinearVelocity1); + new (mConstrainedAngularVelocities + index2) Vector3(constrainedAngularVelocity1); + new (mSplitLinearVelocities + index2) Vector3(splitLinearVelocity1); + new (mSplitAngularVelocities + index2) Vector3(splitAngularVelocity1); + new (mExternalForces + index2) Vector3(externalForce1); + new (mExternalTorques + index2) Vector3(externalTorque1); + mLinearDampings[index2] = linearDamping1; + mAngularDampings[index2] = angularDamping1; + mInitMasses[index2] = initMass1; + mInverseMasses[index2] = inverseMass1; + mInverseInertiaTensorsLocal[index2] = inertiaTensorLocalInverse1; + mInverseInertiaTensorsWorld[index2] = inertiaTensorWorldInverse1; + mConstrainedPositions[index2] = constrainedPosition1; + mConstrainedOrientations[index2] = constrainedOrientation1; + mCentersOfMassLocal[index2] = centerOfMassLocal1; + mCentersOfMassWorld[index2] = centerOfMassWorld1; + mIsGravityEnabled[index2] = isGravityEnabled1; + mIsAlreadyInIsland[index2] = isAlreadyInIsland1; // Update the entity to component index mapping mMapEntityToComponentIndex.add(Pair<Entity, uint32>(entity1, index2)); @@ -160,4 +290,16 @@ void DynamicsComponents::destroyComponent(uint32 index) { mBodies[index].~Entity(); mLinearVelocities[index].~Vector3(); mAngularVelocities[index].~Vector3(); + mConstrainedLinearVelocities[index].~Vector3(); + mConstrainedAngularVelocities[index].~Vector3(); + mSplitLinearVelocities[index].~Vector3(); + mSplitAngularVelocities[index].~Vector3(); + mExternalForces[index].~Vector3(); + mExternalTorques[index].~Vector3(); + mInverseInertiaTensorsLocal[index].~Matrix3x3(); + mInverseInertiaTensorsWorld[index].~Matrix3x3(); + mConstrainedPositions[index].~Vector3(); + mConstrainedOrientations[index].~Quaternion(); + mCentersOfMassLocal[index].~Vector3(); + mCentersOfMassWorld[index].~Vector3(); } diff --git a/src/components/DynamicsComponents.h b/src/components/DynamicsComponents.h index 7c81e61b..8cbe2135 100644 --- a/src/components/DynamicsComponents.h +++ b/src/components/DynamicsComponents.h @@ -30,6 +30,7 @@ #include "mathematics/Transform.h" #include "engine/Entity.h" #include "components/Components.h" +#include "mathematics/Matrix3x3.h" #include "containers/Map.h" // ReactPhysics3D namespace @@ -59,6 +60,60 @@ class DynamicsComponents : public Components { /// Array with the angular velocity of each component Vector3* mAngularVelocities; + /// Array with the constrained linear velocity of each component + Vector3* mConstrainedLinearVelocities; + + /// Array with the constrained angular velocity of each component + Vector3* mConstrainedAngularVelocities; + + /// Array with the split linear velocity of each component + Vector3* mSplitLinearVelocities; + + /// Array with the split angular velocity of each component + Vector3* mSplitAngularVelocities; + + /// Array with the external force of each component + Vector3* mExternalForces; + + /// Array with the external torque of each component + Vector3* mExternalTorques; + + /// Array with the linear damping factor of each component + decimal* mLinearDampings; + + /// Array with the angular damping factor of each component + decimal* mAngularDampings; + + /// Array with the initial mass of each component + decimal* mInitMasses; + + /// Array with the inverse mass of each component + decimal* mInverseMasses; + + /// Array with the inverse of the inertia tensor of each component + Matrix3x3* mInverseInertiaTensorsLocal; + + /// Array with the inverse of the world inertia tensor of each component + Matrix3x3* mInverseInertiaTensorsWorld; + + /// Array with the constrained position of each component (for position error correction) + Vector3* mConstrainedPositions; + + /// Array of constrained orientation for each component (for position error correction) + Quaternion* mConstrainedOrientations; + + /// Array of center of mass of each component (in local-space coordinates) + Vector3* mCentersOfMassLocal; + + /// Array of center of mass of each component (in world-space coordinates) + Vector3* mCentersOfMassWorld; + + /// True if the gravity needs to be applied to this component + bool* mIsGravityEnabled; + + /// Array with the boolean value to know if the body has already been added into an island + bool* mIsAlreadyInIsland; + // -------------------- Methods -------------------- // /// Allocate memory for a given number of components @@ -78,12 +133,11 @@ class DynamicsComponents : public Components { /// Structure for the data of a transform component struct DynamicsComponent { - const Vector3& linearVelocity; - const Vector3& angularVelocity; + const Vector3& worldPosition; /// Constructor - DynamicsComponent(const Vector3& linearVelocity, const Vector3& angularVelocity) - : linearVelocity(linearVelocity), angularVelocity(angularVelocity) { + DynamicsComponent(const Vector3& worldPosition) + : worldPosition(worldPosition) { } }; @@ -100,10 +154,64 @@ class DynamicsComponents : public Components { void addComponent(Entity bodyEntity, bool isSleeping, const DynamicsComponent& component); /// Return the linear velocity of an entity - Vector3& getLinearVelocity(Entity bodyEntity) const; + const Vector3& getLinearVelocity(Entity bodyEntity) const; /// Return the angular velocity of an entity - Vector3& getAngularVelocity(Entity bodyEntity) const; + const Vector3& getAngularVelocity(Entity bodyEntity) const; + + /// Return the constrained linear velocity of an entity + const Vector3& getConstrainedLinearVelocity(Entity bodyEntity) const; + + /// Return the constrained angular velocity of an entity + const Vector3& getConstrainedAngularVelocity(Entity bodyEntity) const; + + /// Return the split linear velocity of an entity + const Vector3& getSplitLinearVelocity(Entity bodyEntity) const; + + /// Return the split angular velocity of an entity + const Vector3& getSplitAngularVelocity(Entity bodyEntity) const; + + /// Return the external force of an entity + const Vector3& getExternalForce(Entity bodyEntity) const; + + /// Return the external torque of an entity + const Vector3& getExternalTorque(Entity bodyEntity) const; + + /// Return the linear damping factor of an entity + decimal getLinearDamping(Entity bodyEntity) const; + + /// Return the angular damping factor of an entity + decimal getAngularDamping(Entity bodyEntity) const; + + /// Return the initial mass of an entity + decimal getInitMass(Entity bodyEntity) const; + + /// Return the mass inverse of an entity + decimal getMassInverse(Entity bodyEntity) const; + + /// Return the inverse local inertia tensor of an entity + const Matrix3x3& getInertiaTensorLocalInverse(Entity bodyEntity); + + /// Return the inverse world inertia tensor of an entity + const Matrix3x3& getInertiaTensorWorldInverse(Entity bodyEntity); + + /// Return the constrained position of an entity + const Vector3& getConstrainedPosition(Entity bodyEntity); + + /// Return the constrained orientation of an entity + const Quaternion& getConstrainedOrientation(Entity bodyEntity); + + /// Return the local center of mass of an entity + const Vector3& getCenterOfMassLocal(Entity bodyEntity); + + /// Return the world center of mass of an entity + const Vector3& getCenterOfMassWorld(Entity bodyEntity); + + /// Return true if gravity is enabled for this entity + bool getIsGravityEnabled(Entity bodyEntity) const; + + /// Return true if the entity is already in an island + bool getIsAlreadyInIsland(Entity bodyEntity) const; /// Set the linear velocity of an entity void setLinearVelocity(Entity bodyEntity, const Vector3& linearVelocity); @@ -111,13 +219,74 @@ class DynamicsComponents : public Components { /// Set the angular velocity of an entity void setAngularVelocity(Entity bodyEntity, const Vector3& angularVelocity); + /// Set the constrained linear velocity of an entity + void setConstrainedLinearVelocity(Entity bodyEntity, const Vector3& constrainedLinearVelocity); + + /// Set the constrained angular velocity of an entity + void setConstrainedAngularVelocity(Entity bodyEntity, const Vector3& constrainedAngularVelocity); + + /// Set the split linear velocity of an entity + void setSplitLinearVelocity(Entity bodyEntity, const Vector3& splitLinearVelocity); + + /// Set the split angular velocity of an entity + void setSplitAngularVelocity(Entity bodyEntity, const Vector3& splitAngularVelocity); + + /// Set the external force of an entity + void setExternalForce(Entity bodyEntity, const Vector3& externalForce); + + /// Set the external force of an entity + void setExternalTorque(Entity bodyEntity, const Vector3& externalTorque); + + /// Set the linear damping factor of an entity + void setLinearDamping(Entity bodyEntity, decimal linearDamping); + + /// Set the angular damping factor of an entity + void setAngularDamping(Entity bodyEntity, decimal angularDamping); + + /// Set the initial mass of an entity + void setInitMass(Entity bodyEntity, decimal initMass); + + /// Set the inverse mass of an entity + void setMassInverse(Entity bodyEntity, decimal inverseMass); + + /// Set the inverse local inertia tensor of an entity + void setInverseInertiaTensorLocal(Entity bodyEntity, const Matrix3x3& inertiaTensorLocalInverse); + + /// Set the inverse world inertia tensor of an entity + void setInverseInertiaTensorWorld(Entity bodyEntity, const Matrix3x3& inertiaTensorWorldInverse); + + /// Set the constrained position of an entity + void setConstrainedPosition(Entity bodyEntity, const Vector3& constrainedPosition); + + /// Set the constrained orientation of an entity + void setConstrainedOrientation(Entity bodyEntity, const Quaternion& constrainedOrientation); + + /// Set the local center of mass of an entity + void setCenterOfMassLocal(Entity bodyEntity, const Vector3& centerOfMassLocal); + + /// Set the world center of mass of an entity + void setCenterOfMassWorld(Entity bodyEntity, const Vector3& centerOfMassWorld); + + /// Set the value to know if the gravity is enabled for this entity + bool setIsGravityEnabled(Entity bodyEntity, bool isGravityEnabled); + + /// Set the value to know if the entity is already in an island + bool setIsAlreadyInIsland(Entity bodyEntity, bool isAlreadyInIsland); + // -------------------- Friendship -------------------- // friend class BroadPhaseSystem; + friend class DynamicsWorld; + friend class ContactSolver; + friend class BallAndSocketJoint; + friend class FixedJoint; + friend class HingeJoint; + friend class SliderJoint; + }; // Return the linear velocity of an entity -inline Vector3& DynamicsComponents::getLinearVelocity(Entity bodyEntity) const { +inline const Vector3& DynamicsComponents::getLinearVelocity(Entity bodyEntity) const { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -125,7 +294,7 @@ inline Vector3& DynamicsComponents::getLinearVelocity(Entity bodyEntity) const { } // Return the angular velocity of an entity -inline Vector3& DynamicsComponents::getAngularVelocity(Entity bodyEntity) const { +inline const Vector3 &DynamicsComponents::getAngularVelocity(Entity bodyEntity) const { assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); @@ -148,6 +317,294 @@ inline void DynamicsComponents::setAngularVelocity(Entity bodyEntity, const Vect mAngularVelocities[mMapEntityToComponentIndex[bodyEntity]] = angularVelocity; } +// Return the constrained linear velocity of an entity +inline const Vector3& DynamicsComponents::getConstrainedLinearVelocity(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mConstrainedLinearVelocities[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the constrained angular velocity of an entity +inline const Vector3& DynamicsComponents::getConstrainedAngularVelocity(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mConstrainedAngularVelocities[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the split linear velocity of an entity +inline const Vector3& DynamicsComponents::getSplitLinearVelocity(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mSplitLinearVelocities[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the split angular velocity of an entity +inline const Vector3& DynamicsComponents::getSplitAngularVelocity(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mSplitAngularVelocities[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the external force of an entity +inline const Vector3& DynamicsComponents::getExternalForce(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mExternalForces[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the external torque of an entity +inline const Vector3& DynamicsComponents::getExternalTorque(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mExternalTorques[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the linear damping factor of an entity +inline decimal DynamicsComponents::getLinearDamping(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mLinearDampings[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the angular damping factor of an entity +inline decimal DynamicsComponents::getAngularDamping(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mAngularDampings[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the initial mass of an entity +inline decimal DynamicsComponents::getInitMass(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mInitMasses[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the inverse mass of an entity +inline decimal DynamicsComponents::getMassInverse(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mInverseMasses[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the inverse local inertia tensor of an entity +inline const Matrix3x3& DynamicsComponents::getInertiaTensorLocalInverse(Entity bodyEntity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mInverseInertiaTensorsLocal[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the inverse world inertia tensor of an entity +inline const Matrix3x3& DynamicsComponents::getInertiaTensorWorldInverse(Entity bodyEntity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mInverseInertiaTensorsWorld[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the constrained position of an entity +inline const Vector3& DynamicsComponents::getConstrainedPosition(Entity bodyEntity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mConstrainedPositions[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the constrained orientation of an entity +inline const Quaternion& DynamicsComponents::getConstrainedOrientation(Entity bodyEntity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mConstrainedOrientations[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the local center of mass of an entity +inline const Vector3& DynamicsComponents::getCenterOfMassLocal(Entity bodyEntity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mCentersOfMassLocal[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return the world center of mass of an entity +inline const Vector3& DynamicsComponents::getCenterOfMassWorld(Entity bodyEntity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mCentersOfMassWorld[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Set the constrained linear velocity of an entity +inline void DynamicsComponents::setConstrainedLinearVelocity(Entity bodyEntity, const Vector3& constrainedLinearVelocity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mConstrainedLinearVelocities[mMapEntityToComponentIndex[bodyEntity]] = constrainedLinearVelocity; +} + +// Set the constrained angular velocity of an entity +inline void DynamicsComponents::setConstrainedAngularVelocity(Entity bodyEntity, const Vector3& constrainedAngularVelocity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mConstrainedAngularVelocities[mMapEntityToComponentIndex[bodyEntity]] = constrainedAngularVelocity; +} + +// Set the split linear velocity of an entity +inline void DynamicsComponents::setSplitLinearVelocity(Entity bodyEntity, const Vector3& splitLinearVelocity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mSplitLinearVelocities[mMapEntityToComponentIndex[bodyEntity]] = splitLinearVelocity; +} + +// Set the split angular velocity of an entity +inline void DynamicsComponents::setSplitAngularVelocity(Entity bodyEntity, const Vector3& splitAngularVelocity) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mSplitAngularVelocities[mMapEntityToComponentIndex[bodyEntity]] = splitAngularVelocity; +} + +// Set the external force of an entity +inline void DynamicsComponents::setExternalForce(Entity bodyEntity, const Vector3& externalForce) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mExternalForces[mMapEntityToComponentIndex[bodyEntity]] = externalForce; +} + +// Set the external force of an entity +inline void DynamicsComponents::setExternalTorque(Entity bodyEntity, const Vector3& externalTorque) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mExternalTorques[mMapEntityToComponentIndex[bodyEntity]] = externalTorque; +} + +// Set the linear damping factor of an entity +inline void DynamicsComponents::setLinearDamping(Entity bodyEntity, decimal linearDamping) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mLinearDampings[mMapEntityToComponentIndex[bodyEntity]] = linearDamping; +} + +// Set the angular damping factor of an entity +inline void DynamicsComponents::setAngularDamping(Entity bodyEntity, decimal angularDamping) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mAngularDampings[mMapEntityToComponentIndex[bodyEntity]] = angularDamping; +} + +// Set the initial mass of an entity +inline void DynamicsComponents::setInitMass(Entity bodyEntity, decimal initMass) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mInitMasses[mMapEntityToComponentIndex[bodyEntity]] = initMass; +} + +// Set the mass inverse of an entity +inline void DynamicsComponents::setMassInverse(Entity bodyEntity, decimal inverseMass) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mInverseMasses[mMapEntityToComponentIndex[bodyEntity]] = inverseMass; +} + +// Set the inverse local inertia tensor of an entity +inline void DynamicsComponents::setInverseInertiaTensorLocal(Entity bodyEntity, const Matrix3x3& inertiaTensorLocalInverse) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mInverseInertiaTensorsLocal[mMapEntityToComponentIndex[bodyEntity]] = inertiaTensorLocalInverse; +} + +// Set the inverse world inertia tensor of an entity +inline void DynamicsComponents::setInverseInertiaTensorWorld(Entity bodyEntity, const Matrix3x3& inertiaTensorWorldInverse) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mInverseInertiaTensorsWorld[mMapEntityToComponentIndex[bodyEntity]] = inertiaTensorWorldInverse; +} + +// Set the constrained position of an entity +inline void DynamicsComponents::setConstrainedPosition(Entity bodyEntity, const Vector3& constrainedPosition) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mConstrainedPositions[mMapEntityToComponentIndex[bodyEntity]] = constrainedPosition; +} + +// Set the constrained orientation of an entity +inline void DynamicsComponents::setConstrainedOrientation(Entity bodyEntity, const Quaternion& constrainedOrientation) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mConstrainedOrientations[mMapEntityToComponentIndex[bodyEntity]] = constrainedOrientation; +} + +// Set the local center of mass of an entity +inline void DynamicsComponents::setCenterOfMassLocal(Entity bodyEntity, const Vector3& centerOfMassLocal) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mCentersOfMassLocal[mMapEntityToComponentIndex[bodyEntity]] = centerOfMassLocal; +} + +// Set the world center of mass of an entity +inline void DynamicsComponents::setCenterOfMassWorld(Entity bodyEntity, const Vector3& centerOfMassWorld) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mCentersOfMassWorld[mMapEntityToComponentIndex[bodyEntity]] = centerOfMassWorld; +} + +// Return true if gravity is enabled for this entity +inline bool DynamicsComponents::getIsGravityEnabled(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mIsGravityEnabled[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Return true if the entity is already in an island +inline bool DynamicsComponents::getIsAlreadyInIsland(Entity bodyEntity) const { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + return mIsAlreadyInIsland[mMapEntityToComponentIndex[bodyEntity]]; +} + +// Set the value to know if the gravity is enabled for this entity +inline bool DynamicsComponents::setIsGravityEnabled(Entity bodyEntity, bool isGravityEnabled) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mIsGravityEnabled[mMapEntityToComponentIndex[bodyEntity]] = isGravityEnabled; +} + +/// Set the value to know if the entity is already in an island +inline bool DynamicsComponents::setIsAlreadyInIsland(Entity bodyEntity, bool isAlreadyInIsland) { + + assert(mMapEntityToComponentIndex.containsKey(bodyEntity)); + + mIsAlreadyInIsland[mMapEntityToComponentIndex[bodyEntity]] = isAlreadyInIsland; +} + } #endif diff --git a/src/configuration.h b/src/configuration.h index 2686635a..3dae1e18 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -129,7 +129,7 @@ struct WorldSettings { decimal defaultBounciness = decimal(0.5); /// Velocity threshold for contact velocity restitution - decimal restitutionVelocityThreshold = decimal(1.0); + decimal restitutionVelocityThreshold = decimal(0.5); /// Default rolling resistance decimal defaultRollingRestistance = decimal(0.0); @@ -154,13 +154,8 @@ struct WorldSettings { /// might enter sleeping mode decimal defaultSleepAngularVelocity = decimal(3.0) * (PI / decimal(180.0)); - /// Maximum number of contact manifolds in an overlapping pair that involves two - /// convex collision shapes. - int nbMaxContactManifoldsConvexShape = 1; - - /// Maximum number of contact manifolds in an overlapping pair that involves at - /// least one concave collision shape. - int nbMaxContactManifoldsConcaveShape = 3; + /// Maximum number of contact manifolds in an overlapping pair + uint nbMaxContactManifolds = 3; /// This is used to test if two contact manifold are similar (same contact normal) in order to /// merge them. If the cosine of the angle between the normals of the two manifold are larger @@ -184,8 +179,7 @@ struct WorldSettings { ss << "defaultTimeBeforeSleep=" << defaultTimeBeforeSleep << std::endl; ss << "defaultSleepLinearVelocity=" << defaultSleepLinearVelocity << std::endl; ss << "defaultSleepAngularVelocity=" << defaultSleepAngularVelocity << std::endl; - ss << "nbMaxContactManifoldsConvexShape=" << nbMaxContactManifoldsConvexShape << std::endl; - ss << "nbMaxContactManifoldsConcaveShape=" << nbMaxContactManifoldsConcaveShape << std::endl; + ss << "nbMaxContactManifolds=" << nbMaxContactManifolds << std::endl; ss << "cosAngleSimilarContactManifold=" << cosAngleSimilarContactManifold << std::endl; return ss.str(); diff --git a/src/constraint/BallAndSocketJoint.cpp b/src/constraint/BallAndSocketJoint.cpp index 0da68185..b795560d 100644 --- a/src/constraint/BallAndSocketJoint.cpp +++ b/src/constraint/BallAndSocketJoint.cpp @@ -26,6 +26,7 @@ // Libraries #include "BallAndSocketJoint.h" #include "engine/ConstraintSolver.h" +#include "components/DynamicsComponents.h" using namespace reactphysics3d; @@ -44,13 +45,9 @@ BallAndSocketJoint::BallAndSocketJoint(uint id, const BallAndSocketJointInfo& jo // Initialize before solving the constraint void BallAndSocketJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverData) { - // Initialize the bodies index in the velocity array - mIndexBody1 = mBody1->mArrayIndex; - mIndexBody2 = mBody2->mArrayIndex; - // Get the bodies center of mass and orientations - const Vector3& x1 = mBody1->mCenterOfMassWorld; - const Vector3& x2 = mBody2->mCenterOfMassWorld; + const Vector3& x1 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody1Entity); + const Vector3& x2 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody2Entity); const Quaternion& orientationBody1 = mBody1->getTransform().getOrientation(); const Quaternion& orientationBody2 = mBody2->getTransform().getOrientation(); @@ -67,7 +64,9 @@ void BallAndSocketJoint::initBeforeSolve(const ConstraintSolverData& constraintS Matrix3x3 skewSymmetricMatrixU2= Matrix3x3::computeSkewSymmetricMatrixForCrossProduct(mR2World); // Compute the matrix K=JM^-1J^t (3x3 matrix) - decimal inverseMassBodies = mBody1->mMassInverse + mBody2->mMassInverse; + decimal body1MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody1->getEntity()); + decimal body2MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody2->getEntity()); + decimal inverseMassBodies = body1MassInverse + body2MassInverse; Matrix3x3 massMatrix = Matrix3x3(inverseMassBodies, 0, 0, 0, inverseMassBodies, 0, 0, 0, inverseMassBodies) + @@ -98,36 +97,42 @@ void BallAndSocketJoint::initBeforeSolve(const ConstraintSolverData& constraintS // Warm start the constraint (apply the previous impulse at the beginning of the step) void BallAndSocketJoint::warmstart(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Compute the impulse P=J^T * lambda for the body 1 const Vector3 linearImpulseBody1 = -mImpulse; const Vector3 angularImpulseBody1 = mImpulse.cross(mR1World); // Apply the impulse to the body 1 - v1 += mBody1->mMassInverse * linearImpulseBody1; + v1 += constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity) * linearImpulseBody1; w1 += mI1 * angularImpulseBody1; // Compute the impulse P=J^T * lambda for the body 2 const Vector3 angularImpulseBody2 = -mImpulse.cross(mR2World); // Apply the impulse to the body to the body 2 - v2 += mBody2->mMassInverse * mImpulse; + v2 += constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity) * mImpulse; w2 += mI2 * angularImpulseBody2; } // Solve the velocity constraint void BallAndSocketJoint::solveVelocityConstraint(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Compute J*v const Vector3 Jv = v2 + w2.cross(mR2World) - v1 - w1.cross(mR1World); @@ -141,14 +146,14 @@ void BallAndSocketJoint::solveVelocityConstraint(const ConstraintSolverData& con const Vector3 angularImpulseBody1 = deltaLambda.cross(mR1World); // Apply the impulse to the body 1 - v1 += mBody1->mMassInverse * linearImpulseBody1; + v1 += constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity) * linearImpulseBody1; w1 += mI1 * angularImpulseBody1; // Compute the impulse P=J^T * lambda for the body 2 const Vector3 angularImpulseBody2 = -deltaLambda.cross(mR2World); // Apply the impulse to the body 2 - v2 += mBody2->mMassInverse * deltaLambda; + v2 += constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity) * deltaLambda; w2 += mI2 * angularImpulseBody2; } @@ -160,14 +165,14 @@ void BallAndSocketJoint::solvePositionConstraint(const ConstraintSolverData& con if (mPositionCorrectionTechnique != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) return; // Get the bodies center of mass and orientations - Vector3& x1 = constraintSolverData.positions[mIndexBody1]; - Vector3& x2 = constraintSolverData.positions[mIndexBody2]; - Quaternion& q1 = constraintSolverData.orientations[mIndexBody1]; - Quaternion& q2 = constraintSolverData.orientations[mIndexBody2]; + Vector3 x1 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody1Entity); + Vector3 x2 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody2Entity); + Quaternion q1 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody1Entity); + Quaternion q2 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody2Entity); // Get the inverse mass and inverse inertia tensors of the bodies - decimal inverseMassBody1 = mBody1->mMassInverse; - decimal inverseMassBody2 = mBody2->mMassInverse; + const decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // Recompute the inverse inertia tensors mI1 = mBody1->getInertiaTensorInverseWorld(); @@ -225,5 +230,10 @@ void BallAndSocketJoint::solvePositionConstraint(const ConstraintSolverData& con x2 += v2; q2 += Quaternion(0, w2) * q2 * decimal(0.5); q2.normalize(); + + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody1Entity, x1); + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody2Entity, x2); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody1Entity, q1); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody2Entity, q2); } diff --git a/src/constraint/ContactPoint.cpp b/src/constraint/ContactPoint.cpp index 9be0f1d0..e3290b4b 100644 --- a/src/constraint/ContactPoint.cpp +++ b/src/constraint/ContactPoint.cpp @@ -45,6 +45,21 @@ ContactPoint::ContactPoint(const ContactPointInfo* contactInfo, const WorldSetti mIsObsolete = false; } +// Constructor +ContactPoint::ContactPoint(const ContactPointInfo& contactInfo, const WorldSettings& worldSettings) + : mNormal(contactInfo.normal), + mPenetrationDepth(contactInfo.penetrationDepth), + mLocalPointOnShape1(contactInfo.localPoint1), + mLocalPointOnShape2(contactInfo.localPoint2), + mIsRestingContact(false), mPenetrationImpulse(0), mIsObsolete(false), + mWorldSettings(worldSettings) { + + assert(mPenetrationDepth > decimal(0.0)); + assert(mNormal.lengthSquare() > decimal(0.8)); + + mIsObsolete = false; +} + // Update the contact point with a new one that is similar (very close) /// The idea is to keep the cache impulse (for warm starting the contact solver) void ContactPoint::update(const ContactPointInfo* contactInfo) { diff --git a/src/constraint/ContactPoint.h b/src/constraint/ContactPoint.h index e705c7bf..c681396d 100644 --- a/src/constraint/ContactPoint.h +++ b/src/constraint/ContactPoint.h @@ -93,18 +93,6 @@ class ContactPoint { /// Set the mIsRestingContact variable void setIsRestingContact(bool isRestingContact); - /// Set to true to make the contact point obsolete - void setIsObsolete(bool isObselete); - - /// Set the next contact point in the linked list - void setNext(ContactPoint* next); - - /// Set the previous contact point in the linked list - void setPrevious(ContactPoint* previous); - - /// Return true if the contact point is obsolete - bool getIsObsolete() const; - public : // -------------------- Methods -------------------- // @@ -112,17 +100,20 @@ class ContactPoint { /// Constructor ContactPoint(const ContactPointInfo* contactInfo, const WorldSettings& worldSettings); + /// Constructor + ContactPoint(const ContactPointInfo& contactInfo, const WorldSettings& worldSettings); + /// Destructor ~ContactPoint() = default; - /// Deleted copy-constructor - ContactPoint(const ContactPoint& contact) = delete; + /// Copy-constructor + ContactPoint(const ContactPoint& contact) = default; - /// Deleted assignment operator - ContactPoint& operator=(const ContactPoint& contact) = delete; + /// Assignment operator + ContactPoint& operator=(const ContactPoint& contact) = default; /// Return the normal vector of the contact - Vector3 getNormal() const; + const Vector3& getNormal() const; /// Return the contact point on the first proxy shape in the local-space of the proxy shape const Vector3& getLocalPointOnShape1() const; @@ -136,12 +127,6 @@ class ContactPoint { /// Return true if the contact is a resting contact bool getIsRestingContact() const; - /// Return the previous contact point in the linked list - inline ContactPoint* getPrevious() const; - - /// Return the next contact point in the linked list - ContactPoint* getNext() const; - /// Return the penetration depth decimal getPenetrationDepth() const; @@ -152,13 +137,14 @@ class ContactPoint { friend class ContactManifold; friend class ContactManifoldSet; friend class ContactSolver; + friend class CollisionDetection; }; // Return the normal vector of the contact /** * @return The contact normal */ -inline Vector3 ContactPoint::getNormal() const { +inline const Vector3& ContactPoint::getNormal() const { return mNormal; } @@ -216,54 +202,6 @@ inline void ContactPoint::setIsRestingContact(bool isRestingContact) { mIsRestingContact = isRestingContact; } -// Return true if the contact point is obsolete -/** - * @return True if the contact is obsolete - */ -inline bool ContactPoint::getIsObsolete() const { - return mIsObsolete; -} - -// Set to true to make the contact point obsolete -/** - * @param isObsolete True if the contact is obsolete - */ -inline void ContactPoint::setIsObsolete(bool isObsolete) { - mIsObsolete = isObsolete; -} - -// Return the next contact point in the linked list -/** - * @return A pointer to the next contact point in the linked-list of points - */ -inline ContactPoint* ContactPoint::getNext() const { - return mNext; -} - -// Set the next contact point in the linked list -/** - * @param next Pointer to the next contact point in the linked-list of points - */ -inline void ContactPoint::setNext(ContactPoint* next) { - mNext = next; -} - -// Return the previous contact point in the linked list -/** - * @return A pointer to the previous contact point in the linked-list of points - */ -inline ContactPoint* ContactPoint::getPrevious() const { - return mPrevious; -} - -// Set the previous contact point in the linked list -/** - * @param previous Pointer to the previous contact point in the linked-list of points - */ -inline void ContactPoint::setPrevious(ContactPoint* previous) { - mPrevious = previous; -} - // Return the penetration depth of the contact /** * @return the penetration depth (in meters) diff --git a/src/constraint/FixedJoint.cpp b/src/constraint/FixedJoint.cpp index 99fca6c2..a195e974 100644 --- a/src/constraint/FixedJoint.cpp +++ b/src/constraint/FixedJoint.cpp @@ -26,6 +26,7 @@ // Libraries #include "FixedJoint.h" #include "engine/ConstraintSolver.h" +#include "components/DynamicsComponents.h" using namespace reactphysics3d; @@ -59,13 +60,9 @@ FixedJoint::FixedJoint(uint id, const FixedJointInfo& jointInfo) // Initialize before solving the constraint void FixedJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverData) { - // Initialize the bodies index in the velocity array - mIndexBody1 = mBody1->mArrayIndex; - mIndexBody2 = mBody2->mArrayIndex; - // Get the bodies positions and orientations - const Vector3& x1 = mBody1->mCenterOfMassWorld; - const Vector3& x2 = mBody2->mCenterOfMassWorld; + const Vector3& x1 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody1Entity); + const Vector3& x2 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody2Entity); const Quaternion& orientationBody1 = mBody1->getTransform().getOrientation(); const Quaternion& orientationBody2 = mBody2->getTransform().getOrientation(); @@ -82,7 +79,9 @@ void FixedJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDat Matrix3x3 skewSymmetricMatrixU2= Matrix3x3::computeSkewSymmetricMatrixForCrossProduct(mR2World); // Compute the matrix K=JM^-1J^t (3x3 matrix) for the 3 translation constraints - decimal inverseMassBodies = mBody1->mMassInverse + mBody2->mMassInverse; + const decimal body1MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody1->getEntity()); + const decimal body2MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody2->getEntity()); + const decimal inverseMassBodies = body1MassInverse + body2MassInverse; Matrix3x3 massMatrix = Matrix3x3(inverseMassBodies, 0, 0, 0, inverseMassBodies, 0, 0, 0, inverseMassBodies) + @@ -129,15 +128,18 @@ void FixedJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDat // Warm start the constraint (apply the previous impulse at the beginning of the step) void FixedJoint::warmstart(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Get the inverse mass of the bodies - const decimal inverseMassBody1 = mBody1->mMassInverse; - const decimal inverseMassBody2 = mBody2->mMassInverse; + const decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // Compute the impulse P=J^T * lambda for the 3 translation constraints for body 1 Vector3 linearImpulseBody1 = -mImpulseTranslation; @@ -164,15 +166,18 @@ void FixedJoint::warmstart(const ConstraintSolverData& constraintSolverData) { // Solve the velocity constraint void FixedJoint::solveVelocityConstraint(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Get the inverse mass of the bodies - decimal inverseMassBody1 = mBody1->mMassInverse; - decimal inverseMassBody2 = mBody2->mMassInverse; + decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // --------------- Translation Constraints --------------- // @@ -226,14 +231,14 @@ void FixedJoint::solvePositionConstraint(const ConstraintSolverData& constraintS if (mPositionCorrectionTechnique != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) return; // Get the bodies positions and orientations - Vector3& x1 = constraintSolverData.positions[mIndexBody1]; - Vector3& x2 = constraintSolverData.positions[mIndexBody2]; - Quaternion& q1 = constraintSolverData.orientations[mIndexBody1]; - Quaternion& q2 = constraintSolverData.orientations[mIndexBody2]; + Vector3 x1 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody1Entity); + Vector3 x2 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody2Entity); + Quaternion q1 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody1Entity); + Quaternion q2 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody2Entity); // Get the inverse mass and inverse inertia tensors of the bodies - decimal inverseMassBody1 = mBody1->mMassInverse; - decimal inverseMassBody2 = mBody2->mMassInverse; + decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // Recompute the inverse inertia tensors mI1 = mBody1->getInertiaTensorInverseWorld(); @@ -250,7 +255,7 @@ void FixedJoint::solvePositionConstraint(const ConstraintSolverData& constraintS // --------------- Translation Constraints --------------- // // Compute the matrix K=JM^-1J^t (3x3 matrix) for the 3 translation constraints - decimal inverseMassBodies = mBody1->mMassInverse + mBody2->mMassInverse; + decimal inverseMassBodies = inverseMassBody1 + inverseMassBody2; Matrix3x3 massMatrix = Matrix3x3(inverseMassBodies, 0, 0, 0, inverseMassBodies, 0, 0, 0, inverseMassBodies) + @@ -348,5 +353,10 @@ void FixedJoint::solvePositionConstraint(const ConstraintSolverData& constraintS // Update the body position/orientation of body 2 q2 += Quaternion(0, w2) * q2 * decimal(0.5); q2.normalize(); + + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody1Entity, x1); + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody2Entity, x2); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody1Entity, q1); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody2Entity, q2); } diff --git a/src/constraint/HingeJoint.cpp b/src/constraint/HingeJoint.cpp index 50106d13..a1651027 100644 --- a/src/constraint/HingeJoint.cpp +++ b/src/constraint/HingeJoint.cpp @@ -26,6 +26,7 @@ // Libraries #include "HingeJoint.h" #include "engine/ConstraintSolver.h" +#include "components/DynamicsComponents.h" using namespace reactphysics3d; @@ -66,13 +67,9 @@ HingeJoint::HingeJoint(uint id, const HingeJointInfo& jointInfo) // Initialize before solving the constraint void HingeJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverData) { - // Initialize the bodies index in the velocity array - mIndexBody1 = mBody1->mArrayIndex; - mIndexBody2 = mBody2->mArrayIndex; - // Get the bodies positions and orientations - const Vector3& x1 = mBody1->mCenterOfMassWorld; - const Vector3& x2 = mBody2->mCenterOfMassWorld; + const Vector3& x1 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody1Entity); + const Vector3& x2 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody2Entity); const Quaternion& orientationBody1 = mBody1->getTransform().getOrientation(); const Quaternion& orientationBody2 = mBody2->getTransform().getOrientation(); @@ -116,7 +113,9 @@ void HingeJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDat Matrix3x3 skewSymmetricMatrixU2= Matrix3x3::computeSkewSymmetricMatrixForCrossProduct(mR2World); // Compute the inverse mass matrix K=JM^-1J^t for the 3 translation constraints (3x3 matrix) - decimal inverseMassBodies = mBody1->mMassInverse + mBody2->mMassInverse; + decimal body1MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody1->getEntity()); + decimal body2MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody2->getEntity()); + decimal inverseMassBodies = body1MassInverse + body2MassInverse; Matrix3x3 massMatrix = Matrix3x3(inverseMassBodies, 0, 0, 0, inverseMassBodies, 0, 0, 0, inverseMassBodies) + @@ -198,15 +197,18 @@ void HingeJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDat // Warm start the constraint (apply the previous impulse at the beginning of the step) void HingeJoint::warmstart(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Get the inverse mass and inverse inertia tensors of the bodies - const decimal inverseMassBody1 = mBody1->mMassInverse; - const decimal inverseMassBody2 = mBody2->mMassInverse; + const decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // Compute the impulse P=J^T * lambda for the 2 rotation constraints Vector3 rotationImpulse = -mB2CrossA1 * mImpulseRotation.x - mC2CrossA1 * mImpulseRotation.y; @@ -254,15 +256,18 @@ void HingeJoint::warmstart(const ConstraintSolverData& constraintSolverData) { // Solve the velocity constraint void HingeJoint::solveVelocityConstraint(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Get the inverse mass and inverse inertia tensors of the bodies - decimal inverseMassBody1 = mBody1->mMassInverse; - decimal inverseMassBody2 = mBody2->mMassInverse; + decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // --------------- Translation Constraints --------------- // @@ -405,14 +410,14 @@ void HingeJoint::solvePositionConstraint(const ConstraintSolverData& constraintS if (mPositionCorrectionTechnique != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) return; // Get the bodies positions and orientations - Vector3& x1 = constraintSolverData.positions[mIndexBody1]; - Vector3& x2 = constraintSolverData.positions[mIndexBody2]; - Quaternion& q1 = constraintSolverData.orientations[mIndexBody1]; - Quaternion& q2 = constraintSolverData.orientations[mIndexBody2]; + Vector3 x1 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody1Entity); + Vector3 x2 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody2Entity); + Quaternion q1 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody1Entity); + Quaternion q2 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody2Entity); // Get the inverse mass and inverse inertia tensors of the bodies - decimal inverseMassBody1 = mBody1->mMassInverse; - decimal inverseMassBody2 = mBody2->mMassInverse; + decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // Recompute the inverse inertia tensors mI1 = mBody1->getInertiaTensorInverseWorld(); @@ -448,7 +453,9 @@ void HingeJoint::solvePositionConstraint(const ConstraintSolverData& constraintS // --------------- Translation Constraints --------------- // // Compute the matrix K=JM^-1J^t (3x3 matrix) for the 3 translation constraints - decimal inverseMassBodies = mBody1->mMassInverse + mBody2->mMassInverse; + const decimal body1InverseMass = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal body2InverseMass = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); + decimal inverseMassBodies = body1InverseMass + body2InverseMass; Matrix3x3 massMatrix = Matrix3x3(inverseMassBodies, 0, 0, 0, inverseMassBodies, 0, 0, 0, inverseMassBodies) + @@ -603,6 +610,11 @@ void HingeJoint::solvePositionConstraint(const ConstraintSolverData& constraintS q2.normalize(); } } + + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody1Entity, x1); + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody2Entity, x2); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody1Entity, q1); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody2Entity, q2); } diff --git a/src/constraint/Joint.cpp b/src/constraint/Joint.cpp index a24b1f2e..f1ca34eb 100644 --- a/src/constraint/Joint.cpp +++ b/src/constraint/Joint.cpp @@ -30,7 +30,8 @@ using namespace reactphysics3d; // Constructor Joint::Joint(uint id, const JointInfo& jointInfo) - :mId(id), mBody1(jointInfo.body1), mBody2(jointInfo.body2), mType(jointInfo.type), + :mId(id), mBody1(jointInfo.body1), mBody2(jointInfo.body2), mBody1Entity(jointInfo.body1->getEntity()), + mBody2Entity(jointInfo.body2->getEntity()), mType(jointInfo.type), mPositionCorrectionTechnique(jointInfo.positionCorrectionTechnique), mIsCollisionEnabled(jointInfo.isCollisionEnabled), mIsAlreadyInIsland(false) { diff --git a/src/constraint/Joint.h b/src/constraint/Joint.h index 1c89d464..38e26b5d 100644 --- a/src/constraint/Joint.h +++ b/src/constraint/Joint.h @@ -124,20 +124,22 @@ class Joint { uint mId; /// Pointer to the first body of the joint + // TODO : Use Entities instead RigidBody* const mBody1; /// Pointer to the second body of the joint + // TODO : Use Entities instead RigidBody* const mBody2; + /// Entity of the first body of the joint + Entity mBody1Entity; + + /// Entity of the second body of the joint + Entity mBody2Entity; + /// Type of the joint const JointType mType; - /// Body 1 index in the velocity array to solve the constraint - uint mIndexBody1; - - /// Body 2 index in the velocity array to solve the constraint - uint mIndexBody2; - /// Position correction technique used for the constraint (used for joints) JointsPositionCorrectionTechnique mPositionCorrectionTechnique; diff --git a/src/constraint/SliderJoint.cpp b/src/constraint/SliderJoint.cpp index b01fb410..48be0755 100644 --- a/src/constraint/SliderJoint.cpp +++ b/src/constraint/SliderJoint.cpp @@ -26,6 +26,7 @@ // Libraries #include "SliderJoint.h" #include "engine/ConstraintSolver.h" +#include "components/DynamicsComponents.h" using namespace reactphysics3d; @@ -74,13 +75,9 @@ SliderJoint::SliderJoint(uint id, const SliderJointInfo& jointInfo) // Initialize before solving the constraint void SliderJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverData) { - // Initialize the bodies index in the veloc ity array - mIndexBody1 = mBody1->mArrayIndex; - mIndexBody2 = mBody2->mArrayIndex; - // Get the bodies positions and orientations - const Vector3& x1 = mBody1->mCenterOfMassWorld; - const Vector3& x2 = mBody2->mCenterOfMassWorld; + const Vector3& x1 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody1Entity); + const Vector3& x2 = constraintSolverData.dynamicsComponents.getCenterOfMassWorld(mBody2Entity); const Quaternion& orientationBody1 = mBody1->getTransform().getOrientation(); const Quaternion& orientationBody2 = mBody2->getTransform().getOrientation(); @@ -127,7 +124,9 @@ void SliderJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDa // Compute the inverse of the mass matrix K=JM^-1J^t for the 2 translation // constraints (2x2 matrix) - decimal sumInverseMass = mBody1->mMassInverse + mBody2->mMassInverse; + const decimal body1MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody1->getEntity()); + const decimal body2MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody2->getEntity()); + const decimal sumInverseMass = body1MassInverse + body2MassInverse; Vector3 I1R1PlusUCrossN1 = mI1 * mR1PlusUCrossN1; Vector3 I1R1PlusUCrossN2 = mI1 * mR1PlusUCrossN2; Vector3 I2R2CrossN1 = mI2 * mR2CrossN1; @@ -173,7 +172,7 @@ void SliderJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDa if (mIsLimitEnabled && (mIsLowerLimitViolated || mIsUpperLimitViolated)) { // Compute the inverse of the mass matrix K=JM^-1J^t for the limits (1x1 matrix) - mInverseMassMatrixLimit = mBody1->mMassInverse + mBody2->mMassInverse + + mInverseMassMatrixLimit = sumInverseMass + mR1PlusUCrossSliderAxis.dot(mI1 * mR1PlusUCrossSliderAxis) + mR2CrossSliderAxis.dot(mI2 * mR2CrossSliderAxis); mInverseMassMatrixLimit = (mInverseMassMatrixLimit > 0.0) ? @@ -196,7 +195,7 @@ void SliderJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDa if (mIsMotorEnabled) { // Compute the inverse of mass matrix K=JM^-1J^t for the motor (1x1 matrix) - mInverseMassMatrixMotor = mBody1->mMassInverse + mBody2->mMassInverse; + mInverseMassMatrixMotor = sumInverseMass; mInverseMassMatrixMotor = (mInverseMassMatrixMotor > 0.0) ? decimal(1.0) / mInverseMassMatrixMotor : decimal(0.0); } @@ -216,15 +215,18 @@ void SliderJoint::initBeforeSolve(const ConstraintSolverData& constraintSolverDa // Warm start the constraint (apply the previous impulse at the beginning of the step) void SliderJoint::warmstart(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Get the inverse mass and inverse inertia tensors of the bodies - const decimal inverseMassBody1 = mBody1->mMassInverse; - const decimal inverseMassBody2 = mBody2->mMassInverse; + const decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // Compute the impulse P=J^T * lambda for the lower and upper limits constraints of body 1 decimal impulseLimits = mImpulseUpperLimit - mImpulseLowerLimit; @@ -275,15 +277,18 @@ void SliderJoint::warmstart(const ConstraintSolverData& constraintSolverData) { // Solve the velocity constraint void SliderJoint::solveVelocityConstraint(const ConstraintSolverData& constraintSolverData) { + uint32 dynamicsComponentIndexBody1 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody1Entity); + uint32 dynamicsComponentIndexBody2 = constraintSolverData.dynamicsComponents.getEntityIndex(mBody2Entity); + // Get the velocities - Vector3& v1 = constraintSolverData.linearVelocities[mIndexBody1]; - Vector3& v2 = constraintSolverData.linearVelocities[mIndexBody2]; - Vector3& w1 = constraintSolverData.angularVelocities[mIndexBody1]; - Vector3& w2 = constraintSolverData.angularVelocities[mIndexBody2]; + Vector3& v1 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody1]; + Vector3& v2 = constraintSolverData.dynamicsComponents.mConstrainedLinearVelocities[dynamicsComponentIndexBody2]; + Vector3& w1 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody1]; + Vector3& w2 = constraintSolverData.dynamicsComponents.mConstrainedAngularVelocities[dynamicsComponentIndexBody2]; // Get the inverse mass and inverse inertia tensors of the bodies - decimal inverseMassBody1 = mBody1->mMassInverse; - decimal inverseMassBody2 = mBody2->mMassInverse; + decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // --------------- Translation Constraints --------------- // @@ -437,14 +442,14 @@ void SliderJoint::solvePositionConstraint(const ConstraintSolverData& constraint if (mPositionCorrectionTechnique != JointsPositionCorrectionTechnique::NON_LINEAR_GAUSS_SEIDEL) return; // Get the bodies positions and orientations - Vector3& x1 = constraintSolverData.positions[mIndexBody1]; - Vector3& x2 = constraintSolverData.positions[mIndexBody2]; - Quaternion& q1 = constraintSolverData.orientations[mIndexBody1]; - Quaternion& q2 = constraintSolverData.orientations[mIndexBody2]; + Vector3 x1 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody1Entity); + Vector3 x2 = constraintSolverData.dynamicsComponents.getConstrainedPosition(mBody2Entity); + Quaternion q1 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody1Entity); + Quaternion q2 = constraintSolverData.dynamicsComponents.getConstrainedOrientation(mBody2Entity); // Get the inverse mass and inverse inertia tensors of the bodies - decimal inverseMassBody1 = mBody1->mMassInverse; - decimal inverseMassBody2 = mBody2->mMassInverse; + const decimal inverseMassBody1 = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal inverseMassBody2 = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); // Recompute the inertia tensor of bodies mI1 = mBody1->getInertiaTensorInverseWorld(); @@ -483,7 +488,9 @@ void SliderJoint::solvePositionConstraint(const ConstraintSolverData& constraint // Recompute the inverse of the mass matrix K=JM^-1J^t for the 2 translation // constraints (2x2 matrix) - decimal sumInverseMass = mBody1->mMassInverse + mBody2->mMassInverse; + const decimal body1MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal body2MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); + decimal sumInverseMass = body1MassInverse + body2MassInverse; Vector3 I1R1PlusUCrossN1 = mI1 * mR1PlusUCrossN1; Vector3 I1R1PlusUCrossN2 = mI1 * mR1PlusUCrossN2; Vector3 I2R2CrossN1 = mI2 * mR2CrossN1; @@ -603,7 +610,9 @@ void SliderJoint::solvePositionConstraint(const ConstraintSolverData& constraint if (mIsLowerLimitViolated || mIsUpperLimitViolated) { // Compute the inverse of the mass matrix K=JM^-1J^t for the limits (1x1 matrix) - mInverseMassMatrixLimit = mBody1->mMassInverse + mBody2->mMassInverse + + const decimal body1MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody1Entity); + const decimal body2MassInverse = constraintSolverData.dynamicsComponents.getMassInverse(mBody2Entity); + mInverseMassMatrixLimit = body1MassInverse + body2MassInverse + mR1PlusUCrossSliderAxis.dot(mI1 * mR1PlusUCrossSliderAxis) + mR2CrossSliderAxis.dot(mI2 * mR2CrossSliderAxis); mInverseMassMatrixLimit = (mInverseMassMatrixLimit > 0.0) ? @@ -676,6 +685,11 @@ void SliderJoint::solvePositionConstraint(const ConstraintSolverData& constraint q2.normalize(); } } + + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody1Entity, x1); + constraintSolverData.dynamicsComponents.setConstrainedPosition(mBody2Entity, x2); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody1Entity, q1); + constraintSolverData.dynamicsComponents.setConstrainedOrientation(mBody2Entity, q2); } // Enable/Disable the limits of the joint diff --git a/src/containers/Set.h b/src/containers/Set.h index 93cba1ca..c6162c44 100755 --- a/src/containers/Set.h +++ b/src/containers/Set.h @@ -543,7 +543,7 @@ class Set { } /// Return a list with all the values of the set - List<V> toList(MemoryAllocator& listAllocator) { + List<V> toList(MemoryAllocator& listAllocator) const { List<V> list(listAllocator); diff --git a/src/engine/CollisionWorld.cpp b/src/engine/CollisionWorld.cpp index 09809a17..c63d017a 100644 --- a/src/engine/CollisionWorld.cpp +++ b/src/engine/CollisionWorld.cpp @@ -202,9 +202,6 @@ void CollisionWorld::destroyCollisionBody(CollisionBody* collisionBody) { // Add the body ID to the list of free IDs mFreeBodiesIds.add(collisionBody->getId()); - // Reset the contact manifold list of the body - collisionBody->resetContactManifoldsList(); - mBodyComponents.removeComponent(collisionBody->getEntity()); mTransformComponents.removeComponent(collisionBody->getEntity()); mEntityManager.destroyEntity(collisionBody->getEntity()); @@ -236,17 +233,6 @@ bodyindex CollisionWorld::computeNextAvailableBodyId() { return bodyID; } -// Reset all the contact manifolds linked list of each body -void CollisionWorld::resetContactManifoldListsOfBodies() { - - // For each rigid body of the world - for (List<CollisionBody*>::Iterator it = mBodies.begin(); it != mBodies.end(); ++it) { - - // Reset the contact manifold list of the body - (*it)->resetContactManifoldsList(); - } -} - // Notify the world if a body is disabled (sleeping or inactive) or not void CollisionWorld::notifyBodyDisabled(Entity bodyEntity, bool isDisabled) { @@ -270,37 +256,10 @@ void CollisionWorld::notifyBodyDisabled(Entity bodyEntity, bool isDisabled) { } } -// Test if the AABBs of two bodies overlap -/** - * @param body1 Pointer to the first body to test - * @param body2 Pointer to the second body to test - * @return True if the AABBs of the two bodies overlap and false otherwise - */ -bool CollisionWorld::testAABBOverlap(const CollisionBody* body1, - const CollisionBody* body2) const { - - // If one of the body is not active, we return no overlap - if (!body1->isActive() || !body2->isActive()) return false; - - // Compute the AABBs of both bodies - AABB body1AABB = body1->getAABB(); - AABB body2AABB = body2->getAABB(); - - // Return true if the two AABBs overlap - return body1AABB.testCollision(body2AABB); -} - -// Report all the bodies which have an AABB that overlaps with the AABB in parameter -/** - * @param aabb AABB used to test for overlap - * @param overlapCallback Pointer to the callback class to report overlap - * @param categoryMaskBits bits mask used to filter the bodies to test overlap with - */ -void CollisionWorld::testAABBOverlap(const AABB& aabb, OverlapCallback* overlapCallback, unsigned short categoryMaskBits) { - mCollisionDetection.testAABBOverlap(aabb, overlapCallback, categoryMaskBits); -} - // Return true if two bodies overlap +/// Use this method if you are not interested in contacts but if you simply want to know +/// if the two bodies overlap. If you want to get the contacts, you need to use the +/// testCollision() method instead. /** * @param body1 Pointer to the first body * @param body2 Pointer to a second body diff --git a/src/engine/CollisionWorld.h b/src/engine/CollisionWorld.h index 3bf33372..f9d0b5d8 100644 --- a/src/engine/CollisionWorld.h +++ b/src/engine/CollisionWorld.h @@ -37,6 +37,8 @@ #include "components/TransformComponents.h" #include "components/ProxyShapeComponents.h" #include "components/DynamicsComponents.h" +#include "collision/CollisionCallback.h" +#include "collision/OverlapCallback.h" /// Namespace reactphysics3d namespace reactphysics3d { @@ -130,9 +132,6 @@ class CollisionWorld { /// Return the next available body id bodyindex computeNextAvailableBodyId(); - /// Reset all the contact manifolds linked list of each body - void resetContactManifoldListsOfBodies(); - /// Notify the world if a body is disabled (slepping or inactive) or not void notifyBodyDisabled(Entity entity, bool isDisabled); @@ -163,30 +162,25 @@ class CollisionWorld { CollisionDispatch& getCollisionDispatch(); /// Ray cast method - void raycast(const Ray& ray, RaycastCallback* raycastCallback, - unsigned short raycastWithCategoryMaskBits = 0xFFFF) const; + void raycast(const Ray& ray, RaycastCallback* raycastCallback, unsigned short raycastWithCategoryMaskBits = 0xFFFF) const; - /// Test if the AABBs of two bodies overlap - bool testAABBOverlap(const CollisionBody* body1, - const CollisionBody* body2) const; - - /// Report all the bodies which have an AABB that overlaps with the AABB in parameter - void testAABBOverlap(const AABB& aabb, OverlapCallback* overlapCallback, unsigned short categoryMaskBits = 0xFFFF); - - /// Return true if two bodies overlap + /// Return true if two bodies overlap (collide) bool testOverlap(CollisionBody* body1, CollisionBody* body2); - /// Report all the bodies that overlap with the body in parameter - void testOverlap(CollisionBody* body, OverlapCallback* overlapCallback, unsigned short categoryMaskBits = 0xFFFF); + /// Report all the bodies that overlap (collide) with the body in parameter + void testOverlap(CollisionBody* body, OverlapCallback& overlapCallback); - /// Test and report collisions between two bodies - void testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback* callback); + /// Report all the bodies that overlap (collide) in the world + void testOverlap(OverlapCallback& overlapCallback); - /// Test and report collisions between a body and all the others bodies of the world - void testCollision(CollisionBody* body, CollisionCallback* callback, unsigned short categoryMaskBits = 0xFFFF); + /// Test collision and report contacts between two bodies. + void testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback); - /// Test and report collisions between all shapes of the world - void testCollision(CollisionCallback* callback); + /// Test collision and report all the contacts involving the body in parameter + void testCollision(CollisionBody* body, CollisionCallback& callback); + + /// Test collision and report contacts between each colliding bodies in the world + void testCollision(CollisionCallback& callback); #ifdef IS_PROFILING_ACTIVE @@ -215,6 +209,8 @@ class CollisionWorld { friend class RigidBody; friend class ProxyShape; friend class ConvexMeshShape; + friend class CollisionCallback::ContactPair; + friend class OverlapCallback::OverlapPair; }; // Set the collision dispatch configuration @@ -241,42 +237,63 @@ inline void CollisionWorld::raycast(const Ray& ray, mCollisionDetection.raycast(raycastCallback, ray, raycastWithCategoryMaskBits); } -// Test and report collisions between two bodies +// Test collision and report contacts between two bodies. +/// Use this method if you only want to get all the contacts between two bodies. +/// All the contacts will be reported using the callback object in paramater. +/// If you are not interested in the contacts but you only want to know if the bodies collide, +/// you can use the testOverlap() method instead. /** * @param body1 Pointer to the first body to test * @param body2 Pointer to the second body to test * @param callback Pointer to the object with the callback method */ -inline void CollisionWorld::testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback* callback) { +inline void CollisionWorld::testCollision(CollisionBody* body1, CollisionBody* body2, CollisionCallback& callback) { mCollisionDetection.testCollision(body1, body2, callback); } -// Test and report collisions between a body and all the others bodies of the world +// Test collision and report all the contacts involving the body in parameter +/// Use this method if you only want to get all the contacts involving a given body. +/// All the contacts will be reported using the callback object in paramater. +/// If you are not interested in the contacts but you only want to know if the bodies collide, +/// you can use the testOverlap() method instead. /** * @param body Pointer to the body against which we need to test collision * @param callback Pointer to the object with the callback method to report contacts - * @param categoryMaskBits Bits mask corresponding to the category of bodies we need to test collision with */ -inline void CollisionWorld::testCollision(CollisionBody* body, CollisionCallback* callback, unsigned short categoryMaskBits) { - mCollisionDetection.testCollision(body, callback, categoryMaskBits); +inline void CollisionWorld::testCollision(CollisionBody* body, CollisionCallback& callback) { + mCollisionDetection.testCollision(body, callback); } -// Test and report collisions between all bodies of the world +// Test collision and report contacts between each colliding bodies in the world +/// Use this method if you want to get all the contacts between colliding bodies in the world. +/// All the contacts will be reported using the callback object in paramater. +/// If you are not interested in the contacts but you only want to know if the bodies collide, +/// you can use the testOverlap() method instead. /** * @param callback Pointer to the object with the callback method to report contacts */ -inline void CollisionWorld::testCollision(CollisionCallback* callback) { +inline void CollisionWorld::testCollision(CollisionCallback& callback) { mCollisionDetection.testCollision(callback); } -// Report all the bodies that overlap with the body in parameter +// Report all the bodies that overlap (collide) with the body in parameter +/// Use this method if you are not interested in contacts but if you simply want to know +/// which bodies overlap with the body in parameter. If you want to get the contacts, you need to use the +/// testCollision() method instead. /** * @param body Pointer to the collision body to test overlap with * @param overlapCallback Pointer to the callback class to report overlap - * @param categoryMaskBits bits mask used to filter the bodies to test overlap with */ -inline void CollisionWorld::testOverlap(CollisionBody* body, OverlapCallback* overlapCallback, unsigned short categoryMaskBits) { - mCollisionDetection.testOverlap(body, overlapCallback, categoryMaskBits); +inline void CollisionWorld::testOverlap(CollisionBody* body, OverlapCallback& overlapCallback) { + mCollisionDetection.testOverlap(body, overlapCallback); +} + +// Report all the bodies that overlap (collide) in the world +/// Use this method if you are not interested in contacts but if you simply want to know +/// which bodies overlap. If you want to get the contacts, you need to use the +/// testCollision() method instead. +inline void CollisionWorld::testOverlap(OverlapCallback& overlapCallback) { + mCollisionDetection.testOverlap(overlapCallback); } // Return the name of the world diff --git a/src/engine/ConstraintSolver.cpp b/src/engine/ConstraintSolver.cpp index 58844faa..2d95e041 100644 --- a/src/engine/ConstraintSolver.cpp +++ b/src/engine/ConstraintSolver.cpp @@ -31,7 +31,8 @@ using namespace reactphysics3d; // Constructor -ConstraintSolver::ConstraintSolver() : mIsWarmStartingActive(true) { +ConstraintSolver::ConstraintSolver(Islands& islands, DynamicsComponents& dynamicsComponents) + : mIsWarmStartingActive(true), mIslands(islands), mConstraintSolverData(dynamicsComponents) { #ifdef IS_PROFILING_ACTIVE @@ -42,13 +43,12 @@ ConstraintSolver::ConstraintSolver() : mIsWarmStartingActive(true) { } // Initialize the constraint solver for a given island -void ConstraintSolver::initializeForIsland(decimal dt, Island* island) { +void ConstraintSolver::initializeForIsland(decimal dt, uint islandIndex) { RP3D_PROFILE("ConstraintSolver::initializeForIsland()", mProfiler); - assert(island != nullptr); - assert(island->getNbBodies() > 0); - assert(island->getNbJoints() > 0); + assert(mIslands.bodyEntities[islandIndex].size() > 0); + assert(mIslands.joints[islandIndex].size() > 0); // Set the current time step mTimeStep = dt; @@ -58,49 +58,44 @@ void ConstraintSolver::initializeForIsland(decimal dt, Island* island) { mConstraintSolverData.isWarmStartingActive = mIsWarmStartingActive; // For each joint of the island - Joint** joints = island->getJoints(); - for (uint i=0; i<island->getNbJoints(); i++) { + for (uint i=0; i<mIslands.joints[islandIndex].size(); i++) { // Initialize the constraint before solving it - joints[i]->initBeforeSolve(mConstraintSolverData); + mIslands.joints[islandIndex][i]->initBeforeSolve(mConstraintSolverData); // Warm-start the constraint if warm-starting is enabled if (mIsWarmStartingActive) { - joints[i]->warmstart(mConstraintSolverData); + mIslands.joints[islandIndex][i]->warmstart(mConstraintSolverData); } } } // Solve the velocity constraints -void ConstraintSolver::solveVelocityConstraints(Island* island) { +void ConstraintSolver::solveVelocityConstraints(uint islandIndex) { RP3D_PROFILE("ConstraintSolver::solveVelocityConstraints()", mProfiler); - assert(island != nullptr); - assert(island->getNbJoints() > 0); + assert(mIslands.joints[islandIndex].size() > 0); // For each joint of the island - Joint** joints = island->getJoints(); - for (uint i=0; i<island->getNbJoints(); i++) { + for (uint i=0; i<mIslands.joints[islandIndex].size(); i++) { // Solve the constraint - joints[i]->solveVelocityConstraint(mConstraintSolverData); + mIslands.joints[islandIndex][i]->solveVelocityConstraint(mConstraintSolverData); } } // Solve the position constraints -void ConstraintSolver::solvePositionConstraints(Island* island) { +void ConstraintSolver::solvePositionConstraints(uint islandIndex) { RP3D_PROFILE("ConstraintSolver::solvePositionConstraints()", mProfiler); - assert(island != nullptr); - assert(island->getNbJoints() > 0); + assert(mIslands.joints[islandIndex].size() > 0); // For each joint of the island - Joint** joints = island->getJoints(); - for (uint i=0; i < island->getNbJoints(); i++) { + for (uint i=0; i<mIslands.joints[islandIndex].size(); i++) { // Solve the constraint - joints[i]->solvePositionConstraint(mConstraintSolverData); + mIslands.joints[islandIndex][i]->solvePositionConstraint(mConstraintSolverData); } } diff --git a/src/engine/ConstraintSolver.h b/src/engine/ConstraintSolver.h index 829d6e32..0a4ae48c 100644 --- a/src/engine/ConstraintSolver.h +++ b/src/engine/ConstraintSolver.h @@ -29,6 +29,7 @@ // Libraries #include "configuration.h" #include "mathematics/mathematics.h" +#include "engine/Islands.h" namespace reactphysics3d { @@ -36,6 +37,7 @@ namespace reactphysics3d { class Joint; class Island; class Profiler; +class DynamicsComponents; // Structure ConstraintSolverData /** @@ -49,24 +51,15 @@ struct ConstraintSolverData { /// Current time step of the simulation decimal timeStep; - /// Array with the bodies linear velocities - Vector3* linearVelocities; - - /// Array with the bodies angular velocities - Vector3* angularVelocities; - - /// Reference to the bodies positions - Vector3* positions; - - /// Reference to the bodies orientations - Quaternion* orientations; + /// Reference to the dynamics components + DynamicsComponents& dynamicsComponents; /// True if warm starting of the solver is active bool isWarmStartingActive; /// Constructor - ConstraintSolverData() :linearVelocities(nullptr), angularVelocities(nullptr), - positions(nullptr), orientations(nullptr) { + ConstraintSolverData(DynamicsComponents& dynamicsComponents) + :dynamicsComponents(dynamicsComponents) { } @@ -153,6 +146,9 @@ class ConstraintSolver { /// True if the warm starting of the solver is active bool mIsWarmStartingActive; + /// Reference to the islands + Islands& mIslands; + /// Constraint solver data used to initialize and solve the constraints ConstraintSolverData mConstraintSolverData; @@ -167,19 +163,19 @@ class ConstraintSolver { // -------------------- Methods -------------------- // /// Constructor - ConstraintSolver(); + ConstraintSolver(Islands& islands, DynamicsComponents& dynamicsComponents); /// Destructor ~ConstraintSolver() = default; /// Initialize the constraint solver for a given island - void initializeForIsland(decimal dt, Island* island); + void initializeForIsland(decimal dt, uint islandIndex); /// Solve the constraints - void solveVelocityConstraints(Island* island); + void solveVelocityConstraints(uint islandIndex); /// Solve the position constraints - void solvePositionConstraints(Island* island); + void solvePositionConstraints(uint islandIndex); /// Return true if the Non-Linear-Gauss-Seidel position correction technique is active bool getIsNonLinearGaussSeidelPositionCorrectionActive() const; @@ -187,14 +183,6 @@ class ConstraintSolver { /// Enable/Disable the Non-Linear-Gauss-Seidel position correction technique. void setIsNonLinearGaussSeidelPositionCorrectionActive(bool isActive); - /// Set the constrained velocities arrays - void setConstrainedVelocitiesArrays(Vector3* constrainedLinearVelocities, - Vector3* constrainedAngularVelocities); - - /// Set the constrained positions/orientations arrays - void setConstrainedPositionsArrays(Vector3* constrainedPositions, - Quaternion* constrainedOrientations); - #ifdef IS_PROFILING_ACTIVE /// Set the profiler @@ -204,28 +192,6 @@ class ConstraintSolver { }; -// Set the constrained velocities arrays -inline void ConstraintSolver::setConstrainedVelocitiesArrays(Vector3* constrainedLinearVelocities, - Vector3* constrainedAngularVelocities) { - - assert(constrainedLinearVelocities != nullptr); - assert(constrainedAngularVelocities != nullptr); - - mConstraintSolverData.linearVelocities = constrainedLinearVelocities; - mConstraintSolverData.angularVelocities = constrainedAngularVelocities; -} - -// Set the constrained positions/orientations arrays -inline void ConstraintSolver::setConstrainedPositionsArrays(Vector3* constrainedPositions, - Quaternion* constrainedOrientations) { - - assert(constrainedPositions != nullptr); - assert(constrainedOrientations != nullptr); - - mConstraintSolverData.positions = constrainedPositions; - mConstraintSolverData.orientations = constrainedOrientations; -} - #ifdef IS_PROFILING_ACTIVE // Set the profiler diff --git a/src/engine/ContactSolver.cpp b/src/engine/ContactSolver.cpp index 385cbe5e..a290a9d0 100644 --- a/src/engine/ContactSolver.cpp +++ b/src/engine/ContactSolver.cpp @@ -30,6 +30,9 @@ #include "constraint/ContactPoint.h" #include "utils/Profiler.h" #include "engine/Island.h" +#include "components/BodyComponents.h" +#include "components/DynamicsComponents.h" +#include "components/ProxyShapeComponents.h" #include "collision/ContactManifold.h" using namespace reactphysics3d; @@ -41,11 +44,12 @@ const decimal ContactSolver::BETA_SPLIT_IMPULSE = decimal(0.2); const decimal ContactSolver::SLOP = decimal(0.01); // Constructor -ContactSolver::ContactSolver(MemoryManager& memoryManager, const WorldSettings& worldSettings) - :mMemoryManager(memoryManager), mSplitLinearVelocities(nullptr), - mSplitAngularVelocities(nullptr), mContactConstraints(nullptr), - mContactPoints(nullptr), mLinearVelocities(nullptr), mAngularVelocities(nullptr), - mIsSplitImpulseActive(true), mWorldSettings(worldSettings) { +ContactSolver::ContactSolver(MemoryManager& memoryManager, Islands& islands, BodyComponents& bodyComponents, DynamicsComponents& dynamicsComponents, + ProxyShapeComponents& proxyShapeComponents, const WorldSettings& worldSettings) + :mMemoryManager(memoryManager), mContactConstraints(nullptr), mContactPoints(nullptr), + mIslands(islands), mAllContactManifolds(nullptr), mAllContactPoints(nullptr), mBodyComponents(bodyComponents), + mDynamicsComponents(dynamicsComponents), mProxyShapeComponents(proxyShapeComponents), mIsSplitImpulseActive(true), + mWorldSettings(worldSettings) { #ifdef IS_PROFILING_ACTIVE mProfiler = nullptr; @@ -54,23 +58,18 @@ ContactSolver::ContactSolver(MemoryManager& memoryManager, const WorldSettings& } // Initialize the contact constraints -void ContactSolver::init(Island** islands, uint nbIslands, decimal timeStep) { +void ContactSolver::init(List<ContactManifold>* contactManifolds, List<ContactPoint>* contactPoints, decimal timeStep) { + + mAllContactManifolds = contactManifolds; + mAllContactPoints = contactPoints; RP3D_PROFILE("ContactSolver::init()", mProfiler); mTimeStep = timeStep; // TODO : Try not to count manifolds and contact points here - uint nbContactManifolds = 0; - uint nbContactPoints = 0; - for (uint i = 0; i < nbIslands; i++) { - uint nbManifoldsInIsland = islands[i]->getNbContactManifolds(); - nbContactManifolds += nbManifoldsInIsland; - - for (uint j=0; j < nbManifoldsInIsland; j++) { - nbContactPoints += islands[i]->getContactManifolds()[j]->getNbContactPoints(); - } - } + uint nbContactManifolds = mAllContactManifolds->size(); + uint nbContactPoints = mAllContactPoints->size(); mNbContactManifolds = 0; mNbContactPoints = 0; @@ -90,10 +89,10 @@ void ContactSolver::init(Island** islands, uint nbIslands, decimal timeStep) { assert(mContactConstraints != nullptr); // For each island of the world - for (uint islandIndex = 0; islandIndex < nbIslands; islandIndex++) { + for (uint i = 0; i < mIslands.getNbIslands(); i++) { - if (islands[islandIndex]->getNbContactManifolds() > 0) { - initializeForIsland(islands[islandIndex]); + if (mIslands.nbContactManifolds[i] > 0) { + initializeForIsland(i); } } @@ -102,83 +101,87 @@ void ContactSolver::init(Island** islands, uint nbIslands, decimal timeStep) { } // Initialize the constraint solver for a given island -void ContactSolver::initializeForIsland(Island* island) { +void ContactSolver::initializeForIsland(uint islandIndex) { RP3D_PROFILE("ContactSolver::initializeForIsland()", mProfiler); - assert(island != nullptr); - assert(island->getNbBodies() > 0); - assert(island->getNbContactManifolds() > 0); - assert(mSplitLinearVelocities != nullptr); - assert(mSplitAngularVelocities != nullptr); + assert(mIslands.bodyEntities[islandIndex].size() > 0); + assert(mIslands.nbContactManifolds[islandIndex] > 0); // For each contact manifold of the island - ContactManifold** contactManifolds = island->getContactManifolds(); - for (uint i=0; i<island->getNbContactManifolds(); i++) { + uint contactManifoldsIndex = mIslands.contactManifoldsIndices[islandIndex]; + uint nbContactManifolds = mIslands.nbContactManifolds[islandIndex]; + for (uint m=contactManifoldsIndex; m < contactManifoldsIndex + nbContactManifolds; m++) { - ContactManifold* externalManifold = contactManifolds[i]; + ContactManifold& externalManifold = (*mAllContactManifolds)[m]; - assert(externalManifold->getNbContactPoints() > 0); + assert(externalManifold.getNbContactPoints() > 0); // Get the two bodies of the contact - RigidBody* body1 = static_cast<RigidBody*>(externalManifold->getBody1()); - RigidBody* body2 = static_cast<RigidBody*>(externalManifold->getBody2()); + RigidBody* body1 = static_cast<RigidBody*>(mBodyComponents.getBody(externalManifold.bodyEntity1)); + RigidBody* body2 = static_cast<RigidBody*>(mBodyComponents.getBody(externalManifold.bodyEntity2)); assert(body1 != nullptr); assert(body2 != nullptr); + assert(!mBodyComponents.getIsEntityDisabled(externalManifold.bodyEntity1)); + assert(!mBodyComponents.getIsEntityDisabled(externalManifold.bodyEntity2)); // Get the two contact shapes - const ProxyShape* shape1 = externalManifold->getShape1(); - const ProxyShape* shape2 = externalManifold->getShape2(); + // TODO : Do we really need to get the proxy-shape here + const ProxyShape* shape1 = mProxyShapeComponents.getProxyShape(externalManifold.proxyShapeEntity1); + const ProxyShape* shape2 = mProxyShapeComponents.getProxyShape(externalManifold.proxyShapeEntity2); // Get the position of the two bodies - const Vector3& x1 = body1->mCenterOfMassWorld; - const Vector3& x2 = body2->mCenterOfMassWorld; + const Vector3& x1 = mDynamicsComponents.getCenterOfMassWorld(externalManifold.bodyEntity1); + const Vector3& x2 = mDynamicsComponents.getCenterOfMassWorld(externalManifold.bodyEntity2); // Initialize the internal contact manifold structure using the external // contact manifold new (mContactConstraints + mNbContactManifolds) ContactManifoldSolver(); - mContactConstraints[mNbContactManifolds].indexBody1 = body1->mArrayIndex; - mContactConstraints[mNbContactManifolds].indexBody2 = body2->mArrayIndex; + mContactConstraints[mNbContactManifolds].dynamicsComponentIndexBody1 = mDynamicsComponents.getEntityIndex(body1->getEntity()); + mContactConstraints[mNbContactManifolds].dynamicsComponentIndexBody2 = mDynamicsComponents.getEntityIndex(body2->getEntity()); mContactConstraints[mNbContactManifolds].inverseInertiaTensorBody1 = body1->getInertiaTensorInverseWorld(); mContactConstraints[mNbContactManifolds].inverseInertiaTensorBody2 = body2->getInertiaTensorInverseWorld(); - mContactConstraints[mNbContactManifolds].massInverseBody1 = body1->mMassInverse; - mContactConstraints[mNbContactManifolds].massInverseBody2 = body2->mMassInverse; - mContactConstraints[mNbContactManifolds].nbContacts = externalManifold->getNbContactPoints(); + mContactConstraints[mNbContactManifolds].massInverseBody1 = mDynamicsComponents.getMassInverse(body1->getEntity()); + mContactConstraints[mNbContactManifolds].massInverseBody2 = mDynamicsComponents.getMassInverse(body2->getEntity()); + mContactConstraints[mNbContactManifolds].nbContacts = externalManifold.getNbContactPoints(); mContactConstraints[mNbContactManifolds].frictionCoefficient = computeMixedFrictionCoefficient(body1, body2); mContactConstraints[mNbContactManifolds].rollingResistanceFactor = computeMixedRollingResistance(body1, body2); - mContactConstraints[mNbContactManifolds].externalContactManifold = externalManifold; + mContactConstraints[mNbContactManifolds].externalContactManifold = &externalManifold; mContactConstraints[mNbContactManifolds].normal.setToZero(); mContactConstraints[mNbContactManifolds].frictionPointBody1.setToZero(); mContactConstraints[mNbContactManifolds].frictionPointBody2.setToZero(); // Get the velocities of the bodies - const Vector3& v1 = mLinearVelocities[mContactConstraints[mNbContactManifolds].indexBody1]; - const Vector3& w1 = mAngularVelocities[mContactConstraints[mNbContactManifolds].indexBody1]; - const Vector3& v2 = mLinearVelocities[mContactConstraints[mNbContactManifolds].indexBody2]; - const Vector3& w2 = mAngularVelocities[mContactConstraints[mNbContactManifolds].indexBody2]; + const Vector3& v1 = mDynamicsComponents.getLinearVelocity(externalManifold.bodyEntity1); + const Vector3& w1 = mDynamicsComponents.getAngularVelocity(externalManifold.bodyEntity1); + const Vector3& v2 = mDynamicsComponents.getLinearVelocity(externalManifold.bodyEntity2); + const Vector3& w2 = mDynamicsComponents.getAngularVelocity(externalManifold.bodyEntity2); // For each contact point of the contact manifold - ContactPoint* externalContact = externalManifold->getContactPoints(); - assert(externalContact != nullptr); - while (externalContact != nullptr) { + assert(externalManifold.getNbContactPoints() > 0); + uint contactPointsStartIndex = externalManifold.mContactPointsIndex; + uint nbContactPoints = externalManifold.mNbContactPoints; + for (uint c=contactPointsStartIndex; c < contactPointsStartIndex + nbContactPoints; c++) { + + ContactPoint& externalContact = (*mAllContactPoints)[c]; // Get the contact point on the two bodies - Vector3 p1 = shape1->getLocalToWorldTransform() * externalContact->getLocalPointOnShape1(); - Vector3 p2 = shape2->getLocalToWorldTransform() * externalContact->getLocalPointOnShape2(); + Vector3 p1 = shape1->getLocalToWorldTransform() * externalContact.getLocalPointOnShape1(); + Vector3 p2 = shape2->getLocalToWorldTransform() * externalContact.getLocalPointOnShape2(); new (mContactPoints + mNbContactPoints) ContactPointSolver(); - mContactPoints[mNbContactPoints].externalContact = externalContact; - mContactPoints[mNbContactPoints].normal = externalContact->getNormal(); + mContactPoints[mNbContactPoints].externalContact = &externalContact; + mContactPoints[mNbContactPoints].normal = externalContact.getNormal(); mContactPoints[mNbContactPoints].r1.x = p1.x - x1.x; mContactPoints[mNbContactPoints].r1.y = p1.y - x1.y; mContactPoints[mNbContactPoints].r1.z = p1.z - x1.z; mContactPoints[mNbContactPoints].r2.x = p2.x - x2.x; mContactPoints[mNbContactPoints].r2.y = p2.y - x2.y; mContactPoints[mNbContactPoints].r2.z = p2.z - x2.z; - mContactPoints[mNbContactPoints].penetrationDepth = externalContact->getPenetrationDepth(); - mContactPoints[mNbContactPoints].isRestingContact = externalContact->getIsRestingContact(); - externalContact->setIsRestingContact(true); - mContactPoints[mNbContactPoints].penetrationImpulse = externalContact->getPenetrationImpulse(); + mContactPoints[mNbContactPoints].penetrationDepth = externalContact.getPenetrationDepth(); + mContactPoints[mNbContactPoints].isRestingContact = externalContact.getIsRestingContact(); + externalContact.setIsRestingContact(true); + mContactPoints[mNbContactPoints].penetrationImpulse = externalContact.getPenetrationImpulse(); mContactPoints[mNbContactPoints].penetrationSplitImpulse = 0.0; mContactConstraints[mNbContactManifolds].frictionPointBody1.x += p1.x; @@ -240,8 +243,6 @@ void ContactSolver::initializeForIsland(Island* island) { mContactConstraints[mNbContactManifolds].normal.z += mContactPoints[mNbContactPoints].normal.z; mNbContactPoints++; - - externalContact = externalContact->getNext(); } mContactConstraints[mNbContactManifolds].frictionPointBody1 /=static_cast<decimal>(mContactConstraints[mNbContactManifolds].nbContacts); @@ -252,13 +253,13 @@ void ContactSolver::initializeForIsland(Island* island) { mContactConstraints[mNbContactManifolds].r2Friction.x = mContactConstraints[mNbContactManifolds].frictionPointBody2.x - x2.x; mContactConstraints[mNbContactManifolds].r2Friction.y = mContactConstraints[mNbContactManifolds].frictionPointBody2.y - x2.y; mContactConstraints[mNbContactManifolds].r2Friction.z = mContactConstraints[mNbContactManifolds].frictionPointBody2.z - x2.z; - mContactConstraints[mNbContactManifolds].oldFrictionVector1 = externalManifold->getFrictionVector1(); - mContactConstraints[mNbContactManifolds].oldFrictionVector2 = externalManifold->getFrictionVector2(); + mContactConstraints[mNbContactManifolds].oldFrictionVector1 = externalManifold.getFrictionVector1(); + mContactConstraints[mNbContactManifolds].oldFrictionVector2 = externalManifold.getFrictionVector2(); // Initialize the accumulated impulses with the previous step accumulated impulses - mContactConstraints[mNbContactManifolds].friction1Impulse = externalManifold->getFrictionImpulse1(); - mContactConstraints[mNbContactManifolds].friction2Impulse = externalManifold->getFrictionImpulse2(); - mContactConstraints[mNbContactManifolds].frictionTwistImpulse = externalManifold->getFrictionTwistImpulse(); + mContactConstraints[mNbContactManifolds].friction1Impulse = externalManifold.getFrictionImpulse1(); + mContactConstraints[mNbContactManifolds].friction2Impulse = externalManifold.getFrictionImpulse2(); + mContactConstraints[mNbContactManifolds].frictionTwistImpulse = externalManifold.getFrictionTwistImpulse(); // Compute the inverse K matrix for the rolling resistance constraint bool isBody1DynamicType = body1->getType() == BodyType::DYNAMIC; @@ -343,6 +344,7 @@ void ContactSolver::warmStart() { for (short int i=0; i<mContactConstraints[c].nbContacts; i++) { + // If it is not a new contact (this contact was already existing at last time step) if (mContactPoints[contactPointIndex].isRestingContact) { @@ -354,22 +356,22 @@ void ContactSolver::warmStart() { Vector3 impulsePenetration(mContactPoints[contactPointIndex].normal.x * mContactPoints[contactPointIndex].penetrationImpulse, mContactPoints[contactPointIndex].normal.y * mContactPoints[contactPointIndex].penetrationImpulse, mContactPoints[contactPointIndex].normal.z * mContactPoints[contactPointIndex].penetrationImpulse); - mLinearVelocities[mContactConstraints[c].indexBody1].x -= mContactConstraints[c].massInverseBody1 * impulsePenetration.x; - mLinearVelocities[mContactConstraints[c].indexBody1].y -= mContactConstraints[c].massInverseBody1 * impulsePenetration.y; - mLinearVelocities[mContactConstraints[c].indexBody1].z -= mContactConstraints[c].massInverseBody1 * impulsePenetration.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactConstraints[c].massInverseBody1 * impulsePenetration.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactConstraints[c].massInverseBody1 * impulsePenetration.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactConstraints[c].massInverseBody1 * impulsePenetration.z; - mAngularVelocities[mContactConstraints[c].indexBody1].x -= mContactPoints[contactPointIndex].i1TimesR1CrossN.x * mContactPoints[contactPointIndex].penetrationImpulse; - mAngularVelocities[mContactConstraints[c].indexBody1].y -= mContactPoints[contactPointIndex].i1TimesR1CrossN.y * mContactPoints[contactPointIndex].penetrationImpulse; - mAngularVelocities[mContactConstraints[c].indexBody1].z -= mContactPoints[contactPointIndex].i1TimesR1CrossN.z * mContactPoints[contactPointIndex].penetrationImpulse; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactPoints[contactPointIndex].i1TimesR1CrossN.x * mContactPoints[contactPointIndex].penetrationImpulse; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactPoints[contactPointIndex].i1TimesR1CrossN.y * mContactPoints[contactPointIndex].penetrationImpulse; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactPoints[contactPointIndex].i1TimesR1CrossN.z * mContactPoints[contactPointIndex].penetrationImpulse; // Update the velocities of the body 2 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody2].x += mContactConstraints[c].massInverseBody2 * impulsePenetration.x; - mLinearVelocities[mContactConstraints[c].indexBody2].y += mContactConstraints[c].massInverseBody2 * impulsePenetration.y; - mLinearVelocities[mContactConstraints[c].indexBody2].z += mContactConstraints[c].massInverseBody2 * impulsePenetration.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactConstraints[c].massInverseBody2 * impulsePenetration.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactConstraints[c].massInverseBody2 * impulsePenetration.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactConstraints[c].massInverseBody2 * impulsePenetration.z; - mAngularVelocities[mContactConstraints[c].indexBody2].x += mContactPoints[contactPointIndex].i2TimesR2CrossN.x * mContactPoints[contactPointIndex].penetrationImpulse; - mAngularVelocities[mContactConstraints[c].indexBody2].y += mContactPoints[contactPointIndex].i2TimesR2CrossN.y * mContactPoints[contactPointIndex].penetrationImpulse; - mAngularVelocities[mContactConstraints[c].indexBody2].z += mContactPoints[contactPointIndex].i2TimesR2CrossN.z * mContactPoints[contactPointIndex].penetrationImpulse; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactPoints[contactPointIndex].i2TimesR2CrossN.x * mContactPoints[contactPointIndex].penetrationImpulse; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactPoints[contactPointIndex].i2TimesR2CrossN.y * mContactPoints[contactPointIndex].penetrationImpulse; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactPoints[contactPointIndex].i2TimesR2CrossN.z * mContactPoints[contactPointIndex].penetrationImpulse; } else { // If it is a new contact point @@ -409,12 +411,12 @@ void ContactSolver::warmStart() { mContactConstraints[c].r2CrossT1.z * mContactConstraints[c].friction1Impulse); // Update the velocities of the body 1 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody1] -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2; - mAngularVelocities[mContactConstraints[c].indexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; // Update the velocities of the body 1 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].massInverseBody2 * linearImpulseBody2; - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].massInverseBody2 * linearImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; // ------ Second friction constraint at the center of the contact manifold ----- // @@ -430,18 +432,18 @@ void ContactSolver::warmStart() { angularImpulseBody2.z = mContactConstraints[c].r2CrossT2.z * mContactConstraints[c].friction2Impulse; // 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; - mLinearVelocities[mContactConstraints[c].indexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.z; - mAngularVelocities[mContactConstraints[c].indexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; // Update the velocities of the body 2 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.x; - mLinearVelocities[mContactConstraints[c].indexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.y; - mLinearVelocities[mContactConstraints[c].indexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.z; - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; // ------ Twist friction constraint at the center of the contact manifold ------ // @@ -455,10 +457,10 @@ void ContactSolver::warmStart() { angularImpulseBody2.z = mContactConstraints[c].normal.z * mContactConstraints[c].frictionTwistImpulse; // Update the velocities of the body 1 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; // Update the velocities of the body 2 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; // ------ Rolling resistance at the center of the contact manifold ------ // @@ -466,10 +468,10 @@ void ContactSolver::warmStart() { angularImpulseBody2 = mContactConstraints[c].rollingResistanceImpulse; // Update the velocities of the body 1 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody1] -= mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] -= mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody2; // Update the velocities of the body 1 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; } else { // If it is a new contact manifold @@ -497,10 +499,10 @@ void ContactSolver::solve() { decimal sumPenetrationImpulse = 0.0; // Get the constrained velocities - const Vector3& v1 = mLinearVelocities[mContactConstraints[c].indexBody1]; - const Vector3& w1 = mAngularVelocities[mContactConstraints[c].indexBody1]; - const Vector3& v2 = mLinearVelocities[mContactConstraints[c].indexBody2]; - const Vector3& w2 = mAngularVelocities[mContactConstraints[c].indexBody2]; + const Vector3& v1 = mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1]; + const Vector3& w1 = mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1]; + const Vector3& v2 = mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2]; + const Vector3& w2 = mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2]; for (short int i=0; i<mContactConstraints[c].nbContacts; i++) { @@ -543,22 +545,22 @@ void ContactSolver::solve() { mContactPoints[contactPointIndex].normal.z * deltaLambda); // Update the velocities of the body 1 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulse.x; - mLinearVelocities[mContactConstraints[c].indexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulse.y; - mLinearVelocities[mContactConstraints[c].indexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulse.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulse.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulse.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulse.z; - mAngularVelocities[mContactConstraints[c].indexBody1].x -= mContactPoints[contactPointIndex].i1TimesR1CrossN.x * deltaLambda; - mAngularVelocities[mContactConstraints[c].indexBody1].y -= mContactPoints[contactPointIndex].i1TimesR1CrossN.y * deltaLambda; - mAngularVelocities[mContactConstraints[c].indexBody1].z -= mContactPoints[contactPointIndex].i1TimesR1CrossN.z * deltaLambda; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactPoints[contactPointIndex].i1TimesR1CrossN.x * deltaLambda; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactPoints[contactPointIndex].i1TimesR1CrossN.y * deltaLambda; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactPoints[contactPointIndex].i1TimesR1CrossN.z * deltaLambda; // Update the velocities of the body 2 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulse.x; - mLinearVelocities[mContactConstraints[c].indexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulse.y; - mLinearVelocities[mContactConstraints[c].indexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulse.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulse.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulse.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulse.z; - mAngularVelocities[mContactConstraints[c].indexBody2].x += mContactPoints[contactPointIndex].i2TimesR2CrossN.x * deltaLambda; - mAngularVelocities[mContactConstraints[c].indexBody2].y += mContactPoints[contactPointIndex].i2TimesR2CrossN.y * deltaLambda; - mAngularVelocities[mContactConstraints[c].indexBody2].z += mContactPoints[contactPointIndex].i2TimesR2CrossN.z * deltaLambda; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactPoints[contactPointIndex].i2TimesR2CrossN.x * deltaLambda; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactPoints[contactPointIndex].i2TimesR2CrossN.y * deltaLambda; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactPoints[contactPointIndex].i2TimesR2CrossN.z * deltaLambda; sumPenetrationImpulse += mContactPoints[contactPointIndex].penetrationImpulse; @@ -566,10 +568,10 @@ void ContactSolver::solve() { if (mIsSplitImpulseActive) { // Split impulse (position correction) - const Vector3& v1Split = mSplitLinearVelocities[mContactConstraints[c].indexBody1]; - const Vector3& w1Split = mSplitAngularVelocities[mContactConstraints[c].indexBody1]; - const Vector3& v2Split = mSplitLinearVelocities[mContactConstraints[c].indexBody2]; - const Vector3& w2Split = mSplitAngularVelocities[mContactConstraints[c].indexBody2]; + const Vector3& v1Split = mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1]; + const Vector3& w1Split = mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1]; + const Vector3& v2Split = mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2]; + const Vector3& w2Split = mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2]; //Vector3 deltaVSplit = v2Split + w2Split.cross(mContactPoints[contactPointIndex].r2) - v1Split - w1Split.cross(mContactPoints[contactPointIndex].r1); Vector3 deltaVSplit(v2Split.x + w2Split.y * mContactPoints[contactPointIndex].r2.z - w2Split.z * mContactPoints[contactPointIndex].r2.y - v1Split.x - @@ -594,22 +596,22 @@ void ContactSolver::solve() { mContactPoints[contactPointIndex].normal.z * deltaLambdaSplit); // Update the velocities of the body 1 by applying the impulse P - mSplitLinearVelocities[mContactConstraints[c].indexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulse.x; - mSplitLinearVelocities[mContactConstraints[c].indexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulse.y; - mSplitLinearVelocities[mContactConstraints[c].indexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulse.z; + mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulse.x; + mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulse.y; + mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulse.z; - mSplitAngularVelocities[mContactConstraints[c].indexBody1].x -= mContactPoints[contactPointIndex].i1TimesR1CrossN.x * deltaLambdaSplit; - mSplitAngularVelocities[mContactConstraints[c].indexBody1].y -= mContactPoints[contactPointIndex].i1TimesR1CrossN.y * deltaLambdaSplit; - mSplitAngularVelocities[mContactConstraints[c].indexBody1].z -= mContactPoints[contactPointIndex].i1TimesR1CrossN.z * deltaLambdaSplit; + mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactPoints[contactPointIndex].i1TimesR1CrossN.x * deltaLambdaSplit; + mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactPoints[contactPointIndex].i1TimesR1CrossN.y * deltaLambdaSplit; + mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactPoints[contactPointIndex].i1TimesR1CrossN.z * deltaLambdaSplit; // Update the velocities of the body 1 by applying the impulse P - mSplitLinearVelocities[mContactConstraints[c].indexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulse.x; - mSplitLinearVelocities[mContactConstraints[c].indexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulse.y; - mSplitLinearVelocities[mContactConstraints[c].indexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulse.z; + mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulse.x; + mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulse.y; + mDynamicsComponents.mSplitLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulse.z; - mSplitAngularVelocities[mContactConstraints[c].indexBody2].x += mContactPoints[contactPointIndex].i2TimesR2CrossN.x * deltaLambdaSplit; - mSplitAngularVelocities[mContactConstraints[c].indexBody2].y += mContactPoints[contactPointIndex].i2TimesR2CrossN.y * deltaLambdaSplit; - mSplitAngularVelocities[mContactConstraints[c].indexBody2].z += mContactPoints[contactPointIndex].i2TimesR2CrossN.z * deltaLambdaSplit; + mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactPoints[contactPointIndex].i2TimesR2CrossN.x * deltaLambdaSplit; + mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactPoints[contactPointIndex].i2TimesR2CrossN.y * deltaLambdaSplit; + mDynamicsComponents.mSplitAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactPoints[contactPointIndex].i2TimesR2CrossN.z * deltaLambdaSplit; } contactPointIndex++; @@ -650,18 +652,18 @@ void ContactSolver::solve() { 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; - mLinearVelocities[mContactConstraints[c].indexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.z; - mAngularVelocities[mContactConstraints[c].indexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; // Update the velocities of the body 2 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.x; - mLinearVelocities[mContactConstraints[c].indexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.y; - mLinearVelocities[mContactConstraints[c].indexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.z; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.z; - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; // ------ Second friction constraint at the center of the contact manifol ----- // @@ -699,16 +701,16 @@ void ContactSolver::solve() { angularImpulseBody2.z = mContactConstraints[c].r2CrossT2.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; - mLinearVelocities[mContactConstraints[c].indexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.z; - mAngularVelocities[mContactConstraints[c].indexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].x -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].y -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody1].z -= mContactConstraints[c].massInverseBody1 * linearImpulseBody2.z; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] += mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody1; // Update the velocities of the body 2 by applying the impulse P - mLinearVelocities[mContactConstraints[c].indexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.x; - mLinearVelocities[mContactConstraints[c].indexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.y; - mLinearVelocities[mContactConstraints[c].indexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.z; - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].x += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.x; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].y += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.y; + mDynamicsComponents.mConstrainedLinearVelocities[mContactConstraints[c].dynamicsComponentIndexBody2].z += mContactConstraints[c].massInverseBody2 * linearImpulseBody2.z; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; // ------ Twist friction constraint at the center of the contact manifol ------ // @@ -731,10 +733,10 @@ void ContactSolver::solve() { angularImpulseBody2.z = mContactConstraints[c].normal.z * deltaLambda; // Update the velocities of the body 1 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody1] -= mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] -= mContactConstraints[c].inverseInertiaTensorBody1 * angularImpulseBody2; // Update the velocities of the body 1 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * angularImpulseBody2; // --------- Rolling resistance constraint at the center of the contact manifold --------- // @@ -752,10 +754,10 @@ void ContactSolver::solve() { deltaLambdaRolling = mContactConstraints[c].rollingResistanceImpulse - lambdaTempRolling; // Update the velocities of the body 1 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody1] -= mContactConstraints[c].inverseInertiaTensorBody1 * deltaLambdaRolling; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody1] -= mContactConstraints[c].inverseInertiaTensorBody1 * deltaLambdaRolling; // Update the velocities of the body 2 by applying the impulse P - mAngularVelocities[mContactConstraints[c].indexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * deltaLambdaRolling; + mDynamicsComponents.mConstrainedAngularVelocities[mContactConstraints[c].dynamicsComponentIndexBody2] += mContactConstraints[c].inverseInertiaTensorBody2 * deltaLambdaRolling; } } } diff --git a/src/engine/ContactSolver.h b/src/engine/ContactSolver.h index 26260c0c..5edfc38c 100644 --- a/src/engine/ContactSolver.h +++ b/src/engine/ContactSolver.h @@ -30,6 +30,7 @@ #include "configuration.h" #include "mathematics/Vector3.h" #include "mathematics/Matrix3x3.h" +#include "engine/Islands.h" /// ReactPhysics3D namespace namespace reactphysics3d { @@ -42,6 +43,9 @@ class MemoryManager; class Profiler; class Island; class RigidBody; +class BodyComponents; +class DynamicsComponents; +class ProxyShapeComponents; // Class Contact Solver /** @@ -170,11 +174,11 @@ class ContactSolver { /// Pointer to the external contact manifold ContactManifold* externalContactManifold; - /// Index of body 1 in the constraint solver - int32 indexBody1; + /// Index of body 1 in the dynamics components arrays + uint32 dynamicsComponentIndexBody1; - /// Index of body 2 in the constraint solver - int32 indexBody2; + /// Index of body 2 in the dynamics components arrays + uint32 dynamicsComponentIndexBody2; /// Inverse of the mass of body 1 decimal massInverseBody1; @@ -279,19 +283,15 @@ class ContactSolver { /// Memory manager MemoryManager& mMemoryManager; - /// Split linear velocities for the position contact solver (split impulse) - Vector3* mSplitLinearVelocities; - - /// Split angular velocities for the position contact solver (split impulse) - Vector3* mSplitAngularVelocities; - /// Current time step decimal mTimeStep; /// Contact constraints + // TODO : Use List<> here ContactManifoldSolver* mContactConstraints; /// Contact points + // TODO : Use List<> here ContactPointSolver* mContactPoints; /// Number of contact point constraints @@ -300,11 +300,24 @@ class ContactSolver { /// Number of contact constraints uint mNbContactManifolds; - /// Array of linear velocities - Vector3* mLinearVelocities; + /// Reference to the islands + Islands& mIslands; - /// Array of angular velocities - Vector3* mAngularVelocities; + /// Pointer to the list of contact manifolds from narrow-phase + List<ContactManifold>* mAllContactManifolds; + + /// Pointer to the list of contact points from narrow-phase + List<ContactPoint>* mAllContactPoints; + + /// Reference to the body components + BodyComponents& mBodyComponents; + + /// Reference to the dynamics components + DynamicsComponents& mDynamicsComponents; + + /// Reference to the proxy-shapes components + // TODO : Do we really need to use this ? + ProxyShapeComponents& mProxyShapeComponents; /// True if the split impulse position correction is active bool mIsSplitImpulseActive; @@ -346,24 +359,18 @@ class ContactSolver { // -------------------- Methods -------------------- // /// Constructor - ContactSolver(MemoryManager& memoryManager, const WorldSettings& worldSettings); + ContactSolver(MemoryManager& memoryManager, Islands& islands, BodyComponents& bodyComponents, + DynamicsComponents& dynamicsComponents, ProxyShapeComponents& proxyShapeComponents, + const WorldSettings& worldSettings); /// Destructor ~ContactSolver() = default; /// Initialize the contact constraints - void init(Island** islands, uint nbIslands, decimal timeStep); + void init(List<ContactManifold>* contactManifolds, List<ContactPoint>* contactPoints, decimal timeStep); /// Initialize the constraint solver for a given island - void initializeForIsland(Island* island); - - /// Set the split velocities arrays - void setSplitVelocitiesArrays(Vector3* splitLinearVelocities, - Vector3* splitAngularVelocities); - - /// Set the constrained velocities arrays - void setConstrainedVelocitiesArrays(Vector3* constrainedLinearVelocities, - Vector3* constrainedAngularVelocities); + void initializeForIsland(uint islandIndex); /// Store the computed impulses to use them to /// warm start the solver at the next iteration @@ -386,28 +393,6 @@ class ContactSolver { #endif }; -// Set the split velocities arrays -inline void ContactSolver::setSplitVelocitiesArrays(Vector3* splitLinearVelocities, - Vector3* splitAngularVelocities) { - - assert(splitLinearVelocities != nullptr); - assert(splitAngularVelocities != nullptr); - - mSplitLinearVelocities = splitLinearVelocities; - mSplitAngularVelocities = splitAngularVelocities; -} - -// Set the constrained velocities arrays -inline void ContactSolver::setConstrainedVelocitiesArrays(Vector3* constrainedLinearVelocities, - Vector3* constrainedAngularVelocities) { - - assert(constrainedLinearVelocities != nullptr); - assert(constrainedAngularVelocities != nullptr); - - mLinearVelocities = constrainedLinearVelocities; - mAngularVelocities = constrainedAngularVelocities; -} - // Return true if the split impulses position correction technique is used for contacts inline bool ContactSolver::isSplitImpulseActive() const { return mIsSplitImpulseActive; diff --git a/src/engine/DynamicsWorld.cpp b/src/engine/DynamicsWorld.cpp index 15524353..d3d18d06 100644 --- a/src/engine/DynamicsWorld.cpp +++ b/src/engine/DynamicsWorld.cpp @@ -32,7 +32,9 @@ #include "utils/Profiler.h" #include "engine/EventListener.h" #include "engine/Island.h" +#include "engine/Islands.h" #include "collision/ContactManifold.h" +#include "containers/Stack.h" // Namespaces using namespace reactphysics3d; @@ -45,21 +47,17 @@ using namespace std; * @param logger Pointer to the logger * @param profiler Pointer to the profiler */ -DynamicsWorld::DynamicsWorld(const Vector3& gravity, const WorldSettings& worldSettings, - Logger* logger, Profiler* profiler) +DynamicsWorld::DynamicsWorld(const Vector3& gravity, const WorldSettings& worldSettings, Logger* logger, Profiler* profiler) : CollisionWorld(worldSettings, logger, profiler), - mContactSolver(mMemoryManager, mConfig), + mIslands(mMemoryManager.getSingleFrameAllocator()), + mContactSolver(mMemoryManager, mIslands, mBodyComponents, mDynamicsComponents, mProxyShapesComponents, mConfig), + mConstraintSolver(mIslands, mDynamicsComponents), mNbVelocitySolverIterations(mConfig.defaultVelocitySolverNbIterations), mNbPositionSolverIterations(mConfig.defaultPositionSolverNbIterations), mIsSleepingEnabled(mConfig.isSleepingEnabled), mRigidBodies(mMemoryManager.getPoolAllocator()), mJoints(mMemoryManager.getPoolAllocator()), mGravity(gravity), mTimeStep(decimal(1.0f / 60.0f)), - mIsGravityEnabled(true), mConstrainedLinearVelocities(nullptr), - mConstrainedAngularVelocities(nullptr), mSplitLinearVelocities(nullptr), - mSplitAngularVelocities(nullptr), mConstrainedPositions(nullptr), - mConstrainedOrientations(nullptr), mNbIslands(0), mIslands(nullptr), - mSleepLinearVelocity(mConfig.defaultSleepLinearVelocity), - mSleepAngularVelocity(mConfig.defaultSleepAngularVelocity), - mTimeBeforeSleep(mConfig.defaultTimeBeforeSleep), + mIsGravityEnabled(true), mSleepLinearVelocity(mConfig.defaultSleepLinearVelocity), + mSleepAngularVelocity(mConfig.defaultSleepAngularVelocity), mTimeBeforeSleep(mConfig.defaultTimeBeforeSleep), mFreeJointsIDs(mMemoryManager.getPoolAllocator()), mCurrentJointId(0) { #ifdef IS_PROFILING_ACTIVE @@ -119,14 +117,17 @@ void DynamicsWorld::update(decimal timeStep) { // Notify the event listener about the beginning of an internal tick if (mEventListener != nullptr) mEventListener->beginInternalTick(); - // Reset all the contact manifolds lists of each body - resetContactManifoldListsOfBodies(); - // Compute the collision detection mCollisionDetection.computeCollisionDetection(); - // Compute the islands (separate groups of bodies with constraints between each others) - computeIslands(); + // Create the islands + createIslands(); + + // Create the actual narrow-phase contacts + mCollisionDetection.createContacts(); + + // Report the contacts to the user + mCollisionDetection.reportContacts(); // Integrate the velocities integrateRigidBodiesVelocities(); @@ -151,6 +152,9 @@ void DynamicsWorld::update(decimal timeStep) { // Reset the external force and torque applied to the bodies resetBodiesForceAndTorque(); + // Reset the islands + mIslands.clear(); + // Reset the single frame memory allocator mMemoryManager.resetFrameAllocator(); } @@ -162,37 +166,27 @@ void DynamicsWorld::integrateRigidBodiesPositions() { RP3D_PROFILE("DynamicsWorld::integrateRigidBodiesPositions()", mProfiler); - // For each island of the world - for (uint i=0; i < mNbIslands; i++) { + const decimal isSplitImpulseActive = mContactSolver.isSplitImpulseActive() ? decimal(1.0) : decimal(0.0); - RigidBody** bodies = mIslands[i]->getBodies(); + for (uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { - // For each body of the island - for (uint b=0; b < mIslands[i]->getNbBodies(); b++) { + // Get the constrained velocity + Vector3 newLinVelocity = mDynamicsComponents.mConstrainedLinearVelocities[i]; + Vector3 newAngVelocity = mDynamicsComponents.mConstrainedAngularVelocities[i]; - // Get the constrained velocity - uint indexArray = bodies[b]->mArrayIndex; - Vector3 newLinVelocity = mConstrainedLinearVelocities[indexArray]; - Vector3 newAngVelocity = mConstrainedAngularVelocities[indexArray]; + // Add the split impulse velocity from Contact Solver (only used + // to update the position) + newLinVelocity += isSplitImpulseActive * mDynamicsComponents.mSplitLinearVelocities[i]; + newAngVelocity += isSplitImpulseActive * mDynamicsComponents.mSplitAngularVelocities[i]; - // Add the split impulse velocity from Contact Solver (only used - // to update the position) - if (mContactSolver.isSplitImpulseActive()) { + // Get current position and orientation of the body + const Vector3& currentPosition = mDynamicsComponents.mCentersOfMassWorld[i]; + const Quaternion& currentOrientation = mTransformComponents.getTransform(mDynamicsComponents.mBodies[i]).getOrientation(); - newLinVelocity += mSplitLinearVelocities[indexArray]; - newAngVelocity += mSplitAngularVelocities[indexArray]; - } - - // Get current position and orientation of the body - const Vector3& currentPosition = bodies[b]->mCenterOfMassWorld; - const Quaternion& currentOrientation = mTransformComponents.getTransform(bodies[b]->getEntity()).getOrientation(); - - // Update the new constrained position and orientation of the body - mConstrainedPositions[indexArray] = currentPosition + newLinVelocity * mTimeStep; - mConstrainedOrientations[indexArray] = currentOrientation + - Quaternion(0, newAngVelocity) * - currentOrientation * decimal(0.5) * mTimeStep; - } + // Update the new constrained position and orientation of the body + mDynamicsComponents.mConstrainedPositions[i] = currentPosition + newLinVelocity * mTimeStep; + mDynamicsComponents.mConstrainedOrientations[i] = currentOrientation + Quaternion(0, newAngVelocity) * + currentOrientation * decimal(0.5) * mTimeStep; } } @@ -203,73 +197,47 @@ void DynamicsWorld::updateBodiesState() { // TODO : Make sure we compute this in a system - // For each island of the world - for (uint islandIndex = 0; islandIndex < mNbIslands; islandIndex++) { + for (uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { - // For each body of the island - RigidBody** bodies = mIslands[islandIndex]->getBodies(); + // Update the linear and angular velocity of the body + mDynamicsComponents.mLinearVelocities[i] = mDynamicsComponents.mConstrainedLinearVelocities[i]; + mDynamicsComponents.mAngularVelocities[i] = mDynamicsComponents.mConstrainedAngularVelocities[i]; - for (uint b=0; b < mIslands[islandIndex]->getNbBodies(); b++) { + // Update the position of the center of mass of the body + mDynamicsComponents.mCentersOfMassWorld[i] = mDynamicsComponents.mConstrainedPositions[i]; - uint index = bodies[b]->mArrayIndex; + // Update the orientation of the body + const Quaternion& constrainedOrientation = mDynamicsComponents.mConstrainedOrientations[i]; + mTransformComponents.getTransform(mDynamicsComponents.mBodies[i]).setOrientation(constrainedOrientation.getUnit()); + } - // Update the linear and angular velocity of the body - mDynamicsComponents.setLinearVelocity(bodies[b]->getEntity(), mConstrainedLinearVelocities[index]); - mDynamicsComponents.setAngularVelocity(bodies[b]->getEntity(), mConstrainedAngularVelocities[index]); + // Update the transform of the body (using the new center of mass and new orientation) + for (uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { - // Update the position of the center of mass of the body - bodies[b]->mCenterOfMassWorld = mConstrainedPositions[index]; + Transform& transform = mTransformComponents.getTransform(mDynamicsComponents.mBodies[i]); + const Vector3& centerOfMassWorld = mDynamicsComponents.mCentersOfMassWorld[i]; + const Vector3& centerOfMassLocal = mDynamicsComponents.mCentersOfMassLocal[i]; + transform.setPosition(centerOfMassWorld - transform.getOrientation() * centerOfMassLocal); + } - // Update the orientation of the body - mTransformComponents.getTransform(bodies[b]->getEntity()).setOrientation(mConstrainedOrientations[index].getUnit()); + // Update the world inverse inertia tensor of the body + for (uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { - // Update the transform of the body (using the new center of mass and new orientation) - bodies[b]->updateTransformWithCenterOfMass(); - - // Update the world inverse inertia tensor of the body - bodies[b]->updateInertiaTensorInverseWorld(); - } + Matrix3x3 orientation = mTransformComponents.getTransform(mDynamicsComponents.mBodies[i]).getOrientation().getMatrix(); + const Matrix3x3& inverseInertiaLocalTensor = mDynamicsComponents.mInverseInertiaTensorsLocal[i]; + mDynamicsComponents.mInverseInertiaTensorsWorld[i] = orientation * inverseInertiaLocalTensor * orientation.getTranspose(); } // Update the proxy-shapes components mCollisionDetection.updateProxyShapes(); } -// Initialize the bodies velocities arrays for the next simulation step. -void DynamicsWorld::initVelocityArrays() { +// Reset the split velocities of the bodies +void DynamicsWorld::resetSplitVelocities() { - RP3D_PROFILE("DynamicsWorld::initVelocityArrays()", mProfiler); - - // Allocate memory for the bodies velocity arrays - uint nbBodies = mRigidBodies.size(); - - mSplitLinearVelocities = static_cast<Vector3*>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - nbBodies * sizeof(Vector3))); - mSplitAngularVelocities = static_cast<Vector3*>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - nbBodies * sizeof(Vector3))); - mConstrainedLinearVelocities = static_cast<Vector3*>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - nbBodies * sizeof(Vector3))); - mConstrainedAngularVelocities = static_cast<Vector3*>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - nbBodies * sizeof(Vector3))); - mConstrainedPositions = static_cast<Vector3*>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - nbBodies * sizeof(Vector3))); - mConstrainedOrientations = static_cast<Quaternion*>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - nbBodies * sizeof(Quaternion))); - assert(mSplitLinearVelocities != nullptr); - assert(mSplitAngularVelocities != nullptr); - assert(mConstrainedLinearVelocities != nullptr); - assert(mConstrainedAngularVelocities != nullptr); - assert(mConstrainedPositions != nullptr); - assert(mConstrainedOrientations != nullptr); - - // Initialize the map of body indexes in the velocity arrays - uint i = 0; - for (List<RigidBody*>::Iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { - - mSplitLinearVelocities[i].setToZero(); - mSplitAngularVelocities[i].setToZero(); - - (*it)->mArrayIndex = i++; + for(uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { + mDynamicsComponents.mSplitLinearVelocities[i].setToZero(); + mDynamicsComponents.mSplitAngularVelocities[i].setToZero(); } } @@ -282,61 +250,55 @@ void DynamicsWorld::integrateRigidBodiesVelocities() { RP3D_PROFILE("DynamicsWorld::integrateRigidBodiesVelocities()", mProfiler); - // Initialize the bodies velocity arrays - initVelocityArrays(); + // Reset the split velocities of the bodies + resetSplitVelocities(); - // For each island of the world - for (uint i=0; i < mNbIslands; i++) { + // Integration component velocities using force/torque + for (uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { - RigidBody** bodies = mIslands[i]->getBodies(); + assert(mDynamicsComponents.mSplitLinearVelocities[i] == Vector3(0, 0, 0)); + assert(mDynamicsComponents.mSplitAngularVelocities[i] == Vector3(0, 0, 0)); - // For each body of the island - for (uint b=0; b < mIslands[i]->getNbBodies(); b++) { + // Integrate the external force to get the new velocity of the body + mDynamicsComponents.mConstrainedLinearVelocities[i] = mDynamicsComponents.mLinearVelocities[i] + mTimeStep * + mDynamicsComponents.mInverseMasses[i] * mDynamicsComponents.mExternalForces[i]; + mDynamicsComponents.mConstrainedAngularVelocities[i] = mDynamicsComponents.mAngularVelocities[i] + + mTimeStep * mDynamicsComponents.mInverseInertiaTensorsWorld[i] * mDynamicsComponents.mExternalTorques[i]; + } - // Insert the body into the map of constrained velocities - uint indexBody = bodies[b]->mArrayIndex; + // Apply gravity force + for (uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { + // If the gravity has to be applied to this rigid body + if (mDynamicsComponents.mIsGravityEnabled[i] && mIsGravityEnabled) { - assert(mSplitLinearVelocities[indexBody] == Vector3(0, 0, 0)); - assert(mSplitAngularVelocities[indexBody] == Vector3(0, 0, 0)); - - // Integrate the external force to get the new velocity of the body - mConstrainedLinearVelocities[indexBody] = bodies[b]->getLinearVelocity() + - mTimeStep * bodies[b]->mMassInverse * bodies[b]->mExternalForce; - mConstrainedAngularVelocities[indexBody] = bodies[b]->getAngularVelocity() + - mTimeStep * bodies[b]->getInertiaTensorInverseWorld() * - bodies[b]->mExternalTorque; - - // If the gravity has to be applied to this rigid body - if (bodies[b]->isGravityEnabled() && mIsGravityEnabled) { - - // Integrate the gravity force - mConstrainedLinearVelocities[indexBody] += mTimeStep * bodies[b]->mMassInverse * - bodies[b]->getMass() * mGravity; - } - - // Apply the velocity damping - // Damping force : F_c = -c' * v (c=damping factor) - // Equation : m * dv/dt = -c' * v - // => dv/dt = -c * v (with c=c'/m) - // => dv/dt + c * v = 0 - // Solution : v(t) = v0 * e^(-c * t) - // => v(t + dt) = v0 * e^(-c(t + dt)) - // = v0 * e^(-ct) * e^(-c * dt) - // = v(t) * e^(-c * dt) - // => v2 = v1 * e^(-c * dt) - // Using Taylor Serie for e^(-x) : e^x ~ 1 + x + x^2/2! + ... - // => e^(-x) ~ 1 - x - // => v2 = v1 * (1 - c * dt) - decimal linDampingFactor = bodies[b]->getLinearDamping(); - decimal angDampingFactor = bodies[b]->getAngularDamping(); - decimal linearDamping = pow(decimal(1.0) - linDampingFactor, mTimeStep); - decimal angularDamping = pow(decimal(1.0) - angDampingFactor, mTimeStep); - mConstrainedLinearVelocities[indexBody] *= linearDamping; - mConstrainedAngularVelocities[indexBody] *= angularDamping; - - indexBody++; + // Integrate the gravity force + mDynamicsComponents.mConstrainedLinearVelocities[i] = mDynamicsComponents.mConstrainedLinearVelocities[i] + mTimeStep * + mDynamicsComponents.mInverseMasses[i] * mDynamicsComponents.mInitMasses[i] * mGravity; } } + + // Apply the velocity damping + // Damping force : F_c = -c' * v (c=damping factor) + // Equation : m * dv/dt = -c' * v + // => dv/dt = -c * v (with c=c'/m) + // => dv/dt + c * v = 0 + // Solution : v(t) = v0 * e^(-c * t) + // => v(t + dt) = v0 * e^(-c(t + dt)) + // = v0 * e^(-ct) * e^(-c * dt) + // = v(t) * e^(-c * dt) + // => v2 = v1 * e^(-c * dt) + // Using Taylor Serie for e^(-x) : e^x ~ 1 + x + x^2/2! + ... + // => e^(-x) ~ 1 - x + // => v2 = v1 * (1 - c * dt) + for (uint32 i=0; i < mDynamicsComponents.getNbEnabledComponents(); i++) { + + const decimal linDampingFactor = mDynamicsComponents.mLinearDampings[i]; + const decimal angDampingFactor = mDynamicsComponents.mAngularDampings[i]; + const decimal linearDamping = pow(decimal(1.0) - linDampingFactor, mTimeStep); + const decimal angularDamping = pow(decimal(1.0) - angDampingFactor, mTimeStep); + mDynamicsComponents.mConstrainedLinearVelocities[i] = mDynamicsComponents.mConstrainedLinearVelocities[i] * linearDamping; + mDynamicsComponents.mConstrainedAngularVelocities[i] = mDynamicsComponents.mConstrainedAngularVelocities[i] * angularDamping; + } } // Solve the contacts and constraints @@ -344,40 +306,31 @@ void DynamicsWorld::solveContactsAndConstraints() { RP3D_PROFILE("DynamicsWorld::solveContactsAndConstraints()", mProfiler); - // Set the velocities arrays - mContactSolver.setSplitVelocitiesArrays(mSplitLinearVelocities, mSplitAngularVelocities); - mContactSolver.setConstrainedVelocitiesArrays(mConstrainedLinearVelocities, - mConstrainedAngularVelocities); - mConstraintSolver.setConstrainedVelocitiesArrays(mConstrainedLinearVelocities, - mConstrainedAngularVelocities); - mConstraintSolver.setConstrainedPositionsArrays(mConstrainedPositions, - mConstrainedOrientations); - // ---------- Solve velocity constraints for joints and contacts ---------- // // Initialize the contact solver - mContactSolver.init(mIslands, mNbIslands, mTimeStep); + mContactSolver.init(mCollisionDetection.mCurrentContactManifolds, mCollisionDetection.mCurrentContactPoints, mTimeStep); // For each island of the world - for (uint islandIndex = 0; islandIndex < mNbIslands; islandIndex++) { + for (uint islandIndex = 0; islandIndex < mIslands.getNbIslands(); islandIndex++) { // If there are constraints to solve - if (mIslands[islandIndex]->getNbJoints() > 0) { + if (mIslands.joints[islandIndex].size() > 0) { // Initialize the constraint solver - mConstraintSolver.initializeForIsland(mTimeStep, mIslands[islandIndex]); + mConstraintSolver.initializeForIsland(mTimeStep, islandIndex); } } // For each iteration of the velocity solver for (uint i=0; i<mNbVelocitySolverIterations; i++) { - for (uint islandIndex = 0; islandIndex < mNbIslands; islandIndex++) { + for (uint islandIndex = 0; islandIndex < mIslands.getNbIslands(); islandIndex++) { // Solve the constraints - if (mIslands[islandIndex]->getNbJoints() > 0) { + if (mIslands.joints[islandIndex].size() > 0) { - mConstraintSolver.solveVelocityConstraints(mIslands[islandIndex]); + mConstraintSolver.solveVelocityConstraints(islandIndex); } } @@ -396,17 +349,17 @@ void DynamicsWorld::solvePositionCorrection() { if (mJoints.size() == 0) return; // For each island of the world - for (uint islandIndex = 0; islandIndex < mNbIslands; islandIndex++) { + for (uint islandIndex = 0; islandIndex < mIslands.getNbIslands(); islandIndex++) { // ---------- Solve the position error correction for the constraints ---------- // - if (mIslands[islandIndex]->getNbJoints() > 0) { + if (mIslands.joints[islandIndex].size() > 0) { // For each iteration of the position (error correction) solver for (uint i=0; i<mNbPositionSolverIterations; i++) { // Solve the position constraints - mConstraintSolver.solvePositionConstraints(mIslands[islandIndex]); + mConstraintSolver.solvePositionConstraints(islandIndex); } } } @@ -429,7 +382,7 @@ RigidBody* DynamicsWorld::createRigidBody(const Transform& transform) { assert(bodyID < std::numeric_limits<reactphysics3d::bodyindex>::max()); mTransformComponents.addComponent(entity, false, TransformComponents::TransformComponent(transform)); - mDynamicsComponents.addComponent(entity, false, DynamicsComponents::DynamicsComponent(Vector3::zero(), Vector3::zero())); + mDynamicsComponents.addComponent(entity, false, DynamicsComponents::DynamicsComponent(transform.getPosition())); // Create the rigid body RigidBody* rigidBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, @@ -479,9 +432,6 @@ void DynamicsWorld::destroyRigidBody(RigidBody* rigidBody) { destroyJoint(element->joint); } - // Reset the contact manifold list of the body - rigidBody->resetContactManifoldsList(); - // Destroy the corresponding entity and its components mBodyComponents.removeComponent(rigidBody->getEntity()); mTransformComponents.removeComponent(rigidBody->getEntity()); @@ -672,123 +622,133 @@ uint DynamicsWorld::computeNextAvailableJointId() { return jointId; } -// Compute the islands of awake bodies. +// Compute the islands using potential contacts and joints +/// We compute the islands before creating the actual contacts here because we want all +/// the contact manifolds and contact points of the same island +/// to be packed together into linear arrays of manifolds and contacts for better caching. /// An island is an isolated group of rigid bodies that have constraints (joints or contacts) /// between each other. This method computes the islands at each time step as follows: For each /// awake rigid body, we run a Depth First Search (DFS) through the constraint graph of that body /// (graph where nodes are the bodies and where the edges are the constraints between the bodies) to /// find all the bodies that are connected with it (the bodies that share joints or contacts with /// it). Then, we create an island with this group of connected bodies. -void DynamicsWorld::computeIslands() { +void DynamicsWorld::createIslands() { - RP3D_PROFILE("DynamicsWorld::computeIslands()", mProfiler); + // TODO : Check if we handle kinematic bodies correctly in islands creation - uint nbBodies = mRigidBodies.size(); + // list of contact pairs involving a non-rigid body + List<uint> nonRigidBodiesContactPairs(mMemoryManager.getSingleFrameAllocator()); - // Allocate and create the array of islands pointer. This memory is allocated - // in the single frame allocator - mIslands = static_cast<Island**>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - sizeof(Island*) * nbBodies)); - mNbIslands = 0; + RP3D_PROFILE("DynamicsWorld::createIslands()", mProfiler); - int nbContactManifolds = 0; + // Reset all the isAlreadyInIsland variables of bodies and joints + for (uint b=0; b < mDynamicsComponents.getNbComponents(); b++) { - // Reset all the isAlreadyInIsland variables of bodies, joints and contact manifolds - for (List<RigidBody*>::Iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { - int nbBodyManifolds = (*it)->resetIsAlreadyInIslandAndCountManifolds(); - nbContactManifolds += nbBodyManifolds; + mDynamicsComponents.mIsAlreadyInIsland[b] = false; } for (List<Joint*>::Iterator it = mJoints.begin(); it != mJoints.end(); ++it) { (*it)->mIsAlreadyInIsland = false; } - // Create a stack (using an array) for the rigid bodies to visit during the Depth First Search - size_t nbBytesStack = sizeof(RigidBody*) * nbBodies; - RigidBody** stackBodiesToVisit = static_cast<RigidBody**>(mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - nbBytesStack)); + // Create a stack for the bodies to visit during the Depth First Search + Stack<Entity> bodyEntityIndicesToVisit(mMemoryManager.getSingleFrameAllocator()); - // For each rigid body of the world - for (List<RigidBody*>::Iterator it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { + uint nbTotalManifolds = 0; - RigidBody* body = *it; + // For each dynamic component + // TODO : Here we iterate on dynamic component where we can have static, kinematic and dynamic bodies. Maybe we should + // not use a dynamic component for a static body. + for (uint b=0; b < mDynamicsComponents.getNbEnabledComponents(); b++) { // If the body has already been added to an island, we go to the next body - if (body->mIsAlreadyInIsland) continue; + if (mDynamicsComponents.mIsAlreadyInIsland[b]) continue; // If the body is static, we go to the next body + // TODO : Do not use pointer to rigid body here (maybe move getType() into a component) + CollisionBody* body = static_cast<CollisionBody*>(mBodyComponents.getBody(mDynamicsComponents.mBodies[b])); if (body->getType() == BodyType::STATIC) continue; - // If the body is sleeping or inactive, we go to the next body - if (body->isSleeping() || !body->isActive()) continue; - // Reset the stack of bodies to visit - uint stackIndex = 0; - stackBodiesToVisit[stackIndex] = body; - stackIndex++; - body->mIsAlreadyInIsland = true; + bodyEntityIndicesToVisit.clear(); + + // Add the body into the stack of bodies to visit + mDynamicsComponents.mIsAlreadyInIsland[b] = true; + bodyEntityIndicesToVisit.push(mDynamicsComponents.mBodies[b]); // Create the new island - void* allocatedMemoryIsland = mMemoryManager.allocate(MemoryManager::AllocationType::Frame, - sizeof(Island)); - mIslands[mNbIslands] = new (allocatedMemoryIsland) Island(nbBodies, nbContactManifolds, mJoints.size(), - mMemoryManager); + uint32 islandIndex = mIslands.addIsland(nbTotalManifolds); // While there are still some bodies to visit in the stack - while (stackIndex > 0) { + while (bodyEntityIndicesToVisit.size() > 0) { // Get the next body to visit from the stack - stackIndex--; - RigidBody* bodyToVisit = stackBodiesToVisit[stackIndex]; - assert(bodyToVisit->isActive()); - - // Awake the body if it is sleeping - bodyToVisit->setIsSleeping(false); + const Entity bodyToVisitEntity = bodyEntityIndicesToVisit.pop(); // Add the body into the island - mIslands[mNbIslands]->addBody(bodyToVisit); + mIslands.bodyEntities[islandIndex].add(bodyToVisitEntity); // If the current body is static, we do not want to perform the DFS // search across that body - if (bodyToVisit->getType() == BodyType::STATIC) continue; + // TODO : Do not use pointer to rigid body here (maybe move getType() into a component) + RigidBody* rigidBodyToVisit = static_cast<RigidBody*>(mBodyComponents.getBody(bodyToVisitEntity)); - // For each contact manifold in which the current body is involded - ContactManifoldListElement* contactElement; - for (contactElement = bodyToVisit->mContactManifoldsList; contactElement != nullptr; - contactElement = contactElement->getNext()) { + // Awake the body if it is sleeping + rigidBodyToVisit->setIsSleeping(false); - ContactManifold* contactManifold = contactElement->getContactManifold(); + if (rigidBodyToVisit->getType() == BodyType::STATIC) continue; - assert(contactManifold->getNbContactPoints() > 0); + // If the body is involved in contacts with other bodies + auto itBodyContactPairs = mCollisionDetection.mMapBodyToContactPairs.find(bodyToVisitEntity); + if (itBodyContactPairs != mCollisionDetection.mMapBodyToContactPairs.end()) { - // Check if the current contact manifold has already been added into an island - if (contactManifold->isAlreadyInIsland()) continue; + // For each contact pair in which the current body is involded + List<uint>& contactPairs = itBodyContactPairs->second; + for (uint p=0; p < contactPairs.size(); p++) { - // Get the other body of the contact manifold - RigidBody* body1 = dynamic_cast<RigidBody*>(contactManifold->getBody1()); - RigidBody* body2 = dynamic_cast<RigidBody*>(contactManifold->getBody2()); + ContactPair& pair = (*mCollisionDetection.mCurrentContactPairs)[contactPairs[p]]; + assert(pair.potentialContactManifoldsIndices.size() > 0); - // If the colliding body is a RigidBody (and not a CollisionBody instead) - if (body1 != nullptr && body2 != nullptr) { + // Check if the current contact pair has already been added into an island + if (pair.isAlreadyInIsland) continue; - // Add the contact manifold into the island - mIslands[mNbIslands]->addContactManifold(contactManifold); - contactManifold->mIsAlreadyInIsland = true; + // Get the other body of the contact manifold + // TODO : Maybe avoid those casts here + RigidBody* body1 = dynamic_cast<RigidBody*>(mBodyComponents.getBody(pair.body1Entity)); + RigidBody* body2 = dynamic_cast<RigidBody*>(mBodyComponents.getBody(pair.body2Entity)); - RigidBody* otherBody = (body1->getId() == bodyToVisit->getId()) ? body2 : body1; + // If the colliding body is a RigidBody (and not a CollisionBody instead) + if (body1 != nullptr && body2 != nullptr) { - // Check if the other body has already been added to the island - if (otherBody->mIsAlreadyInIsland) continue; + nbTotalManifolds += pair.potentialContactManifoldsIndices.size(); - // Insert the other body into the stack of bodies to visit - stackBodiesToVisit[stackIndex] = otherBody; - stackIndex++; - otherBody->mIsAlreadyInIsland = true; + // Add the pair into the list of pair to process to create contacts + mCollisionDetection.mContactPairsIndicesOrderingForContacts.add(pair.contactPairIndex); + + // Add the contact manifold into the island + mIslands.nbContactManifolds[islandIndex] += pair.potentialContactManifoldsIndices.size(); + pair.isAlreadyInIsland = true; + + const Entity otherBodyEntity = pair.body1Entity == bodyToVisitEntity ? pair.body2Entity : pair.body1Entity; + + // Check if the other body has already been added to the island + if (mDynamicsComponents.getIsAlreadyInIsland(otherBodyEntity)) continue; + + // Insert the other body into the stack of bodies to visit + bodyEntityIndicesToVisit.push(otherBodyEntity); + mDynamicsComponents.setIsAlreadyInIsland(otherBodyEntity, true); + } + else { + + // Add the contact pair index in the list of contact pairs that won't be part of islands + nonRigidBodiesContactPairs.add(pair.contactPairIndex); + pair.isAlreadyInIsland = true; + } } } // For each joint in which the current body is involved JointListElement* jointElement; - for (jointElement = bodyToVisit->mJointsList; jointElement != nullptr; + for (jointElement = rigidBodyToVisit->mJointsList; jointElement != nullptr; jointElement = jointElement->next) { Joint* joint = jointElement->joint; @@ -797,35 +757,41 @@ void DynamicsWorld::computeIslands() { if (joint->isAlreadyInIsland()) continue; // Add the joint into the island - mIslands[mNbIslands]->addJoint(joint); + mIslands.joints[islandIndex].add(joint); joint->mIsAlreadyInIsland = true; - // Get the other body of the contact manifold - RigidBody* body1 = static_cast<RigidBody*>(joint->getBody1()); - RigidBody* body2 = static_cast<RigidBody*>(joint->getBody2()); - RigidBody* otherBody = (body1->getId() == bodyToVisit->getId()) ? body2 : body1; + const Entity body1Entity = joint->getBody1()->getEntity(); + const Entity body2Entity = joint->getBody2()->getEntity(); + const Entity otherBodyEntity = body1Entity == bodyToVisitEntity ? body2Entity : body1Entity; // Check if the other body has already been added to the island - if (otherBody->mIsAlreadyInIsland) continue; + if (mDynamicsComponents.getIsAlreadyInIsland(otherBodyEntity)) continue; // Insert the other body into the stack of bodies to visit - stackBodiesToVisit[stackIndex] = otherBody; - stackIndex++; - otherBody->mIsAlreadyInIsland = true; + bodyEntityIndicesToVisit.push(otherBodyEntity); + mDynamicsComponents.setIsAlreadyInIsland(otherBodyEntity, true); } } // Reset the isAlreadyIsland variable of the static bodies so that they // can also be included in the other islands - for (uint i=0; i < mIslands[mNbIslands]->mNbBodies; i++) { + for (uint j=0; j < mDynamicsComponents.getNbEnabledComponents(); j++) { - if (mIslands[mNbIslands]->mBodies[i]->getType() == BodyType::STATIC) { - mIslands[mNbIslands]->mBodies[i]->mIsAlreadyInIsland = false; + // If the body is static, we go to the next body + // TODO : Do not use pointer to rigid body here (maybe move getType() into a component) + CollisionBody* body = static_cast<CollisionBody*>(mBodyComponents.getBody(mDynamicsComponents.mBodies[j])); + if (body->getType() == BodyType::STATIC) { + mDynamicsComponents.mIsAlreadyInIsland[j] = false; } } + } - mNbIslands++; - } + // Add the contact pairs that are not part of islands at the end of the array of pairs for contacts creations + mCollisionDetection.mContactPairsIndicesOrderingForContacts.addRange(nonRigidBodiesContactPairs); + + assert(mCollisionDetection.mCurrentContactPairs->size() == mCollisionDetection.mContactPairsIndicesOrderingForContacts.size()); + + mCollisionDetection.mMapBodyToContactPairs.clear(true); } // Put bodies to sleep if needed. @@ -839,32 +805,36 @@ void DynamicsWorld::updateSleepingBodies() { const decimal sleepAngularVelocitySquare = mSleepAngularVelocity * mSleepAngularVelocity; // For each island of the world - for (uint i=0; i<mNbIslands; i++) { + for (uint i=0; i<mIslands.getNbIslands(); i++) { decimal minSleepTime = DECIMAL_LARGEST; // For each body of the island - RigidBody** bodies = mIslands[i]->getBodies(); - for (uint b=0; b < mIslands[i]->getNbBodies(); b++) { + for (uint b=0; b < mIslands.bodyEntities[i].size(); b++) { + + const Entity bodyEntity = mIslands.bodyEntities[i][b]; + + // TODO : We should not have to do this cast here to get type of body + CollisionBody* body = static_cast<CollisionBody*>(mBodyComponents.getBody(bodyEntity)); // Skip static bodies - if (bodies[b]->getType() == BodyType::STATIC) continue; + if (body->getType() == BodyType::STATIC) continue; // If the body is velocity is large enough to stay awake - if (bodies[b]->getLinearVelocity().lengthSquare() > sleepLinearVelocitySquare || - bodies[b]->getAngularVelocity().lengthSquare() > sleepAngularVelocitySquare || - !bodies[b]->isAllowedToSleep()) { + if (mDynamicsComponents.getLinearVelocity(bodyEntity).lengthSquare() > sleepLinearVelocitySquare || + mDynamicsComponents.getAngularVelocity(bodyEntity).lengthSquare() > sleepAngularVelocitySquare || + !body->isAllowedToSleep()) { // Reset the sleep time of the body - bodies[b]->mSleepTime = decimal(0.0); + body->mSleepTime = decimal(0.0); minSleepTime = decimal(0.0); } - else { // If the body velocity is bellow the sleeping velocity threshold + else { // If the body velocity is below the sleeping velocity threshold // Increase the sleep time - bodies[b]->mSleepTime += mTimeStep; - if (bodies[b]->mSleepTime < minSleepTime) { - minSleepTime = bodies[b]->mSleepTime; + body->mSleepTime += mTimeStep; + if (body->mSleepTime < minSleepTime) { + minSleepTime = body->mSleepTime; } } } @@ -875,8 +845,11 @@ void DynamicsWorld::updateSleepingBodies() { if (minSleepTime >= mTimeBeforeSleep) { // Put all the bodies of the island to sleep - for (uint b=0; b < mIslands[i]->getNbBodies(); b++) { - bodies[b]->setIsSleeping(true); + for (uint b=0; b < mIslands.bodyEntities[i].size(); b++) { + + const Entity bodyEntity = mIslands.bodyEntities[i][b]; + CollisionBody* body = static_cast<CollisionBody*>(mBodyComponents.getBody(bodyEntity)); + body->setIsSleeping(true); } } } @@ -906,33 +879,3 @@ void DynamicsWorld::enableSleeping(bool isSleepingEnabled) { RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::World, "Dynamics World: isSleepingEnabled=" + (isSleepingEnabled ? std::string("true") : std::string("false")) ); } - -// Return the list of all contacts of the world -/** - * @return A pointer to the first contact manifold in the linked-list of manifolds - */ -List<const ContactManifold*> DynamicsWorld::getContactsList() { - - List<const ContactManifold*> contactManifolds(mMemoryManager.getPoolAllocator()); - - // For each currently overlapping pair of bodies - for (auto it = mCollisionDetection.mOverlappingPairs.begin(); - it != mCollisionDetection.mOverlappingPairs.end(); ++it) { - - OverlappingPair* pair = it->second; - - // For each contact manifold of the pair - const ContactManifoldSet& manifoldSet = pair->getContactManifoldSet(); - ContactManifold* manifold = manifoldSet.getContactManifolds(); - while (manifold != nullptr) { - - // Get the contact manifold - contactManifolds.add(manifold); - - manifold = manifold->getNext(); - } - } - - // Return all the contact manifold - return contactManifolds; -} diff --git a/src/engine/DynamicsWorld.h b/src/engine/DynamicsWorld.h index f17aaefd..b6d95ff8 100644 --- a/src/engine/DynamicsWorld.h +++ b/src/engine/DynamicsWorld.h @@ -33,6 +33,7 @@ #include "utils/Logger.h" #include "engine/ContactSolver.h" #include "components/DynamicsComponents.h" +#include "engine/Islands.h" /// Namespace ReactPhysics3D namespace reactphysics3d { @@ -54,6 +55,9 @@ class DynamicsWorld : public CollisionWorld { // -------------------- Attributes -------------------- // + /// All the islands of bodies of the current frame + Islands mIslands; + /// Contact solver ContactSolver mContactSolver; @@ -84,32 +88,6 @@ class DynamicsWorld : public CollisionWorld { /// True if the gravity force is on bool mIsGravityEnabled; - /// Array of constrained linear velocities (state of the linear velocities - /// after solving the constraints) - Vector3* mConstrainedLinearVelocities; - - /// Array of constrained angular velocities (state of the angular velocities - /// after solving the constraints) - Vector3* mConstrainedAngularVelocities; - - /// Split linear velocities for the position contact solver (split impulse) - Vector3* mSplitLinearVelocities; - - /// Split angular velocities for the position contact solver (split impulse) - Vector3* mSplitAngularVelocities; - - /// Array of constrained rigid bodies position (for position error correction) - Vector3* mConstrainedPositions; - - /// Array of constrained rigid bodies orientation (for position error correction) - Quaternion* mConstrainedOrientations; - - /// Number of islands in the world - uint mNbIslands; - - /// Array with all the islands of awaken bodies - Island** mIslands; - /// Sleep linear velocity threshold decimal mSleepLinearVelocity; @@ -134,8 +112,8 @@ class DynamicsWorld : public CollisionWorld { /// Reset the external force and torque applied to the bodies void resetBodiesForceAndTorque(); - /// Initialize the bodies velocities arrays for the next simulation step. - void initVelocityArrays(); + /// Reset the split velocities of the bodies + void resetSplitVelocities(); /// Integrate the velocities of rigid bodies. void integrateRigidBodiesVelocities(); @@ -149,6 +127,9 @@ class DynamicsWorld : public CollisionWorld { /// Compute the islands of awake bodies. void computeIslands(); + /// Compute the islands using potential contacts and joints and create the actual contacts. + void createIslands(); + /// Update the postion/orientation of the bodies void updateBodiesState(); @@ -259,9 +240,6 @@ class DynamicsWorld : public CollisionWorld { /// Set an event listener object to receive events callbacks. void setEventListener(EventListener* eventListener); - /// Return the list of all contacts of the world - List<const ContactManifold*> getContactsList(); - // -------------------- Friendship -------------------- // friend class RigidBody; @@ -271,10 +249,9 @@ class DynamicsWorld : public CollisionWorld { inline void DynamicsWorld::resetBodiesForceAndTorque() { // For each body of the world - List<RigidBody*>::Iterator it; - for (it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { - (*it)->mExternalForce.setToZero(); - (*it)->mExternalTorque.setToZero(); + for (uint32 i=0; i < mDynamicsComponents.getNbComponents(); i++) { + mDynamicsComponents.mExternalForces[i].setToZero(); + mDynamicsComponents.mExternalTorques[i].setToZero(); } } diff --git a/src/engine/EventListener.h b/src/engine/EventListener.h index 16408a1a..e0ee9e80 100644 --- a/src/engine/EventListener.h +++ b/src/engine/EventListener.h @@ -39,7 +39,7 @@ namespace reactphysics3d { * new event listener class to the physics world using the DynamicsWorld::setEventListener() * method. */ -class EventListener { +class EventListener : public CollisionCallback { public : @@ -47,20 +47,22 @@ class EventListener { EventListener() = default; /// Destructor - virtual ~EventListener() = default; + virtual ~EventListener() override = default; - /// Called when a new contact point is found between two bodies + /// Called when some contacts occur /** - * @param contact Information about the contact + * @param collisionInfo Information about the contacts */ - virtual void newContact(const CollisionCallback::CollisionCallbackInfo& collisionInfo) {} + virtual void onContact(const CollisionCallback::CallbackData& callbackData) override {} + // TODO : Remove this method (no internal steps anymore) /// Called at the beginning of an internal tick of the simulation step. /// Each time the DynamicsWorld::update() method is called, the physics /// engine will do several internal simulation steps. This method is /// called at the beginning of each internal simulation step. virtual void beginInternalTick() {} + // TODO : Remove this method (no internal steps anymore) /// Called at the end of an internal tick of the simulation step. /// Each time the DynamicsWorld::update() metho is called, the physics /// engine will do several internal simulation steps. This method is diff --git a/src/engine/Islands.h b/src/engine/Islands.h new file mode 100644 index 00000000..dec4e49e --- /dev/null +++ b/src/engine/Islands.h @@ -0,0 +1,120 @@ +/******************************************************************************** +* ReactPhysics3D physics library, http://www.reactphysics3d.com * +* Copyright (c) 2010-2018 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. * +* * +********************************************************************************/ + +#ifndef REACTPHYSICS3D_ISLANDS_H +#define REACTPHYSICS3D_ISLANDS_H + +// Libraries +#include "configuration.h" +#include "containers/List.h" +#include "engine/Entity.h" +#include "constraint/Joint.h" + +namespace reactphysics3d { + +// Declarations + +// Structure Islands +/** + * This class contains all the islands of bodies during a frame. + * An island represent an isolated group of awake bodies that are connected with each other by + * some contraints (contacts or joints). + */ +struct Islands { + + private: + + // -------------------- Attributes -------------------- // + + /// Reference to the memory allocator + MemoryAllocator& memoryAllocator; + + public: + + // -------------------- Attributes -------------------- // + + + /// For each island, index of the first contact manifold of the island in the array of contact manifolds + List<uint> contactManifoldsIndices; + + /// For each island, number of contact manifolds in the island + List<uint> nbContactManifolds; + + /// For each island, list of all the entities of the bodies in the island + List<List<Entity>> bodyEntities; + + // TODO : Use joints entities here and not pointers + /// For each island, list of the joints that are part of the island + List<List<Joint*>> joints; + + // -------------------- Methods -------------------- // + + /// Constructor + Islands(MemoryAllocator& allocator) + :memoryAllocator(allocator), contactManifoldsIndices(allocator), nbContactManifolds(allocator), bodyEntities(allocator), + joints(allocator) { + + } + + /// Destructor + ~Islands() = default; + + /// Assignment operator + Islands& operator=(const Islands& island) = default; + + /// Copy-constructor + Islands(const Islands& island) = default; + + /// Return the number of islands + uint32 getNbIslands() const { + return static_cast<uint32>(contactManifoldsIndices.size()); + } + + /// Add an island and return its index + uint32 addIsland(uint32 contactManifoldStartIndex) { + + uint32 islandIndex = contactManifoldsIndices.size(); + + contactManifoldsIndices.add(contactManifoldStartIndex); + nbContactManifolds.add(0); + bodyEntities.add(List<Entity>(memoryAllocator)); + joints.add(List<Joint*>(memoryAllocator)); + + return islandIndex; + } + + /// Clear all the islands + void clear() { + + contactManifoldsIndices.clear(true); + nbContactManifolds.clear(true); + bodyEntities.clear(true); + joints.clear(true); + } +}; + +} + +#endif diff --git a/src/engine/OverlappingPair.cpp b/src/engine/OverlappingPair.cpp index 67d0bb18..6dacb6dd 100644 --- a/src/engine/OverlappingPair.cpp +++ b/src/engine/OverlappingPair.cpp @@ -35,7 +35,7 @@ using namespace reactphysics3d; OverlappingPair::OverlappingPair(ProxyShape* shape1, ProxyShape* shape2, MemoryAllocator& persistentMemoryAllocator, MemoryAllocator& temporaryMemoryAllocator, const WorldSettings& worldSettings) - : mPairID(computeID(shape1->getBroadPhaseId(), shape2->getBroadPhaseId())), mContactManifoldSet(shape1, shape2, persistentMemoryAllocator, worldSettings), + : mPairID(computeID(shape1->getBroadPhaseId(), shape2->getBroadPhaseId())), mProxyShape1(shape1), mProxyShape2(shape2), mPersistentAllocator(persistentMemoryAllocator), mTempMemoryAllocator(temporaryMemoryAllocator), mLastFrameCollisionInfos(mPersistentAllocator), mWorldSettings(worldSettings) { diff --git a/src/engine/OverlappingPair.h b/src/engine/OverlappingPair.h index 1fae4fb1..2062a499 100644 --- a/src/engine/OverlappingPair.h +++ b/src/engine/OverlappingPair.h @@ -27,7 +27,6 @@ #define REACTPHYSICS3D_OVERLAPPING_PAIR_H // Libraries -#include "collision/ContactManifoldSet.h" #include "collision/ProxyShape.h" #include "containers/Map.h" #include "containers/Pair.h" @@ -110,8 +109,9 @@ class OverlappingPair { /// Pair ID OverlappingPairId mPairID; - /// Set of persistent contact manifolds - ContactManifoldSet mContactManifoldSet; + // TODO : Replace this by entities + ProxyShape* mProxyShape1; + ProxyShape* mProxyShape2; /// Persistent memory allocator MemoryAllocator& mPersistentAllocator; @@ -146,6 +146,9 @@ class OverlappingPair { /// Deleted assignment operator OverlappingPair& operator=(const OverlappingPair& pair) = delete; + /// Return the Id of the pair + OverlappingPairId getId() const; + /// Return the pointer to first proxy collision shape ProxyShape* getShape1() const; @@ -155,30 +158,12 @@ class OverlappingPair { /// Return the last frame collision info LastFrameCollisionInfo* getLastFrameCollisionInfo(ShapeIdPair& shapeIds); - /// Return the a reference to the contact manifold set - const ContactManifoldSet& getContactManifoldSet(); - - /// Add potential contact-points from narrow-phase into potential contact manifolds - void addPotentialContactPoints(const NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex); - /// Return a reference to the temporary memory allocator MemoryAllocator& getTemporaryAllocator(); /// Return true if one of the shapes of the pair is a concave shape bool hasConcaveShape() const; - /// Return true if the overlapping pair has contact manifolds with contacts - bool hasContacts() const; - - /// Make the contact manifolds and contact points obsolete - void makeContactsObsolete(); - - /// Clear the obsolete contact manifold and contact points - void clearObsoleteManifoldsAndContactPoints(); - - /// Reduce the contact manifolds that have too many contact points - void reduceContactManifolds(); - /// Add a new last frame collision info if it does not exist for the given shapes already LastFrameCollisionInfo* addLastFrameInfoIfNecessary(uint shapeId1, uint shapeId2); @@ -202,14 +187,19 @@ class OverlappingPair { friend class DynamicsWorld; }; +// Return the Id of the pair +inline OverlappingPair::OverlappingPairId OverlappingPair::getId() const { + return mPairID; +} + // Return the pointer to first body inline ProxyShape* OverlappingPair::getShape1() const { - return mContactManifoldSet.getShape1(); -} + return mProxyShape1; +} // Return the pointer to second body inline ProxyShape* OverlappingPair::getShape2() const { - return mContactManifoldSet.getShape2(); + return mProxyShape2; } // Return the last frame collision info for a given shape id or nullptr if none is found @@ -222,17 +212,6 @@ inline LastFrameCollisionInfo* OverlappingPair::getLastFrameCollisionInfo(ShapeI return nullptr; } -// Return the contact manifold -inline const ContactManifoldSet& OverlappingPair::getContactManifoldSet() { - return mContactManifoldSet; -} - -// Make the contact manifolds and contact points obsolete -inline void OverlappingPair::makeContactsObsolete() { - - mContactManifoldSet.makeContactsObsolete(); -} - // Return the pair of bodies index inline OverlappingPair::OverlappingPairId OverlappingPair::computeID(int shape1BroadPhaseId, int shape2BroadPhaseId) { assert(shape1BroadPhaseId >= 0 && shape2BroadPhaseId >= 0); @@ -268,31 +247,11 @@ inline bool OverlappingPair::hasConcaveShape() const { !getShape2()->getCollisionShape()->isConvex(); } -// Return true if the overlapping pair has contact manifolds with contacts -inline bool OverlappingPair::hasContacts() const { - return mContactManifoldSet.getContactManifolds() != nullptr; -} - -// Clear the obsolete contact manifold and contact points -inline void OverlappingPair::clearObsoleteManifoldsAndContactPoints() { - mContactManifoldSet.clearObsoleteManifoldsAndContactPoints(); -} - -// Reduce the contact manifolds that have too many contact points -inline void OverlappingPair::reduceContactManifolds() { - mContactManifoldSet.reduce(); -} - // Return the last frame collision info for a given pair of shape ids inline LastFrameCollisionInfo* OverlappingPair::getLastFrameCollisionInfo(uint shapeId1, uint shapeId2) const { return mLastFrameCollisionInfos[ShapeIdPair(shapeId1, shapeId2)]; } -// Create a new potential contact manifold using contact-points from narrow-phase -inline void OverlappingPair::addPotentialContactPoints(const NarrowPhaseInfoBatch& narrowPhaseInfoBatch, uint batchIndex) { - mContactManifoldSet.addContactPoints(narrowPhaseInfoBatch, batchIndex); -} - } #endif diff --git a/src/memory/DefaultAllocator.h b/src/memory/DefaultAllocator.h index 5ff94895..ce4d1741 100644 --- a/src/memory/DefaultAllocator.h +++ b/src/memory/DefaultAllocator.h @@ -50,6 +50,10 @@ 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 { + + // TODO : Make sure to reduce the calls to default allocator is not called within a frame. For instance by a call + // to a pool allocator with size too large + return malloc(size); } diff --git a/src/systems/BroadPhaseSystem.cpp b/src/systems/BroadPhaseSystem.cpp index 448df4f1..3b7ec9dc 100644 --- a/src/systems/BroadPhaseSystem.cpp +++ b/src/systems/BroadPhaseSystem.cpp @@ -52,8 +52,8 @@ BroadPhaseSystem::BroadPhaseSystem(CollisionDetection& collisionDetection, Proxy } // Return true if the two broad-phase collision shapes are overlapping -bool BroadPhaseSystem::testOverlappingShapes(const ProxyShape* shape1, - const ProxyShape* shape2) const { +// TODO : Use proxy-shape entities in parameters +bool BroadPhaseSystem::testOverlappingShapes(const ProxyShape* shape1, const ProxyShape* shape2) const { if (shape1->getBroadPhaseId() == -1 || shape2->getBroadPhaseId() == -1) return false; diff --git a/src/systems/BroadPhaseSystem.h b/src/systems/BroadPhaseSystem.h index 140486c3..7931da92 100644 --- a/src/systems/BroadPhaseSystem.h +++ b/src/systems/BroadPhaseSystem.h @@ -186,7 +186,7 @@ class BroadPhaseSystem { void reportAllShapesOverlappingWithAABB(const AABB& aabb, List<int>& overlappingNodes) const; /// Compute all the overlapping pairs of collision shapes - void computeOverlappingPairs(MemoryManager& memoryManager, List<Pair<int, int> >& overlappingNodes); + void computeOverlappingPairs(MemoryManager& memoryManager, List<Pair<int, int>>& overlappingNodes); /// Return the proxy shape corresponding to the broad-phase node id in parameter ProxyShape* getProxyShapeForBroadPhaseId(int broadPhaseId) const; diff --git a/test/tests/collision/TestCollisionWorld.h b/test/tests/collision/TestCollisionWorld.h index affb678e..c7e78a07 100644 --- a/test/tests/collision/TestCollisionWorld.h +++ b/test/tests/collision/TestCollisionWorld.h @@ -32,6 +32,7 @@ #include "constraint/ContactPoint.h" #include "collision/ContactManifold.h" #include <map> +#include <vector> /// Reactphysics3D namespace namespace reactphysics3d { @@ -64,12 +65,12 @@ struct CollisionPointData { } }; -// Contact manifold collision data -struct CollisionManifoldData { +// Contact pair collision data +struct ContactPairData { std::vector<CollisionPointData> contactPoints; - int getNbContactPoints() const { + uint getNbContactPoints() const { return contactPoints.size(); } @@ -94,18 +95,18 @@ struct CollisionData { std::pair<const ProxyShape*, const ProxyShape*> proxyShapes; std::pair<CollisionBody*, CollisionBody*> bodies; - std::vector<CollisionManifoldData> contactManifolds; + std::vector<ContactPairData> contactPairs; - int getNbContactManifolds() const { - return contactManifolds.size(); + int getNbContactPairs() const { + return contactPairs.size(); } int getTotalNbContactPoints() const { int nbPoints = 0; - std::vector<CollisionManifoldData>::const_iterator it; - for (it = contactManifolds.begin(); it != contactManifolds.end(); ++it) { + std::vector<ContactPairData>::const_iterator it; + for (it = contactPairs.begin(); it != contactPairs.end(); ++it) { nbPoints += it->getNbContactPoints(); } @@ -123,8 +124,8 @@ struct CollisionData { bool hasContactPointSimilarTo(const Vector3& localPointBody1, const Vector3& localPointBody2, decimal penetrationDepth, decimal epsilon = 0.001) const { - std::vector<CollisionManifoldData>::const_iterator it; - for (it = contactManifolds.cbegin(); it != contactManifolds.cend(); ++it) { + std::vector<ContactPairData>::const_iterator it; + for (it = contactPairs.cbegin(); it != contactPairs.cend(); ++it) { if (it->hasContactPointSimilarTo(localPointBody1, localPointBody2, penetrationDepth)) { return true; @@ -182,34 +183,33 @@ class WorldCollisionCallback : public CollisionCallback } } - // This method will be called for each contact - virtual void notifyContact(const CollisionCallbackInfo& collisionCallbackInfo) override { + // This method is called when some contacts occur + virtual void onContact(const CallbackData& callbackData) override { - CollisionData collisionData; - collisionData.bodies = std::make_pair(collisionCallbackInfo.body1, collisionCallbackInfo.body2); - collisionData.proxyShapes = std::make_pair(collisionCallbackInfo.proxyShape1, collisionCallbackInfo.proxyShape2); + CollisionData collisionData; - ContactManifoldListElement* element = collisionCallbackInfo.contactManifoldElements; - while (element != nullptr) { + // For each contact pair + for (uint p=0; p < callbackData.getNbContactPairs(); p++) { - ContactManifold* contactManifold = element->getContactManifold(); + ContactPairData contactPairData; + ContactPair contactPair = callbackData.getContactPair(p); - CollisionManifoldData collisionManifold; + collisionData.bodies = std::make_pair(contactPair.getBody1(), contactPair.getBody2()); + collisionData.proxyShapes = std::make_pair(contactPair.getProxyShape1(), contactPair.getProxyShape2()); - ContactPoint* contactPoint = contactManifold->getContactPoints(); - while (contactPoint != nullptr) { + // For each contact point + for (uint c=0; c < contactPair.getNbContactPoints(); c++) { - CollisionPointData collisionPoint(contactPoint->getLocalPointOnShape1(), contactPoint->getLocalPointOnShape2(), contactPoint->getPenetrationDepth()); - collisionManifold.contactPoints.push_back(collisionPoint); + ContactPoint contactPoint = contactPair.getContactPoint(c); - contactPoint = contactPoint->getNext(); - } + CollisionPointData collisionPoint(contactPoint.getLocalPointOnShape1(), contactPoint.getLocalPointOnShape2(), contactPoint.getPenetrationDepth()); + contactPairData.contactPoints.push_back(collisionPoint); + } - collisionData.contactManifolds.push_back(collisionManifold); - mCollisionDatas.insert(std::make_pair(getCollisionKeyPair(collisionData.proxyShapes), collisionData)); + collisionData.contactPairs.push_back(contactPairData); + } - element = element->getNext(); - } + mCollisionDatas.insert(std::make_pair(getCollisionKeyPair(collisionData.proxyShapes), collisionData)); } }; @@ -218,30 +218,40 @@ class WorldOverlapCallback : public OverlapCallback { private: - std::vector<CollisionBody*> mOverlapBodies; + std::vector<std::pair<CollisionBody*, CollisionBody*>> mOverlapBodies; public: /// Destructor - virtual ~WorldOverlapCallback() { + virtual ~WorldOverlapCallback() override { reset(); } /// This method will be called for each reported overlapping bodies - virtual void notifyOverlap(CollisionBody* collisionBody) override { - mOverlapBodies.push_back(collisionBody); + virtual void onOverlap(CallbackData& callbackData) override { + + // For each overlapping pair + for (uint i=0; i < callbackData.getNbOverlappingPairs(); i++) { + + OverlapPair overlapPair = callbackData.getOverlappingPair(i); + mOverlapBodies.push_back(std::make_pair(overlapPair.getBody1(), overlapPair.getBody2())); + } } void reset() { mOverlapBodies.clear(); } - bool hasOverlap() const { - return !mOverlapBodies.empty(); - } + bool hasOverlapWithBody(CollisionBody* collisionBody) const { - std::vector<CollisionBody*>& getOverlapBodies() { - return mOverlapBodies; + for (uint i=0; i < mOverlapBodies.size(); i++) { + + if (mOverlapBodies[i].first == collisionBody || mOverlapBodies[i].second == collisionBody) { + return true; + } + } + + return false; } }; @@ -478,7 +488,6 @@ class TestCollisionWorld : public Test { testNoCollisions(); testNoOverlap(); - testNoAABBOverlap(); testSphereVsSphereCollision(); testSphereVsBoxCollision(); @@ -507,51 +516,51 @@ class TestCollisionWorld : public Test { // ---------- Global test ---------- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); // ---------- Single body test ---------- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody2, &mCollisionCallback); + mWorld->testCollision(mBoxBody2, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody2, &mCollisionCallback); + mWorld->testCollision(mSphereBody2, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); // Two bodies test mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, mBoxBody2, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mBoxBody2, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mSphereBody2, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mSphereBody2, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mSphereBody1, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, mSphereBody2, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mSphereBody2, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody2, mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody2, mSphereBody1, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody2, mSphereBody2, &mCollisionCallback); + mWorld->testCollision(mBoxBody2, mSphereBody2, mCollisionCallback); rp3d_test(!mCollisionCallback.hasContacts()); } @@ -563,20 +572,20 @@ class TestCollisionWorld : public Test { // ---------- Single body test ---------- // mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(!mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(!mOverlapCallback.hasOverlapWithBody(mBoxBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody2, &mOverlapCallback); - rp3d_test(!mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mBoxBody2, mOverlapCallback); + rp3d_test(!mOverlapCallback.hasOverlapWithBody(mBoxBody2)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(!mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(!mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody2, &mOverlapCallback); - rp3d_test(!mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mSphereBody2, mOverlapCallback); + rp3d_test(!mOverlapCallback.hasOverlapWithBody(mSphereBody2)); // Two bodies test @@ -588,21 +597,6 @@ class TestCollisionWorld : public Test { rp3d_test(!mWorld->testOverlap(mBoxBody2, mSphereBody2)); } - void testNoAABBOverlap() { - - // All the shapes of the world are not touching when they are created. - // Here we test that at the beginning, there is no AABB overlap at all. - - // Two bodies test - - rp3d_test(!mWorld->testAABBOverlap(mBoxBody1, mBoxBody2)); - rp3d_test(!mWorld->testAABBOverlap(mSphereBody1, mSphereBody2)); - rp3d_test(!mWorld->testAABBOverlap(mBoxBody1, mSphereBody1)); - rp3d_test(!mWorld->testAABBOverlap(mBoxBody1, mSphereBody2)); - rp3d_test(!mWorld->testAABBOverlap(mBoxBody2, mSphereBody1)); - rp3d_test(!mWorld->testAABBOverlap(mBoxBody2, mSphereBody2)); - } - void testSphereVsSphereCollision() { Transform initTransform1 = mSphereBody1->getTransform(); @@ -615,29 +609,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mSphereBody2->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mSphereBody2)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody2, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mSphereBody2, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody2)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mSphereProxyShape2)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mSphereProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -654,14 +644,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mSphereProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mSphereProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -675,14 +665,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody2, &mCollisionCallback); + mWorld->testCollision(mSphereBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mSphereProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mSphereProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -696,14 +686,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mSphereBody2, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mSphereBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mSphereProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mSphereProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -735,29 +725,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mBoxBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mBoxBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -774,14 +760,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -795,14 +781,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -816,14 +802,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -845,29 +831,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mBoxBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mBoxBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -884,14 +866,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -905,14 +887,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -926,14 +908,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -955,29 +937,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mBoxBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mBoxBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -994,14 +972,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1015,14 +993,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1036,14 +1014,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mBoxProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mBoxProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1075,29 +1053,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mCapsuleBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mCapsuleBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mCapsuleBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1114,14 +1088,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1135,14 +1109,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1156,14 +1130,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1185,29 +1159,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mCapsuleBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mCapsuleBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mCapsuleBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1224,14 +1194,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1245,14 +1215,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1266,14 +1236,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1305,29 +1275,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mConvexMeshBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mConvexMeshBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConvexMeshBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1344,14 +1310,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1365,14 +1331,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1386,14 +1352,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1415,29 +1381,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mConvexMeshBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mConvexMeshBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConvexMeshBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1454,14 +1416,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1475,14 +1437,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1496,14 +1458,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1525,29 +1487,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mConvexMeshBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mConvexMeshBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConvexMeshBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1564,14 +1522,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1585,14 +1543,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1606,14 +1564,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConvexMeshProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConvexMeshProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1645,29 +1603,25 @@ class TestCollisionWorld : public Test { mSphereBody1->setTransform(transform1); mConcaveMeshBody->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mSphereBody1, mConcaveMeshBody)); + mOverlapCallback.reset(); + mWorld->testOverlap(mSphereBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mSphereBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mSphereBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConcaveMeshBody, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConcaveMeshBody, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConcaveMeshBody)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConcaveMeshProxyShape)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1684,14 +1638,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1705,14 +1659,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1726,14 +1680,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mSphereBody1, mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mSphereBody1, mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mSphereProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mSphereProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -1765,29 +1719,25 @@ class TestCollisionWorld : public Test { mBoxBody1->setTransform(transform1); mBoxBody2->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mBoxBody1, mBoxBody2)); + mOverlapCallback.reset(); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody2, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mBoxBody2, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody2)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mBoxProxyShape2)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mBoxProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -1822,14 +1772,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mBoxProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mBoxProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -1851,14 +1801,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody2, &mCollisionCallback); + mWorld->testCollision(mBoxBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mBoxProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mBoxProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -1881,14 +1831,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, mBoxBody2, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mBoxBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mBoxProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mBoxProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -1929,29 +1879,25 @@ class TestCollisionWorld : public Test { mBoxBody1->setTransform(transform1); mConvexMeshBody2->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mBoxBody1, mConvexMeshBody2)); + mOverlapCallback.reset(); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody2, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConvexMeshBody2, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody2)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConvexMeshProxyShape2)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -1986,14 +1932,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConvexMeshProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -2015,14 +1961,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody2, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConvexMeshProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -2045,14 +1991,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, mConvexMeshBody2, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mConvexMeshBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConvexMeshProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -2093,29 +2039,25 @@ class TestCollisionWorld : public Test { mConvexMeshBody1->setTransform(transform1); mConvexMeshBody2->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mConvexMeshBody1, mConvexMeshBody2)); + mOverlapCallback.reset(); + mWorld->testOverlap(mConvexMeshBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody2, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConvexMeshBody2, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody2)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConvexMeshProxyShape2)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -2150,14 +2092,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConvexMeshProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -2179,14 +2121,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody2, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConvexMeshProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -2209,14 +2151,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, mConvexMeshBody2, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mConvexMeshBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConvexMeshProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConvexMeshProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // True if the bodies are swapped in the collision callback response @@ -2257,29 +2199,25 @@ class TestCollisionWorld : public Test { mBoxBody1->setTransform(transform1); mCapsuleBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mBoxBody1, mCapsuleBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mCapsuleBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mCapsuleProxyShape1)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2295,14 +2233,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2316,14 +2254,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2337,14 +2275,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2376,29 +2314,25 @@ class TestCollisionWorld : public Test { mConvexMeshBody1->setTransform(transform1); mCapsuleBody1->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mConvexMeshBody1, mCapsuleBody1)); + mOverlapCallback.reset(); + mWorld->testOverlap(mConvexMeshBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mCapsuleBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody1)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mCapsuleProxyShape1)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2414,14 +2348,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2435,14 +2369,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2456,14 +2390,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mCapsuleProxyShape1)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mCapsuleProxyShape1); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2495,85 +2429,81 @@ class TestCollisionWorld : public Test { mBoxBody1->setTransform(transform1); mConcaveMeshBody->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mBoxBody1, mConcaveMeshBody)); + mOverlapCallback.reset(); + mWorld->testOverlap(mBoxBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mBoxBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mBoxBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConcaveMeshBody, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConcaveMeshBody, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConcaveMeshBody)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConcaveMeshProxyShape)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mBoxBody1, mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mBoxBody1, mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mBoxProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mBoxProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // Test contact points - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // Reset the init transforms @@ -2597,85 +2527,81 @@ class TestCollisionWorld : public Test { mConvexMeshBody1->setTransform(transform1); mConcaveMeshBody->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mConvexMeshBody1, mConcaveMeshBody)); + mOverlapCallback.reset(); + mWorld->testOverlap(mConvexMeshBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConvexMeshBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mConvexMeshBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConcaveMeshBody, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConcaveMeshBody, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConcaveMeshBody)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConcaveMeshProxyShape)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConvexMeshBody1, mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mConvexMeshBody1, mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mConvexMeshProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mConvexMeshProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 4); // Test contact points - for (size_t i=0; i<collisionData->contactManifolds[0].contactPoints.size(); i++) { - rp3d_test(approxEqual(collisionData->contactManifolds[0].contactPoints[i].penetrationDepth, 1.0f)); + for (size_t i=0; i<collisionData->contactPairs[0].contactPoints.size(); i++) { + rp3d_test(approxEqual(collisionData->contactPairs[0].contactPoints[i].penetrationDepth, 1.0f)); } // Reset the init transforms @@ -2699,29 +2625,25 @@ class TestCollisionWorld : public Test { mCapsuleBody1->setTransform(transform1); mCapsuleBody2->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mCapsuleBody1, mCapsuleBody2)); + mOverlapCallback.reset(); + mWorld->testOverlap(mCapsuleBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody2, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mCapsuleBody2, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody2)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2738,14 +2660,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2759,14 +2681,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody2, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2780,14 +2702,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, mCapsuleBody2, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCapsuleBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2809,29 +2731,25 @@ class TestCollisionWorld : public Test { mCapsuleBody1->setTransform(transform1); mCapsuleBody2->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mCapsuleBody1, mCapsuleBody2)); + mOverlapCallback.reset(); + mWorld->testOverlap(mCapsuleBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody2, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mCapsuleBody2, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody2)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2848,14 +2766,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2869,14 +2787,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody2, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2890,14 +2808,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, mCapsuleBody2, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCapsuleBody2, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mCapsuleProxyShape2)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mCapsuleProxyShape2); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2929,29 +2847,25 @@ class TestCollisionWorld : public Test { mCapsuleBody1->setTransform(transform1); mConcaveMeshBody->setTransform(transform2); - // ----- Test AABB overlap ----- // - - rp3d_test(mWorld->testAABBOverlap(mCapsuleBody1, mConcaveMeshBody)); + mOverlapCallback.reset(); + mWorld->testOverlap(mCapsuleBody1, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mCapsuleBody1)); mOverlapCallback.reset(); - mWorld->testOverlap(mCapsuleBody1, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); - - mOverlapCallback.reset(); - mWorld->testOverlap(mConcaveMeshBody, &mOverlapCallback); - rp3d_test(mOverlapCallback.hasOverlap()); + mWorld->testOverlap(mConcaveMeshBody, mOverlapCallback); + rp3d_test(mOverlapCallback.hasOverlapWithBody(mConcaveMeshBody)); // ----- Test global collision test ----- // mCollisionCallback.reset(); - mWorld->testCollision(&mCollisionCallback); + mWorld->testCollision(mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mConcaveMeshProxyShape)); // Get collision data const CollisionData* collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2968,14 +2882,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 1 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -2989,14 +2903,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against body 2 only ----- // mCollisionCallback.reset(); - mWorld->testCollision(mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response @@ -3010,14 +2924,14 @@ class TestCollisionWorld : public Test { // ----- Test collision against selected body 1 and 2 ----- // mCollisionCallback.reset(); - mWorld->testCollision(mCapsuleBody1, mConcaveMeshBody, &mCollisionCallback); + mWorld->testCollision(mCapsuleBody1, mConcaveMeshBody, mCollisionCallback); rp3d_test(mCollisionCallback.areProxyShapesColliding(mCapsuleProxyShape1, mConcaveMeshProxyShape)); // Get collision data collisionData = mCollisionCallback.getCollisionData(mCapsuleProxyShape1, mConcaveMeshProxyShape); rp3d_test(collisionData != nullptr); - rp3d_test(collisionData->getNbContactManifolds() == 1); + rp3d_test(collisionData->getNbContactPairs() == 1); rp3d_test(collisionData->getTotalNbContactPoints() == 1); // True if the bodies are swapped in the collision callback response diff --git a/testbed/scenes/collisiondetection/CollisionDetectionScene.cpp b/testbed/scenes/collisiondetection/CollisionDetectionScene.cpp index ea57959c..ecff6b94 100755 --- a/testbed/scenes/collisiondetection/CollisionDetectionScene.cpp +++ b/testbed/scenes/collisiondetection/CollisionDetectionScene.cpp @@ -35,7 +35,7 @@ using namespace collisiondetectionscene; // Constructor CollisionDetectionScene::CollisionDetectionScene(const std::string& name, EngineSettings& settings) : SceneDemo(name, settings, SCENE_RADIUS, false), mMeshFolderPath("meshes/"), - mContactManager(mPhongShader, mMeshFolderPath), + mContactManager(mPhongShader, mMeshFolderPath, mContactPoints), mAreNormalsDisplayed(false) { mSelectedShapeIndex = 0; @@ -160,6 +160,8 @@ CollisionDetectionScene::CollisionDetectionScene(const std::string& name, Engine // Reset the scene void CollisionDetectionScene::reset() { + SceneDemo::reset(); + mSphere1->setTransform(rp3d::Transform(rp3d::Vector3(15, 5, 0), rp3d::Quaternion::identity())); mSphere2->setTransform(rp3d::Transform(rp3d::Vector3(0, 6, 0), rp3d::Quaternion::identity())); mCapsule1->setTransform(rp3d::Transform(rp3d::Vector3(-8, 7, 0), rp3d::Quaternion::identity())); @@ -206,8 +208,6 @@ CollisionDetectionScene::~CollisionDetectionScene() { mPhysicsWorld->destroyCollisionBody(mHeightField->getCollisionBody()); delete mHeightField; - mContactManager.resetPoints(); - // Destroy the static data for the visual contact points VisualContactPoint::destroyStaticData(); @@ -218,9 +218,9 @@ CollisionDetectionScene::~CollisionDetectionScene() { // Take a step for the simulation void CollisionDetectionScene::update() { - mContactManager.resetPoints(); + mContactPoints.clear(); - mPhysicsWorld->testCollision(&mContactManager); + mPhysicsWorld->testCollision(mContactManager); SceneDemo::update(); } @@ -313,38 +313,33 @@ bool CollisionDetectionScene::keyboardEvent(int key, int scancode, int action, i return false; } -// This method will be called for each reported contact point -void ContactManager::notifyContact(const CollisionCallbackInfo& collisionCallbackInfo) { +// This method is called when some contacts occur +void ContactManager::onContact(const CallbackData& callbackData) { - // For each contact manifold - rp3d::ContactManifoldListElement* manifoldElement = collisionCallbackInfo.contactManifoldElements; - while (manifoldElement != nullptr) { + // For each contact pair + for (uint p=0; p < callbackData.getNbContactPairs(); p++) { - // Get the contact manifold - rp3d::ContactManifold* contactManifold = manifoldElement->getContactManifold(); + ContactPair contactPair = callbackData.getContactPair(p); - // For each contact point - rp3d::ContactPoint* contactPoint = contactManifold->getContactPoints(); - while (contactPoint != nullptr) { + // For each contact point of the contact pair + for (uint c=0; c < contactPair.getNbContactPoints(); c++) { - // Contact normal - rp3d::Vector3 normal = contactPoint->getNormal(); - openglframework::Vector3 contactNormal(normal.x, normal.y, normal.z); + ContactPoint contactPoint = contactPair.getContactPoint(c); - rp3d::Vector3 point1 = contactPoint->getLocalPointOnShape1(); - point1 = collisionCallbackInfo.proxyShape1->getLocalToWorldTransform() * point1; + // Contact normal + rp3d::Vector3 normal = contactPoint.getWorldNormal(); + openglframework::Vector3 contactNormal(normal.x, normal.y, normal.z); - openglframework::Vector3 position1(point1.x, point1.y, point1.z); - mContactPoints.push_back(ContactPoint(position1, contactNormal, openglframework::Color::red())); + rp3d::Vector3 point1 = contactPoint.getLocalPointOnShape1(); + point1 = contactPair.getProxyShape1()->getLocalToWorldTransform() * point1; - rp3d::Vector3 point2 = contactPoint->getLocalPointOnShape2(); - point2 = collisionCallbackInfo.proxyShape2->getLocalToWorldTransform() * point2; - openglframework::Vector3 position2(point2.x, point2.y, point2.z); - mContactPoints.push_back(ContactPoint(position2, contactNormal, openglframework::Color::blue())); + openglframework::Vector3 position1(point1.x, point1.y, point1.z); + mContactPoints.push_back(SceneContactPoint(position1, contactNormal, openglframework::Color::red())); - contactPoint = contactPoint->getNext(); - } - - manifoldElement = manifoldElement->getNext(); + rp3d::Vector3 point2 = contactPoint.getLocalPointOnShape2(); + point2 = contactPair.getProxyShape2()->getLocalToWorldTransform() * point2; + openglframework::Vector3 position2(point2.x, point2.y, point2.z); + mContactPoints.push_back(SceneContactPoint(position2, contactNormal, openglframework::Color::blue())); + } } } diff --git a/testbed/scenes/collisiondetection/CollisionDetectionScene.h b/testbed/scenes/collisiondetection/CollisionDetectionScene.h index 2e134567..27ab3419 100644 --- a/testbed/scenes/collisiondetection/CollisionDetectionScene.h +++ b/testbed/scenes/collisiondetection/CollisionDetectionScene.h @@ -63,30 +63,22 @@ class ContactManager : public rp3d::CollisionCallback { private: - /// All the visual contact points - std::vector<ContactPoint> mContactPoints; - /// Contact point mesh folder path std::string mMeshFolderPath; + /// Reference to the list of all the visual contact points + std::vector<SceneContactPoint>& mContactPoints; + public: - ContactManager(openglframework::Shader& shader, const std::string& meshFolderPath) - : mMeshFolderPath(meshFolderPath) { + ContactManager(openglframework::Shader& shader, const std::string& meshFolderPath, + std::vector<SceneContactPoint>& contactPoints) + : mMeshFolderPath(meshFolderPath), mContactPoints(contactPoints) { } - /// This method will be called for each reported contact point - virtual void notifyContact(const CollisionCallbackInfo& collisionCallbackInfo) override; - - void resetPoints() { - - mContactPoints.clear(); - } - - std::vector<ContactPoint> getContactPoints() const { - return mContactPoints; - } + /// This method is called when some contacts occur + virtual void onContact(const CallbackData& callbackData) override; }; // Class CollisionDetectionScene @@ -151,9 +143,6 @@ class CollisionDetectionScene : public SceneDemo { /// Display/Hide the contact points virtual void setIsContactPointsDisplayed(bool display) override; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override; }; // Display or not the surface normals at hit points @@ -171,11 +160,6 @@ inline void CollisionDetectionScene::setIsContactPointsDisplayed(bool display) { SceneDemo::setIsContactPointsDisplayed(true); } -// Return all the contact points of the scene -inline std::vector<ContactPoint> CollisionDetectionScene::getContactPoints() { - return mContactManager.getContactPoints(); -} - } #endif diff --git a/testbed/scenes/collisionshapes/CollisionShapesScene.cpp b/testbed/scenes/collisionshapes/CollisionShapesScene.cpp index cffe7b50..12021ca7 100644 --- a/testbed/scenes/collisionshapes/CollisionShapesScene.cpp +++ b/testbed/scenes/collisionshapes/CollisionShapesScene.cpp @@ -49,7 +49,9 @@ CollisionShapesScene::CollisionShapesScene(const std::string& name, EngineSettin worldSettings.worldName = name; // Create the dynamics world for the physics simulation - mPhysicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + rp3d::DynamicsWorld* dynamicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + dynamicsWorld->setEventListener(this); + mPhysicsWorld = dynamicsWorld; for (int i=0; i<NB_COMPOUND_SHAPES; i++) { @@ -199,6 +201,8 @@ CollisionShapesScene::~CollisionShapesScene() { /// Reset the scene void CollisionShapesScene::reset() { + SceneDemo::reset(); + const float radius = 3.0f; for (uint i = 0; i<NB_COMPOUND_SHAPES; i++) { diff --git a/testbed/scenes/collisionshapes/CollisionShapesScene.h b/testbed/scenes/collisionshapes/CollisionShapesScene.h index dbb49539..d10b3644 100644 --- a/testbed/scenes/collisionshapes/CollisionShapesScene.h +++ b/testbed/scenes/collisionshapes/CollisionShapesScene.h @@ -99,16 +99,8 @@ class CollisionShapesScene : public SceneDemo { /// Reset the scene virtual void reset() override; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override; }; -// Return all the contact points of the scene -inline std::vector<ContactPoint> CollisionShapesScene::getContactPoints() { - return computeContactPointsOfWorld(getDynamicsWorld()); -} - } #endif diff --git a/testbed/scenes/concavemesh/ConcaveMeshScene.cpp b/testbed/scenes/concavemesh/ConcaveMeshScene.cpp index 31a06c0f..7b73dec4 100644 --- a/testbed/scenes/concavemesh/ConcaveMeshScene.cpp +++ b/testbed/scenes/concavemesh/ConcaveMeshScene.cpp @@ -49,7 +49,9 @@ ConcaveMeshScene::ConcaveMeshScene(const std::string& name, EngineSettings& sett worldSettings.worldName = name; // Create the dynamics world for the physics simulation - mPhysicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + rp3d::DynamicsWorld* dynamicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + dynamicsWorld->setEventListener(this); + mPhysicsWorld = dynamicsWorld; // ---------- Create the boxes ----------- // for (int i = 0; i<NB_COMPOUND_SHAPES; i++) { @@ -206,6 +208,8 @@ ConcaveMeshScene::~ConcaveMeshScene() { // Reset the scene void ConcaveMeshScene::reset() { + SceneDemo::reset(); + const float radius = 15.0f; for (uint i = 0; i<NB_COMPOUND_SHAPES; i++) { diff --git a/testbed/scenes/concavemesh/ConcaveMeshScene.h b/testbed/scenes/concavemesh/ConcaveMeshScene.h index 91ede1bb..16dcb753 100644 --- a/testbed/scenes/concavemesh/ConcaveMeshScene.h +++ b/testbed/scenes/concavemesh/ConcaveMeshScene.h @@ -96,16 +96,8 @@ class ConcaveMeshScene : public SceneDemo { /// Reset the scene virtual void reset() override; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override; }; -// Return all the contact points of the scene -inline std::vector<ContactPoint> ConcaveMeshScene::getContactPoints() { - return computeContactPointsOfWorld(getDynamicsWorld()); -} - } #endif diff --git a/testbed/scenes/cubes/CubesScene.cpp b/testbed/scenes/cubes/CubesScene.cpp index ab363c0b..66a59215 100644 --- a/testbed/scenes/cubes/CubesScene.cpp +++ b/testbed/scenes/cubes/CubesScene.cpp @@ -47,7 +47,9 @@ CubesScene::CubesScene(const std::string& name, EngineSettings& settings) worldSettings.worldName = name; // Create the dynamics world for the physics simulation - mPhysicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + rp3d::DynamicsWorld* dynamicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + dynamicsWorld->setEventListener(this); + mPhysicsWorld = dynamicsWorld; // Create all the cubes of the scene for (int i=0; i<NB_CUBES; i++) { @@ -117,6 +119,8 @@ CubesScene::~CubesScene() { // Reset the scene void CubesScene::reset() { + SceneDemo::reset(); + float radius = 2.0f; // Create all the cubes of the scene diff --git a/testbed/scenes/cubes/CubesScene.h b/testbed/scenes/cubes/CubesScene.h index e284a91d..4e39637a 100755 --- a/testbed/scenes/cubes/CubesScene.h +++ b/testbed/scenes/cubes/CubesScene.h @@ -67,16 +67,8 @@ class CubesScene : public SceneDemo { /// Reset the scene virtual void reset() override; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override; }; -// Return all the contact points of the scene -inline std::vector<ContactPoint> CubesScene::getContactPoints() { - return computeContactPointsOfWorld(getDynamicsWorld()); -} - } #endif diff --git a/testbed/scenes/cubestack/CubeStackScene.cpp b/testbed/scenes/cubestack/CubeStackScene.cpp index c30674b3..56fb3c35 100644 --- a/testbed/scenes/cubestack/CubeStackScene.cpp +++ b/testbed/scenes/cubestack/CubeStackScene.cpp @@ -47,7 +47,9 @@ CubeStackScene::CubeStackScene(const std::string& name, EngineSettings& settings worldSettings.worldName = name; // Create the dynamics world for the physics simulation - mPhysicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + rp3d::DynamicsWorld* dynamicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + dynamicsWorld->setEventListener(this); + mPhysicsWorld = dynamicsWorld; // Create all the cubes of the scene for (int i=1; i<=NB_FLOORS; i++) { @@ -120,6 +122,8 @@ CubeStackScene::~CubeStackScene() { // Reset the scene void CubeStackScene::reset() { + SceneDemo::reset(); + int index = 0; for (int i=NB_FLOORS; i > 0; i--) { diff --git a/testbed/scenes/cubestack/CubeStackScene.h b/testbed/scenes/cubestack/CubeStackScene.h index d4a4dace..3e3abd2e 100644 --- a/testbed/scenes/cubestack/CubeStackScene.h +++ b/testbed/scenes/cubestack/CubeStackScene.h @@ -67,16 +67,8 @@ class CubeStackScene : public SceneDemo { /// Reset the scene virtual void reset() override; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override; }; -// Return all the contact points of the scene -inline std::vector<ContactPoint> CubeStackScene::getContactPoints() { - return computeContactPointsOfWorld(getDynamicsWorld()); -} - } #endif diff --git a/testbed/scenes/heightfield/HeightFieldScene.cpp b/testbed/scenes/heightfield/HeightFieldScene.cpp index 8b8d489c..d2734bbc 100644 --- a/testbed/scenes/heightfield/HeightFieldScene.cpp +++ b/testbed/scenes/heightfield/HeightFieldScene.cpp @@ -49,7 +49,9 @@ HeightFieldScene::HeightFieldScene(const std::string& name, EngineSettings& sett worldSettings.worldName = name; // Create the dynamics world for the physics simulation - mPhysicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + rp3d::DynamicsWorld* dynamicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + dynamicsWorld->setEventListener(this); + mPhysicsWorld = dynamicsWorld; for (int i = 0; i<NB_COMPOUND_SHAPES; i++) { @@ -205,6 +207,8 @@ HeightFieldScene::~HeightFieldScene() { // Reset the scene void HeightFieldScene::reset() { + SceneDemo::reset(); + const float radius = 3.0f; for (uint i = 0; i<NB_COMPOUND_SHAPES; i++) { diff --git a/testbed/scenes/heightfield/HeightFieldScene.h b/testbed/scenes/heightfield/HeightFieldScene.h index 90edda93..5ceee07c 100644 --- a/testbed/scenes/heightfield/HeightFieldScene.h +++ b/testbed/scenes/heightfield/HeightFieldScene.h @@ -98,16 +98,8 @@ class HeightFieldScene : public SceneDemo { /// Reset the scene virtual void reset() override ; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override ; }; -// Return all the contact points of the scene -inline std::vector<ContactPoint> HeightFieldScene::getContactPoints() { - return computeContactPointsOfWorld(getDynamicsWorld()); -} - } #endif diff --git a/testbed/scenes/joints/JointsScene.cpp b/testbed/scenes/joints/JointsScene.cpp index 736e0252..6c6a2bfc 100644 --- a/testbed/scenes/joints/JointsScene.cpp +++ b/testbed/scenes/joints/JointsScene.cpp @@ -48,7 +48,9 @@ JointsScene::JointsScene(const std::string& name, EngineSettings& settings) worldSettings.worldName = name; // Create the dynamics world for the physics simulation - mPhysicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + rp3d::DynamicsWorld* dynamicsWorld = new rp3d::DynamicsWorld(gravity, worldSettings); + dynamicsWorld->setEventListener(this); + mPhysicsWorld = dynamicsWorld; // Create the Ball-and-Socket joint createBallAndSocketJoints(); @@ -129,6 +131,8 @@ void JointsScene::updatePhysics() { // Reset the scene void JointsScene::reset() { + SceneDemo::reset(); + openglframework::Vector3 positionBox(0, 15, 5); openglframework::Vector3 boxDimension(1, 1, 1); diff --git a/testbed/scenes/joints/JointsScene.h b/testbed/scenes/joints/JointsScene.h index f101bf32..4a4d1c2d 100644 --- a/testbed/scenes/joints/JointsScene.h +++ b/testbed/scenes/joints/JointsScene.h @@ -125,16 +125,8 @@ class JointsScene : public SceneDemo { /// Reset the scene virtual void reset() override; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override; }; -// Return all the contact points of the scene -inline std::vector<ContactPoint> JointsScene::getContactPoints() { - return computeContactPointsOfWorld(getDynamicsWorld()); -} - } #endif diff --git a/testbed/scenes/raycast/RaycastScene.cpp b/testbed/scenes/raycast/RaycastScene.cpp index 018c28e4..1c5bb9c0 100644 --- a/testbed/scenes/raycast/RaycastScene.cpp +++ b/testbed/scenes/raycast/RaycastScene.cpp @@ -33,7 +33,7 @@ using namespace raycastscene; // Constructor RaycastScene::RaycastScene(const std::string& name, EngineSettings& settings) : SceneDemo(name, settings, SCENE_RADIUS, false), mMeshFolderPath("meshes/"), - mRaycastManager(mPhongShader, mMeshFolderPath), mCurrentBodyIndex(-1), + mRaycastManager(mPhongShader, mMeshFolderPath, mContactPoints), mCurrentBodyIndex(-1), mAreNormalsDisplayed(false), mVBOVertices(GL_ARRAY_BUFFER) { mIsContactPointsDisplayed = true; @@ -196,6 +196,8 @@ void RaycastScene::changeBody() { // Reset the scene void RaycastScene::reset() { + SceneDemo::reset(); + std::vector<PhysicsObject*>::iterator it; for (it = mPhysicsObjects.begin(); it != mPhysicsObjects.end(); ++it) { (*it)->setTransform(rp3d::Transform(rp3d::Vector3::zero(), rp3d::Quaternion::identity())); diff --git a/testbed/scenes/raycast/RaycastScene.h b/testbed/scenes/raycast/RaycastScene.h index 1d413995..6de17c45 100644 --- a/testbed/scenes/raycast/RaycastScene.h +++ b/testbed/scenes/raycast/RaycastScene.h @@ -64,17 +64,17 @@ class RaycastManager : public rp3d::RaycastCallback { private: - /// All the visual contact points - std::vector<ContactPoint> mHitPoints; + /// Reference to the list of contact points of the scene + std::vector<SceneContactPoint>& mHitPoints; /// Contact point mesh folder path std::string mMeshFolderPath; public: - RaycastManager(openglframework::Shader& shader, - const std::string& meshFolderPath) - : mMeshFolderPath(meshFolderPath) { + RaycastManager(openglframework::Shader& shader, const std::string& meshFolderPath, + std::vector<SceneContactPoint>& hitPoints) + : mMeshFolderPath(meshFolderPath), mHitPoints(hitPoints) { } @@ -85,7 +85,7 @@ class RaycastManager : public rp3d::RaycastCallback { rp3d::Vector3 hitPos = raycastInfo.worldPoint; openglframework::Vector3 position(hitPos.x, hitPos.y, hitPos.z); - mHitPoints.push_back(ContactPoint(position, normal, openglframework::Color::red())); + mHitPoints.push_back(SceneContactPoint(position, normal, openglframework::Color::red())); return raycastInfo.hitFraction; } @@ -95,7 +95,7 @@ class RaycastManager : public rp3d::RaycastCallback { mHitPoints.clear(); } - std::vector<ContactPoint> getHitPoints() const { + std::vector<SceneContactPoint> getHitPoints() const { return mHitPoints; } }; @@ -181,9 +181,6 @@ class RaycastScene : public SceneDemo { /// Display/Hide the contact points virtual void setIsContactPointsDisplayed(bool display) override; - - /// Return all the contact points of the scene - virtual std::vector<ContactPoint> getContactPoints() override; }; // Display or not the surface normals at hit points @@ -201,11 +198,6 @@ inline void RaycastScene::setIsContactPointsDisplayed(bool display) { SceneDemo::setIsContactPointsDisplayed(true); } -// Return all the contact points of the scene -inline std::vector<ContactPoint> RaycastScene::getContactPoints() { - return mRaycastManager.getHitPoints(); -} - } #endif diff --git a/testbed/src/Scene.cpp b/testbed/src/Scene.cpp index 2c45b934..4f6e31d9 100644 --- a/testbed/src/Scene.cpp +++ b/testbed/src/Scene.cpp @@ -185,3 +185,25 @@ void Scene::rotate(int xMouse, int yMouse) { } } } + +// Called when some contacts occur +void Scene::onContact(const rp3d::CollisionCallback::CallbackData& callbackData) { + + // For each contact pair + for (uint p=0; p < callbackData.getNbContactPairs(); p++) { + + rp3d::CollisionCallback::ContactPair contactPair = callbackData.getContactPair(p); + + // For each contact point of the contact pair + for (uint c=0; c < contactPair.getNbContactPoints(); c++) { + + rp3d::CollisionCallback::ContactPoint contactPoint = contactPair.getContactPoint(c); + + rp3d::Vector3 point = contactPair.getProxyShape1()->getLocalToWorldTransform() * contactPoint.getLocalPointOnShape1(); + rp3d::Vector3 normalWorld = contactPoint.getWorldNormal(); + openglframework::Vector3 normal = openglframework::Vector3(normalWorld.x, normalWorld.y, normalWorld.z); + SceneContactPoint contact(openglframework::Vector3(point.x, point.y, point.z), normal, openglframework::Color::red()); + mContactPoints.push_back(contact); + } + } +} diff --git a/testbed/src/Scene.h b/testbed/src/Scene.h index 8fffff2a..a014a3a1 100644 --- a/testbed/src/Scene.h +++ b/testbed/src/Scene.h @@ -31,7 +31,7 @@ #include "reactphysics3d.h" // Structure ContactPoint -struct ContactPoint { +struct SceneContactPoint { public: openglframework::Vector3 point; @@ -39,7 +39,7 @@ struct ContactPoint { openglframework::Color color; /// Constructor - ContactPoint(const openglframework::Vector3& pointWorld, const openglframework::Vector3& normalWorld, const openglframework::Color colorPoint) + SceneContactPoint(const openglframework::Vector3& pointWorld, const openglframework::Vector3& normalWorld, const openglframework::Color colorPoint) : point(pointWorld), normal(normalWorld), color(colorPoint) { } @@ -88,7 +88,7 @@ struct EngineSettings { // Class Scene // Abstract class that represents a 3D scene. -class Scene { +class Scene : public rp3d::EventListener { protected: @@ -130,12 +130,15 @@ class Scene { /// True if contact points are displayed bool mIsContactPointsDisplayed; - /// True if the AABBs of the phycis objects are displayed + /// True if the AABBs of the physics objects are displayed bool mIsAABBsDisplayed; /// True if we render shapes in wireframe mode bool mIsWireframeEnabled; + /// Contact points + std::vector<SceneContactPoint> mContactPoints; + // -------------------- Methods -------------------- // /// Set the scene position (where the camera needs to look at) @@ -165,7 +168,7 @@ class Scene { Scene(const std::string& name, EngineSettings& engineSettings, bool isShadowMappingEnabled = false); /// Destructor - virtual ~Scene(); + virtual ~Scene() override; /// Reshape the view virtual void reshape(int width, int height); @@ -181,7 +184,7 @@ class Scene { virtual void render()=0; /// Reset the scene - virtual void reset()=0; + virtual void reset(); /// Called when a keyboard event occurs virtual bool keyboardEvent(int key, int scancode, int action, int mods); @@ -230,11 +233,11 @@ class Scene { /// Enable/disbale wireframe rendering void setIsWireframeEnabled(bool isEnabled); - /// Return all the contact points of the scene - std::vector<ContactPoint> virtual getContactPoints(); - /// Update the engine settings virtual void updateEngineSettings() = 0; + + /// Called when some contacts occur + virtual void onContact(const rp3d::CollisionCallback::CallbackData& callbackData) override; }; // Called when a keyboard event occurs @@ -247,6 +250,11 @@ inline void Scene::reshape(int width, int height) { mCamera.setDimensions(width, height); } +// Reset the scene +inline void Scene::reset() { + mContactPoints.clear(); +} + // Return a reference to the camera inline const openglframework::Camera& Scene::getCamera() const { return mCamera; @@ -306,11 +314,4 @@ inline void Scene::setIsWireframeEnabled(bool isEnabled) { mIsWireframeEnabled = isEnabled; } -// Return all the contact points of the scene -inline std::vector<ContactPoint> Scene::getContactPoints() { - - // Return an empty list of contact points - return std::vector<ContactPoint>(); -} - #endif diff --git a/testbed/src/SceneDemo.cpp b/testbed/src/SceneDemo.cpp index 3ecaee92..d4fa3fcc 100644 --- a/testbed/src/SceneDemo.cpp +++ b/testbed/src/SceneDemo.cpp @@ -98,7 +98,7 @@ SceneDemo::~SceneDemo() { mColorShader.destroy(); // Destroy the contact points - removeAllContactPoints(); + removeAllVisualContactPoints(); // Destroy rendering data for the AABB AABB::destroy(); @@ -124,6 +124,9 @@ void SceneDemo::update() { // Can be called several times per frame void SceneDemo::updatePhysics() { + // Clear contacts points + mContactPoints.clear(); + if (getDynamicsWorld() != nullptr) { // Take a simulation step @@ -345,20 +348,17 @@ void SceneDemo::drawTextureQuad() { void SceneDemo::updateContactPoints() { // Remove the previous contact points - removeAllContactPoints(); + removeAllVisualContactPoints(); if (mIsContactPointsDisplayed) { - // Get the current contact points of the scene - std::vector<ContactPoint> contactPoints = getContactPoints(); - // For each contact point - std::vector<ContactPoint>::const_iterator it; - for (it = contactPoints.begin(); it != contactPoints.end(); ++it) { + std::vector<SceneContactPoint>::const_iterator it; + for (it = mContactPoints.begin(); it != mContactPoints.end(); ++it) { // Create a visual contact point for rendering VisualContactPoint* point = new VisualContactPoint(it->point, mMeshFolderPath, it->point + it->normal, it->color); - mContactPoints.push_back(point); + mVisualContactPoints.push_back(point); } } } @@ -367,8 +367,8 @@ void SceneDemo::updateContactPoints() { void SceneDemo::renderContactPoints(openglframework::Shader& shader, const openglframework::Matrix4& worldToCameraMatrix) { // Render all the contact points - for (std::vector<VisualContactPoint*>::iterator it = mContactPoints.begin(); - it != mContactPoints.end(); ++it) { + for (std::vector<VisualContactPoint*>::iterator it = mVisualContactPoints.begin(); + it != mVisualContactPoints.end(); ++it) { (*it)->render(mColorShader, worldToCameraMatrix); } } @@ -397,46 +397,14 @@ void SceneDemo::renderAABBs(const openglframework::Matrix4& worldToCameraMatrix) } } -void SceneDemo::removeAllContactPoints() { +void SceneDemo::removeAllVisualContactPoints() { // Destroy all the visual contact points - for (std::vector<VisualContactPoint*>::iterator it = mContactPoints.begin(); - it != mContactPoints.end(); ++it) { + for (std::vector<VisualContactPoint*>::iterator it = mVisualContactPoints.begin(); + it != mVisualContactPoints.end(); ++it) { delete (*it); } - mContactPoints.clear(); -} - -// Return all the contact points of the scene -std::vector<ContactPoint> SceneDemo::computeContactPointsOfWorld(rp3d::DynamicsWorld* world) { - - std::vector<ContactPoint> contactPoints; - - // Get the list of contact manifolds from the world - rp3d::List<const rp3d::ContactManifold*> manifolds = world->getContactsList(); - - // For each contact manifold - rp3d::List<const rp3d::ContactManifold*>::Iterator it; - for (it = manifolds.begin(); it != manifolds.end(); ++it) { - - const rp3d::ContactManifold* manifold = *it; - - // For each contact point of the manifold - rp3d::ContactPoint* contactPoint = manifold->getContactPoints(); - while (contactPoint != nullptr) { - - rp3d::Vector3 point = manifold->getShape1()->getLocalToWorldTransform() * contactPoint->getLocalPointOnShape1(); - rp3d::Vector3 normalWorld = contactPoint->getNormal(); - openglframework::Vector3 normal = openglframework::Vector3(normalWorld.x, normalWorld.y, normalWorld.z); - ContactPoint contact(openglframework::Vector3(point.x, point.y, point.z), normal, openglframework::Color::red()); - contactPoints.push_back(contact); - - contactPoint = contactPoint->getNext(); - } - - } - - return contactPoints; + mVisualContactPoints.clear(); } // Update the engine settings diff --git a/testbed/src/SceneDemo.h b/testbed/src/SceneDemo.h index 95e1ece0..31b832c0 100644 --- a/testbed/src/SceneDemo.h +++ b/testbed/src/SceneDemo.h @@ -60,7 +60,7 @@ class SceneDemo : public Scene { static int shadowMapTextureLevel; /// All the visual contact points - std::vector<VisualContactPoint*> mContactPoints; + std::vector<VisualContactPoint*> mVisualContactPoints; /// Shadow map bias matrix openglframework::Matrix4 mShadowMapBiasMatrix; @@ -123,7 +123,7 @@ class SceneDemo : public Scene { void renderAABBs(const openglframework::Matrix4& worldToCameraMatrix); /// Remove all contact points - void removeAllContactPoints(); + void removeAllVisualContactPoints(); /// Return a reference to the dynamics world rp3d::DynamicsWorld* getDynamicsWorld(); @@ -144,6 +144,9 @@ class SceneDemo : public Scene { /// Update the scene virtual void update() override; + /// Reset the scene + virtual void reset() override; + /// Update the physics world (take a simulation step) /// Can be called several times per frame virtual void updatePhysics() override; @@ -159,9 +162,6 @@ class SceneDemo : public Scene { /// Enabled/Disable the shadow mapping virtual void setIsShadowMappingEnabled(bool isShadowMappingEnabled) override; - - /// Return all the contact points of the scene - std::vector<ContactPoint> computeContactPointsOfWorld(reactphysics3d::DynamicsWorld *world); }; // Enabled/Disable the shadow mapping @@ -184,6 +184,14 @@ inline const rp3d::DynamicsWorld* SceneDemo::getDynamicsWorld() const { return dynamic_cast<rp3d::DynamicsWorld*>(mPhysicsWorld); } +// Reset the scene +inline void SceneDemo::reset() { + + Scene::reset(); + + removeAllVisualContactPoints(); +} + #endif