304 lines
12 KiB
C++
304 lines
12 KiB
C++
/********************************************************************************
|
|
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
|
|
* Copyright (c) 2010-2019 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 "CollisionWorld.h"
|
|
#include "utils/Profiler.h"
|
|
#include "utils/Logger.h"
|
|
|
|
// Namespaces
|
|
using namespace reactphysics3d;
|
|
using namespace std;
|
|
|
|
// Initialization of static fields
|
|
uint CollisionWorld::mNbWorlds = 0;
|
|
|
|
// Constructor
|
|
CollisionWorld::CollisionWorld(MemoryManager& memoryManager, const WorldSettings& worldSettings, Logger* logger, Profiler* profiler)
|
|
: mMemoryManager(memoryManager), mConfig(worldSettings), mEntityManager(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),
|
|
mBodies(mMemoryManager.getHeapAllocator()), mEventListener(nullptr),
|
|
mName(worldSettings.worldName), mIsProfilerCreatedByUser(profiler != nullptr),
|
|
mIsLoggerCreatedByUser(logger != nullptr) {
|
|
|
|
// Automatically generate a name for the world
|
|
if (mName == "") {
|
|
|
|
std::stringstream ss;
|
|
ss << "world";
|
|
|
|
if (mNbWorlds > 0) {
|
|
ss << mNbWorlds;
|
|
}
|
|
|
|
mName = ss.str();
|
|
}
|
|
|
|
#ifdef IS_PROFILING_ACTIVE
|
|
|
|
mProfiler = profiler;
|
|
|
|
// If the user has not provided its own profiler, we create one
|
|
if (mProfiler == nullptr) {
|
|
|
|
mProfiler = new Profiler();
|
|
|
|
// Add a destination file for the profiling data
|
|
mProfiler->addFileDestination("rp3d_profiling_" + mName + ".txt", Profiler::Format::Text);
|
|
}
|
|
|
|
|
|
// Set the profiler
|
|
mCollisionDetection.setProfiler(mProfiler);
|
|
|
|
#endif
|
|
|
|
#ifdef IS_LOGGING_ACTIVE
|
|
|
|
mLogger = logger;
|
|
|
|
// If the user has not provided its own logger, we create one
|
|
if (mLogger == nullptr) {
|
|
|
|
mLogger = new Logger();
|
|
|
|
// Add a log destination file
|
|
uint logLevel = static_cast<uint>(Logger::Level::Information) | static_cast<uint>(Logger::Level::Warning) |
|
|
static_cast<uint>(Logger::Level::Error);
|
|
mLogger->addFileDestination("rp3d_log_" + mName + ".html", logLevel, Logger::Format::HTML);
|
|
}
|
|
|
|
#endif
|
|
|
|
mNbWorlds++;
|
|
|
|
RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::World,
|
|
"Collision World: Collision world " + mName + " has been created");
|
|
RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::World,
|
|
"Collision World: Initial world settings: " + worldSettings.to_string());
|
|
}
|
|
|
|
// Destructor
|
|
CollisionWorld::~CollisionWorld() {
|
|
|
|
RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::World,
|
|
"Collision World: Collision world " + mName + " has been destroyed");
|
|
|
|
// Destroy all the collision bodies that have not been removed
|
|
for (int i=mBodies.size() - 1 ; i >= 0; i--) {
|
|
destroyCollisionBody(mBodies[i]);
|
|
}
|
|
|
|
#ifdef IS_PROFILING_ACTIVE
|
|
|
|
/// Delete the profiler
|
|
if (!mIsProfilerCreatedByUser) {
|
|
delete mProfiler;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef IS_LOGGING_ACTIVE
|
|
|
|
/// Delete the logger
|
|
if (!mIsLoggerCreatedByUser) {
|
|
delete mLogger;
|
|
}
|
|
|
|
#endif
|
|
|
|
assert(mBodies.size() == 0);
|
|
assert(mCollisionBodyComponents.getNbComponents() == 0);
|
|
assert(mTransformComponents.getNbComponents() == 0);
|
|
assert(mCollidersComponents.getNbComponents() == 0);
|
|
}
|
|
|
|
// 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* CollisionWorld::createCollisionBody(const Transform& transform) {
|
|
|
|
// Create a new entity for the body
|
|
Entity entity = mEntityManager.createEntity();
|
|
|
|
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
|
|
mBodies.add(collisionBody);
|
|
|
|
#ifdef IS_PROFILING_ACTIVE
|
|
|
|
collisionBody->setProfiler(mProfiler);
|
|
|
|
#endif
|
|
|
|
#ifdef IS_LOGGING_ACTIVE
|
|
collisionBody->setLogger(mLogger);
|
|
#endif
|
|
|
|
RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body,
|
|
"Body " + std::to_string(entity.id) + ": New collision body created");
|
|
|
|
// Return the pointer to the rigid body
|
|
return collisionBody;
|
|
}
|
|
|
|
// Destroy a collision body
|
|
/**
|
|
* @param collisionBody Pointer to the body to destroy
|
|
*/
|
|
void CollisionWorld::destroyCollisionBody(CollisionBody* collisionBody) {
|
|
|
|
RP3D_LOG(mLogger, Logger::Level::Information, Logger::Category::Body,
|
|
"Body " + std::to_string(collisionBody->getEntity().id) + ": collision body destroyed");
|
|
|
|
// Remove all the collision shapes of the body
|
|
collisionBody->removeAllCollisionShapes();
|
|
|
|
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
|
|
mBodies.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 CollisionWorld::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<Entity>& 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<Entity>& 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 CollisionWorld::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 CollisionWorld::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 CollisionWorld::getWorldAABB(const Collider* collider) const {
|
|
|
|
if (collider->getBroadPhaseId() == -1) {
|
|
return AABB();
|
|
}
|
|
|
|
return mCollisionDetection.getWorldAABB(collider);
|
|
}
|