/******************************************************************************** * ReactPhysics3D physics library, http://www.reactphysics3d.com * * Copyright (c) 2010-2020 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 #include #include #include #include #include #include #include #include #include #include // Namespaces using namespace reactphysics3d; using namespace std; // Static initializations uint PhysicsWorld::mNbWorlds = 0; // Constructor /** * @param gravity Gravity vector in the world (in meters per second squared) * @param worldSettings The settings of the world * @param profiler Pointer to the profiler */ PhysicsWorld::PhysicsWorld(MemoryManager& memoryManager, const WorldSettings& worldSettings, Profiler* profiler) : mMemoryManager(memoryManager), mConfig(worldSettings), mEntityManager(mMemoryManager.getHeapAllocator()), mDebugRenderer(mMemoryManager.getHeapAllocator()), mCollisionBodyComponents(mMemoryManager.getHeapAllocator()), mRigidBodyComponents(mMemoryManager.getHeapAllocator()), mTransformComponents(mMemoryManager.getHeapAllocator()), mCollidersComponents(mMemoryManager.getHeapAllocator()), mJointsComponents(mMemoryManager.getHeapAllocator()), mBallAndSocketJointsComponents(mMemoryManager.getHeapAllocator()), mFixedJointsComponents(mMemoryManager.getHeapAllocator()), mHingeJointsComponents(mMemoryManager.getHeapAllocator()), mSliderJointsComponents(mMemoryManager.getHeapAllocator()), mCollisionDetection(this, mCollidersComponents, mTransformComponents, mCollisionBodyComponents, mRigidBodyComponents, mMemoryManager), mCollisionBodies(mMemoryManager.getHeapAllocator()), mEventListener(nullptr), mName(worldSettings.worldName), mIslands(mMemoryManager.getSingleFrameAllocator()), mContactSolverSystem(mMemoryManager, *this, mIslands, mCollisionBodyComponents, mRigidBodyComponents, mCollidersComponents, mConfig.restitutionVelocityThreshold), mConstraintSolverSystem(*this, mIslands, mRigidBodyComponents, mTransformComponents, mJointsComponents, mBallAndSocketJointsComponents, mFixedJointsComponents, mHingeJointsComponents, mSliderJointsComponents), mDynamicsSystem(*this, mCollisionBodyComponents, mRigidBodyComponents, mTransformComponents, mCollidersComponents, mIsGravityEnabled, mConfig.gravity), mNbVelocitySolverIterations(mConfig.defaultVelocitySolverNbIterations), mNbPositionSolverIterations(mConfig.defaultPositionSolverNbIterations), mIsSleepingEnabled(mConfig.isSleepingEnabled), mRigidBodies(mMemoryManager.getPoolAllocator()), mIsGravityEnabled(true), mSleepLinearVelocity(mConfig.defaultSleepLinearVelocity), mSleepAngularVelocity(mConfig.defaultSleepAngularVelocity), mTimeBeforeSleep(mConfig.defaultTimeBeforeSleep), mCurrentJointId(0) { // Automatically generate a name for the world if (mName == "") { std::stringstream ss; ss << "world"; if (mNbWorlds > 0) { ss << mNbWorlds; } mName = ss.str(); } #ifdef IS_RP3D_PROFILING_ENABLED assert(profiler != nullptr); mProfiler = profiler; // Set the profiler mConstraintSolverSystem.setProfiler(mProfiler); mContactSolverSystem.setProfiler(mProfiler); mDynamicsSystem.setProfiler(mProfiler); mCollisionDetection.setProfiler(mProfiler); #endif mNbWorlds++; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Physics world " + mName + " has been created", __FILE__, __LINE__); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Initial world settings: " + worldSettings.to_string(), __FILE__, __LINE__); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Physics world " + mName + " has been created", __FILE__, __LINE__); } // Destructor PhysicsWorld::~PhysicsWorld() { RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Physics world " + mName + " has been destroyed", __FILE__, __LINE__); // Destroy all the collision bodies that have not been removed for (int i=mCollisionBodies.size() - 1 ; i >= 0; i--) { destroyCollisionBody(mCollisionBodies[i]); } #ifdef IS_RP3D_PROFILING_ENABLED // Print the profiling report into the destinations mProfiler->printReport(); #endif // Destroy all the joints that have not been removed for (uint32 i=0; i < mJointsComponents.getNbComponents(); i++) { destroyJoint(mJointsComponents.mJoints[i]); } // Destroy all the rigid bodies that have not been removed for (int i=mRigidBodies.size() - 1; i >= 0; i--) { destroyRigidBody(mRigidBodies[i]); } assert(mJointsComponents.getNbComponents() == 0); assert(mRigidBodies.size() == 0); assert(mCollisionBodies.size() == 0); assert(mCollisionBodyComponents.getNbComponents() == 0); assert(mTransformComponents.getNbComponents() == 0); assert(mCollidersComponents.getNbComponents() == 0); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Physics world " + mName + " has been destroyed", __FILE__, __LINE__); } // Create a collision body and add it to the world /** * @param transform Transformation mapping the local-space of the body to world-space * @return A pointer to the body that has been created in the world */ CollisionBody* PhysicsWorld::createCollisionBody(const Transform& transform) { // Create a new entity for the body Entity entity = mEntityManager.createEntity(); // Check that the transform is valid if (!transform.isValid()) { RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::Body, "Error when creating a collision body: the init transform is not valid", __FILE__, __LINE__); } assert(transform.isValid()); mTransformComponents.addComponent(entity, false, TransformComponents::TransformComponent(transform)); // Create the collision body CollisionBody* collisionBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(CollisionBody))) CollisionBody(*this, entity); assert(collisionBody != nullptr); // Add the components CollisionBodyComponents::CollisionBodyComponent bodyComponent(collisionBody); mCollisionBodyComponents.addComponent(entity, false, bodyComponent); // Add the collision body to the world mCollisionBodies.add(collisionBody); #ifdef IS_RP3D_PROFILING_ENABLED collisionBody->setProfiler(mProfiler); #endif RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(entity.id) + ": New collision body created", __FILE__, __LINE__); // Return the pointer to the rigid body return collisionBody; } // Destroy a collision body /** * @param collisionBody Pointer to the body to destroy */ void PhysicsWorld::destroyCollisionBody(CollisionBody* collisionBody) { RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(collisionBody->getEntity().id) + ": collision body destroyed", __FILE__, __LINE__); // Remove all the collision shapes of the body collisionBody->removeAllColliders(); mCollisionBodyComponents.removeComponent(collisionBody->getEntity()); mTransformComponents.removeComponent(collisionBody->getEntity()); mEntityManager.destroyEntity(collisionBody->getEntity()); // Call the destructor of the collision body collisionBody->~CollisionBody(); // Remove the collision body from the list of bodies mCollisionBodies.remove(collisionBody); // Free the object from the memory allocator mMemoryManager.release(MemoryManager::AllocationType::Pool, collisionBody, sizeof(CollisionBody)); } // Notify the world if a body is disabled (sleeping) or not void PhysicsWorld::setBodyDisabled(Entity bodyEntity, bool isDisabled) { if (isDisabled == mCollisionBodyComponents.getIsEntityDisabled(bodyEntity)) return; // Notify all the components mCollisionBodyComponents.setIsEntityDisabled(bodyEntity, isDisabled); mTransformComponents.setIsEntityDisabled(bodyEntity, isDisabled); if (mRigidBodyComponents.hasComponent(bodyEntity)) { mRigidBodyComponents.setIsEntityDisabled(bodyEntity, isDisabled); } // For each collider of the body const List& collidersEntities = mCollisionBodyComponents.getColliders(bodyEntity); for (uint i=0; i < collidersEntities.size(); i++) { mCollidersComponents.setIsEntityDisabled(collidersEntities[i], isDisabled); } // Disable the joints of the body if necessary if (mRigidBodyComponents.hasComponent(bodyEntity)) { // For each joint of the body const List& joints = mRigidBodyComponents.getJoints(bodyEntity); for(uint32 i=0; i < joints.size(); i++) { const Entity body1Entity = mJointsComponents.getBody1Entity(joints[i]); const Entity body2Entity = mJointsComponents.getBody2Entity(joints[i]); // If both bodies of the joint are disabled if (mRigidBodyComponents.getIsEntityDisabled(body1Entity) && mRigidBodyComponents.getIsEntityDisabled(body2Entity)) { // We disable the joint setJointDisabled(joints[i], true); } else { // Enable the joint setJointDisabled(joints[i], false); } } } } // Notify the world whether a joint is disabled or not void PhysicsWorld::setJointDisabled(Entity jointEntity, bool isDisabled) { if (isDisabled == mJointsComponents.getIsEntityDisabled(jointEntity)) return; mJointsComponents.setIsEntityDisabled(jointEntity, isDisabled); if (mBallAndSocketJointsComponents.hasComponent(jointEntity)) { mBallAndSocketJointsComponents.setIsEntityDisabled(jointEntity, isDisabled); } if (mFixedJointsComponents.hasComponent(jointEntity)) { mFixedJointsComponents.setIsEntityDisabled(jointEntity, isDisabled); } if (mHingeJointsComponents.hasComponent(jointEntity)) { mHingeJointsComponents.setIsEntityDisabled(jointEntity, isDisabled); } if (mSliderJointsComponents.hasComponent(jointEntity)) { mSliderJointsComponents.setIsEntityDisabled(jointEntity, isDisabled); } } // 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 * @return True if the two bodies overlap */ bool PhysicsWorld::testOverlap(CollisionBody* body1, CollisionBody* body2) { return mCollisionDetection.testOverlap(body1, body2); } // Return the current world-space AABB of given collider /** * @param collider Pointer to a collider * @return The AAABB of the collider in world-space */ AABB PhysicsWorld::getWorldAABB(const Collider* collider) const { if (collider->getBroadPhaseId() == -1) { return AABB(); } return mCollisionDetection.getWorldAABB(collider); } // Update the physics simulation /** * @param timeStep The amount of time to step the simulation by (in seconds) */ void PhysicsWorld::update(decimal timeStep) { #ifdef IS_RP3D_PROFILING_ENABLED // Increment the frame counter of the profiler mProfiler->incrementFrameCounter(); #endif RP3D_PROFILE("PhysicsWorld::update()", mProfiler); // Reset the debug renderer if (mIsDebugRenderingEnabled) { mDebugRenderer.reset(); } // Compute the collision detection mCollisionDetection.computeCollisionDetection(); // Create the islands createIslands(); // Report the contacts to the user mCollisionDetection.reportContactsAndTriggers(); // Disable the joints for pair of sleeping bodies disableJointsOfSleepingBodies(); // Integrate the velocities mDynamicsSystem.integrateRigidBodiesVelocities(timeStep); // Solve the contacts and constraints solveContactsAndConstraints(timeStep); // Integrate the position and orientation of each body mDynamicsSystem.integrateRigidBodiesPositions(timeStep, mContactSolverSystem.isSplitImpulseActive()); // Solve the position correction for constraints solvePositionCorrection(); // Update the state (positions and velocities) of the bodies mDynamicsSystem.updateBodiesState(); // Update the colliders components mCollisionDetection.updateColliders(timeStep); if (mIsSleepingEnabled) updateSleepingBodies(timeStep); // Reset the external force and torque applied to the bodies mDynamicsSystem.resetBodiesForceAndTorque(); // Reset the islands mIslands.clear(); // Generate debug rendering primitives (if enabled) if (mIsDebugRenderingEnabled) { mDebugRenderer.computeDebugRenderingPrimitives(*this); } // Reset the single frame memory allocator mMemoryManager.resetFrameAllocator(); } // Solve the contacts and constraints void PhysicsWorld::solveContactsAndConstraints(decimal timeStep) { RP3D_PROFILE("PhysicsWorld::solveContactsAndConstraints()", mProfiler); // ---------- Solve velocity constraints for joints and contacts ---------- // // Initialize the contact solver mContactSolverSystem.init(mCollisionDetection.mCurrentContactManifolds, mCollisionDetection.mCurrentContactPoints, timeStep); // Initialize the constraint solver mConstraintSolverSystem.initialize(timeStep); // For each iteration of the velocity solver for (uint i=0; isetProfiler(mProfiler); #endif RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(entity.id) + ": New collision body created", __FILE__, __LINE__); // Return the pointer to the rigid body return rigidBody; } // Destroy a rigid body and all the joints which it belongs /** * @param rigidBody Pointer to the body you want to destroy */ void PhysicsWorld::destroyRigidBody(RigidBody* rigidBody) { RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(rigidBody->getEntity().id) + ": rigid body destroyed", __FILE__, __LINE__); // Remove all the collision shapes of the body rigidBody->removeAllColliders(); // Destroy all the joints in which the rigid body to be destroyed is involved const List& joints = mRigidBodyComponents.getJoints(rigidBody->getEntity()); for (uint32 i=0; i < joints.size(); i++) { destroyJoint(mJointsComponents.getJoint(joints[i])); } // Destroy the corresponding entity and its components mCollisionBodyComponents.removeComponent(rigidBody->getEntity()); mRigidBodyComponents.removeComponent(rigidBody->getEntity()); mTransformComponents.removeComponent(rigidBody->getEntity()); mEntityManager.destroyEntity(rigidBody->getEntity()); // Call the destructor of the rigid body rigidBody->~RigidBody(); // Remove the rigid body from the list of rigid bodies mRigidBodies.remove(rigidBody); // Free the object from the memory allocator mMemoryManager.release(MemoryManager::AllocationType::Pool, rigidBody, sizeof(RigidBody)); } // Create a joint between two bodies in the world and return a pointer to the new joint /** * @param jointInfo The information that is necessary to create the joint * @return A pointer to the joint that has been created in the world */ Joint* PhysicsWorld::createJoint(const JointInfo& jointInfo) { // Create a new entity for the joint Entity entity = mEntityManager.createEntity(); Joint* newJoint = nullptr; const bool isJointDisabled = mRigidBodyComponents.getIsEntityDisabled(jointInfo.body1->getEntity()) && mRigidBodyComponents.getIsEntityDisabled(jointInfo.body2->getEntity()); // Allocate memory to create the new joint switch(jointInfo.type) { // Ball-and-Socket joint case JointType::BALLSOCKETJOINT: { // Create a BallAndSocketJoint component BallAndSocketJointComponents::BallAndSocketJointComponent ballAndSocketJointComponent; mBallAndSocketJointsComponents.addComponent(entity, isJointDisabled, ballAndSocketJointComponent); void* allocatedMemory = mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(BallAndSocketJoint)); const BallAndSocketJointInfo& info = static_cast(jointInfo); BallAndSocketJoint* joint = new (allocatedMemory) BallAndSocketJoint(entity, *this, info); newJoint = joint; mBallAndSocketJointsComponents.setJoint(entity, joint); break; } // Slider joint case JointType::SLIDERJOINT: { const SliderJointInfo& info = static_cast(jointInfo); // Create a SliderJoint component SliderJointComponents::SliderJointComponent sliderJointComponent(info.isLimitEnabled, info.isMotorEnabled, info.minTranslationLimit, info.maxTranslationLimit, info.motorSpeed, info.maxMotorForce); mSliderJointsComponents.addComponent(entity, isJointDisabled, sliderJointComponent); void* allocatedMemory = mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(SliderJoint)); SliderJoint* joint = new (allocatedMemory) SliderJoint(entity, *this, info); newJoint = joint; mSliderJointsComponents.setJoint(entity, joint); break; } // Hinge joint case JointType::HINGEJOINT: { const HingeJointInfo& info = static_cast(jointInfo); // Create a HingeJoint component HingeJointComponents::HingeJointComponent hingeJointComponent(info.isLimitEnabled, info.isMotorEnabled, info.minAngleLimit, info.maxAngleLimit, info.motorSpeed, info.maxMotorTorque); mHingeJointsComponents.addComponent(entity, isJointDisabled, hingeJointComponent); void* allocatedMemory = mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(HingeJoint)); HingeJoint* joint = new (allocatedMemory) HingeJoint(entity, *this, info); newJoint = joint; mHingeJointsComponents.setJoint(entity, joint); break; } // Fixed joint case JointType::FIXEDJOINT: { // Create a BallAndSocketJoint component FixedJointComponents::FixedJointComponent fixedJointComponent; mFixedJointsComponents.addComponent(entity, isJointDisabled, fixedJointComponent); void* allocatedMemory = mMemoryManager.allocate(MemoryManager::AllocationType::Pool, sizeof(FixedJoint)); const FixedJointInfo& info = static_cast(jointInfo); FixedJoint* joint = new (allocatedMemory) FixedJoint(entity, *this, info); newJoint = joint; mFixedJointsComponents.setJoint(entity, joint); break; } default: { assert(false); return nullptr; } } JointComponents::JointComponent jointComponent(jointInfo.body1->getEntity(), jointInfo.body2->getEntity(), newJoint, jointInfo.type, jointInfo.positionCorrectionTechnique, jointInfo.isCollisionEnabled); mJointsComponents.addComponent(entity, isJointDisabled, jointComponent); // If the collision between the two bodies of the constraint is disabled if (!jointInfo.isCollisionEnabled) { // Add the pair of bodies in the set of body pairs that cannot collide with each other mCollisionDetection.addNoCollisionPair(jointInfo.body1->getEntity(), jointInfo.body2->getEntity()); } RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Joint, "Joint " + std::to_string(newJoint->getEntity().id) + ": New joint created", __FILE__, __LINE__); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Joint, "Joint " + std::to_string(newJoint->getEntity().id) + ": " + newJoint->to_string(), __FILE__, __LINE__); // Add the joint into the joint list of the bodies involved in the joint addJointToBodies(jointInfo.body1->getEntity(), jointInfo.body2->getEntity(), entity); // Return the pointer to the created joint return newJoint; } // Destroy a joint /** * @param joint Pointer to the joint you want to destroy */ void PhysicsWorld::destroyJoint(Joint* joint) { assert(joint != nullptr); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Joint, "Joint " + std::to_string(joint->getEntity().id) + ": joint destroyed", __FILE__, __LINE__); // If the collision between the two bodies of the constraint was disabled if (!joint->isCollisionEnabled()) { // Remove the pair of bodies from the set of body pairs that cannot collide with each other mCollisionDetection.removeNoCollisionPair(joint->getBody1()->getEntity(), joint->getBody2()->getEntity()); } RigidBody* body1 = joint->getBody1(); RigidBody* body2 = joint->getBody2(); // Wake up the two bodies of the joint body1->setIsSleeping(false); body2->setIsSleeping(false); // Remove the joint from the joint list of the bodies involved in the joint mRigidBodyComponents.removeJointFromBody(body1->getEntity(), joint->getEntity()); mRigidBodyComponents.removeJointFromBody(body2->getEntity(), joint->getEntity()); size_t nbBytes = joint->getSizeInBytes(); Entity jointEntity = joint->getEntity(); // Destroy the corresponding entity and its components mJointsComponents.removeComponent(jointEntity); if (mBallAndSocketJointsComponents.hasComponent(jointEntity)) { mBallAndSocketJointsComponents.removeComponent(jointEntity); } if (mFixedJointsComponents.hasComponent(jointEntity)) { mFixedJointsComponents.removeComponent(jointEntity); } if (mHingeJointsComponents.hasComponent(jointEntity)) { mHingeJointsComponents.removeComponent(jointEntity); } if (mSliderJointsComponents.hasComponent(jointEntity)) { mSliderJointsComponents.removeComponent(jointEntity); } mEntityManager.destroyEntity(jointEntity); // Call the destructor of the joint joint->~Joint(); // Release the allocated memory mMemoryManager.release(MemoryManager::AllocationType::Pool, joint, nbBytes); } // Set the number of iterations for the velocity constraint solver /** * @param nbIterations Number of iterations for the velocity solver */ void PhysicsWorld::setNbIterationsVelocitySolver(uint nbIterations) { mNbVelocitySolverIterations = nbIterations; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Set nb iterations velocity solver to " + std::to_string(nbIterations), __FILE__, __LINE__); } // Add the joint to the list of joints of the two bodies involved in the joint void PhysicsWorld::addJointToBodies(Entity body1, Entity body2, Entity joint) { mRigidBodyComponents.addJointToBody(body1, joint); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(body1.id) + ": Joint " + std::to_string(joint.id) + " added to body", __FILE__, __LINE__); mRigidBodyComponents.addJointToBody(body2, joint); RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::Body, "Body " + std::to_string(body2.id) + ": Joint " + std::to_string(joint.id) + " added to body", __FILE__, __LINE__); } // 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 PhysicsWorld::createIslands() { RP3D_PROFILE("PhysicsWorld::createIslands()", mProfiler); // Reset all the isAlreadyInIsland variables of bodies and joints for (uint b=0; b < mRigidBodyComponents.getNbComponents(); b++) { mRigidBodyComponents.mIsAlreadyInIsland[b] = false; } for (uint32 i=0; i < mJointsComponents.getNbComponents(); i++) { mJointsComponents.mIsAlreadyInIsland[i] = false; } // Create a stack for the bodies to visit during the Depth First Search Stack bodyEntityIndicesToVisit(mMemoryManager.getSingleFrameAllocator()); uint nbTotalManifolds = 0; // For each rigid body component for (uint b=0; b < mRigidBodyComponents.getNbEnabledComponents(); b++) { // If the body has already been added to an island, we go to the next body if (mRigidBodyComponents.mIsAlreadyInIsland[b]) continue; // If the body is static, we go to the next body if (mRigidBodyComponents.mBodyTypes[b] == BodyType::STATIC) continue; // Reset the stack of bodies to visit bodyEntityIndicesToVisit.clear(); // Add the body into the stack of bodies to visit mRigidBodyComponents.mIsAlreadyInIsland[b] = true; bodyEntityIndicesToVisit.push(mRigidBodyComponents.mBodiesEntities[b]); // Create the new island uint32 islandIndex = mIslands.addIsland(nbTotalManifolds); // While there are still some bodies to visit in the stack while (bodyEntityIndicesToVisit.size() > 0) { // Get the next body to visit from the stack const Entity bodyToVisitEntity = bodyEntityIndicesToVisit.pop(); // Add the body into the island mIslands.bodyEntities[islandIndex].add(bodyToVisitEntity); RigidBody* rigidBodyToVisit = static_cast(mCollisionBodyComponents.getBody(bodyToVisitEntity)); // Awake the body if it is sleeping rigidBodyToVisit->setIsSleeping(false); // If the current body is static, we do not want to perform the DFS search across that body if (rigidBodyToVisit->getType() == BodyType::STATIC) continue; // If the body is involved in contacts with other bodies auto itBodyContactPairs = mCollisionDetection.mMapBodyToContactPairs.find(bodyToVisitEntity); if (itBodyContactPairs != mCollisionDetection.mMapBodyToContactPairs.end()) { // For each contact pair in which the current body is involded List& contactPairs = itBodyContactPairs->second; for (uint p=0; p < contactPairs.size(); p++) { ContactPair& pair = (*mCollisionDetection.mCurrentContactPairs)[contactPairs[p]]; // Check if the current contact pair has already been added into an island if (pair.isAlreadyInIsland) continue; // If the colliding body is a RigidBody (and not a CollisionBody) and is not a trigger if (mRigidBodyComponents.hasComponent(pair.body1Entity) && mRigidBodyComponents.hasComponent(pair.body2Entity) && !mCollidersComponents.getIsTrigger(pair.collider1Entity) && !mCollidersComponents.getIsTrigger(pair.collider2Entity)) { assert(pair.potentialContactManifoldsIndices.size() > 0); nbTotalManifolds += pair.potentialContactManifoldsIndices.size(); // 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 (mRigidBodyComponents.getIsAlreadyInIsland(otherBodyEntity)) continue; // Insert the other body into the stack of bodies to visit bodyEntityIndicesToVisit.push(otherBodyEntity); mRigidBodyComponents.setIsAlreadyInIsland(otherBodyEntity, true); } else { // Add the contact pair index in the list of contact pairs that won't be part of islands pair.isAlreadyInIsland = true; } } } // For each joint in which the current body is involved const List& joints = mRigidBodyComponents.getJoints(rigidBodyToVisit->getEntity()); for (uint32 i=0; i < joints.size(); i++) { // Check if the current joint has already been added into an island if (mJointsComponents.getIsAlreadyInIsland(joints[i])) continue; // Add the joint into the island mJointsComponents.setIsAlreadyInIsland(joints[i], true); const Entity body1Entity = mJointsComponents.getBody1Entity(joints[i]); const Entity body2Entity = mJointsComponents.getBody2Entity(joints[i]); const Entity otherBodyEntity = body1Entity == bodyToVisitEntity ? body2Entity : body1Entity; // Check if the other body has already been added to the island if (mRigidBodyComponents.getIsAlreadyInIsland(otherBodyEntity)) continue; // Insert the other body into the stack of bodies to visit bodyEntityIndicesToVisit.push(otherBodyEntity); mRigidBodyComponents.setIsAlreadyInIsland(otherBodyEntity, true); } } // Reset the isAlreadyIsland variable of the static bodies so that they // can also be included in the other islands for (uint j=0; j < mRigidBodyComponents.getNbEnabledComponents(); j++) { if (mRigidBodyComponents.mBodyTypes[j] == BodyType::STATIC) { mRigidBodyComponents.mIsAlreadyInIsland[j] = false; } } } mCollisionDetection.mMapBodyToContactPairs.clear(true); } // Put bodies to sleep if needed. /// For each island, if all the bodies have been almost still for a long enough period of /// time, we put all the bodies of the island to sleep. void PhysicsWorld::updateSleepingBodies(decimal timeStep) { RP3D_PROFILE("PhysicsWorld::updateSleepingBodies()", mProfiler); const decimal sleepLinearVelocitySquare = mSleepLinearVelocity * mSleepLinearVelocity; const decimal sleepAngularVelocitySquare = mSleepAngularVelocity * mSleepAngularVelocity; // For each island of the world for (uint i=0; i sleepLinearVelocitySquare || mRigidBodyComponents.getAngularVelocity(bodyEntity).lengthSquare() > sleepAngularVelocitySquare || !mRigidBodyComponents.getIsAllowedToSleep(bodyEntity)) { // Reset the sleep time of the body mRigidBodyComponents.setSleepTime(bodyEntity, decimal(0.0)); minSleepTime = decimal(0.0); } else { // If the body velocity is below the sleeping velocity threshold // Increase the sleep time decimal sleepTime = mRigidBodyComponents.getSleepTime(bodyEntity); mRigidBodyComponents.setSleepTime(bodyEntity, sleepTime + timeStep); sleepTime = mRigidBodyComponents.getSleepTime(bodyEntity); if (sleepTime < minSleepTime) { minSleepTime = sleepTime; } } } // If the velocity of all the bodies of the island is under the // sleeping velocity threshold for a period of time larger than // the time required to become a sleeping body if (minSleepTime >= mTimeBeforeSleep) { // Put all the bodies of the island to sleep for (uint b=0; b < mIslands.bodyEntities[i].size(); b++) { const Entity bodyEntity = mIslands.bodyEntities[i][b]; RigidBody* body = mRigidBodyComponents.getRigidBody(bodyEntity); body->setIsSleeping(true); } } } } // Enable/Disable the sleeping technique. /// The sleeping technique is used to put bodies that are not moving into sleep /// to speed up the simulation. /** * @param isSleepingEnabled True if you want to enable the sleeping technique * and false otherwise */ void PhysicsWorld::enableSleeping(bool isSleepingEnabled) { mIsSleepingEnabled = isSleepingEnabled; if (!mIsSleepingEnabled) { // For each body of the world List::Iterator it; for (it = mRigidBodies.begin(); it != mRigidBodies.end(); ++it) { // Wake up the rigid body (*it)->setIsSleeping(false); } } RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: isSleepingEnabled=" + (isSleepingEnabled ? std::string("true") : std::string("false")) , __FILE__, __LINE__); } // Set the number of iterations for the position constraint solver /** * @param nbIterations Number of iterations for the position solver */ void PhysicsWorld::setNbIterationsPositionSolver(uint nbIterations) { mNbPositionSolverIterations = nbIterations; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Set nb iterations position solver to " + std::to_string(nbIterations), __FILE__, __LINE__); } // Set the gravity vector of the world /** * @param gravity The gravity vector (in meter per seconds squared) */ void PhysicsWorld::setGravity(Vector3& gravity) { mConfig.gravity = gravity; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: Set gravity vector to " + gravity.to_string(), __FILE__, __LINE__); } // Set the sleep linear velocity. /// When the velocity of a body becomes smaller than the sleep linear/angular /// velocity for a given amount of time, the body starts sleeping and does not need /// to be simulated anymore. /** * @param sleepLinearVelocity The sleep linear velocity (in meters per second) */ void PhysicsWorld::setSleepLinearVelocity(decimal sleepLinearVelocity) { assert(sleepLinearVelocity >= decimal(0.0)); mSleepLinearVelocity = sleepLinearVelocity; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: sleepLinearVelocity= " + std::to_string(sleepLinearVelocity), __FILE__, __LINE__); } // Set the sleep angular velocity. /// When the velocity of a body becomes smaller than the sleep linear/angular /// velocity for a given amount of time, the body starts sleeping and does not need /// to be simulated anymore. /** * @param sleepAngularVelocity The sleep angular velocity (in radian per second) */ void PhysicsWorld::setSleepAngularVelocity(decimal sleepAngularVelocity) { assert(sleepAngularVelocity >= decimal(0.0)); mSleepAngularVelocity = sleepAngularVelocity; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: sleepAngularVelocity= " + std::to_string(sleepAngularVelocity), __FILE__, __LINE__); } // Set the time a body is required to stay still before sleeping /** * @param timeBeforeSleep Time a body is required to stay still before sleeping (in seconds) */ void PhysicsWorld::setTimeBeforeSleep(decimal timeBeforeSleep) { assert(timeBeforeSleep >= decimal(0.0)); mTimeBeforeSleep = timeBeforeSleep; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: timeBeforeSleep= " + std::to_string(timeBeforeSleep), __FILE__, __LINE__); } // Enable/Disable the gravity /** * @param isGravityEnabled True if you want to enable the gravity in the world * and false otherwise */ void PhysicsWorld::setIsGravityEnabled(bool isGravityEnabled) { mIsGravityEnabled = isGravityEnabled; RP3D_LOG(mConfig.worldName, Logger::Level::Information, Logger::Category::World, "Physics World: isGravityEnabled= " + (isGravityEnabled ? std::string("true") : std::string("false")), __FILE__, __LINE__); } // Return a constant pointer to a given CollisionBody of the world /** * @param index Index of a CollisionBody in the world * @return Constant pointer to a given CollisionBody */ const CollisionBody* PhysicsWorld::getCollisionBody(uint index) const { if (index >= getNbCollisionBodies()) { RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::World, "Error when getting collision body: index is out of bounds", __FILE__, __LINE__); } assert(index < mCollisionBodies.size()); return mCollisionBodies[index]; } // Return a pointer to a given CollisionBody of the world /** * @param index Index of a CollisionBody in the world * @return Pointer to a given CollisionBody */ CollisionBody* PhysicsWorld::getCollisionBody(uint index) { if (index >= getNbCollisionBodies()) { RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::World, "Error when getting collision body: index is out of bounds", __FILE__, __LINE__); } assert(index < mCollisionBodies.size()); return mCollisionBodies[index]; } // Return a constant pointer to a given RigidBody of the world /** * @param index Index of a RigidBody in the world * @return Constant pointer to a given RigidBody */ const RigidBody* PhysicsWorld::getRigidBody(uint index) const { if (index >= getNbRigidBodies()) { RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::World, "Error when getting rigid body: index is out of bounds", __FILE__, __LINE__); } assert(index < mRigidBodies.size()); return mRigidBodies[index]; } // Return a pointer to a given RigidBody of the world /** * @param index Index of a RigidBody in the world * @return Pointer to a given RigidBody */ RigidBody* PhysicsWorld::getRigidBody(uint index) { if (index >= getNbRigidBodies()) { RP3D_LOG(mConfig.worldName, Logger::Level::Error, Logger::Category::World, "Error when getting rigid body: index is out of bounds", __FILE__, __LINE__); } assert(index < mRigidBodies.size()); return mRigidBodies[index]; }