diff --git a/CMakeLists.txt b/CMakeLists.txt index 0df07e6f..f23c134a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,7 +140,9 @@ SET (REACTPHYSICS3D_HEADERS "src/engine/OverlappingPair.h" "src/engine/Timer.h" "src/engine/Timer.cpp" + "src/components/Components.h" "src/components/TransformComponents.h" + "src/components/ProxyShapesComponents.h" "src/collision/CollisionCallback.h" "src/collision/OverlapCallback.h" "src/mathematics/mathematics.h" @@ -230,6 +232,7 @@ SET (REACTPHYSICS3D_SOURCES "src/engine/Entity.cpp" "src/engine/EntityManager.cpp" "src/components/TransformComponents.cpp" + "src/components/ProxyShapesComponents.cpp" "src/collision/CollisionCallback.cpp" "src/mathematics/mathematics_functions.cpp" "src/mathematics/Matrix2x2.cpp" diff --git a/src/body/CollisionBody.cpp b/src/body/CollisionBody.cpp index fa0d45f4..65724b16 100644 --- a/src/body/CollisionBody.cpp +++ b/src/body/CollisionBody.cpp @@ -80,6 +80,17 @@ ProxyShape* CollisionBody::addCollisionShape(CollisionShape* collisionShape, sizeof(ProxyShape))) ProxyShape(this, collisionShape, transform, decimal(1), mWorld.mMemoryManager); + // Add the proxy-shape component to the entity of the body + Vector3 localBoundsMin; + Vector3 localBoundsMax; + // TODO : Maybe this method can directly returns an AABB + proxyShape->getCollisionShape()->getLocalBounds(localBoundsMin, localBoundsMax); + + ProxyShapesComponents::ProxyShapeComponent proxyShapeComponent(proxyShape->getBroadPhaseId(), + AABB(localBoundsMin, localBoundsMax), + transform, collisionShape, decimal(1)); + mWorld.mProxyShapesComponents.addComponent(mEntity, mIsSleeping, proxyShapeComponent); + #ifdef IS_PROFILING_ACTIVE // Set the profiler diff --git a/src/body/RigidBody.cpp b/src/body/RigidBody.cpp index 92eabbf0..40b43c6d 100644 --- a/src/body/RigidBody.cpp +++ b/src/body/RigidBody.cpp @@ -286,6 +286,17 @@ ProxyShape* RigidBody::addCollisionShape(CollisionShape* collisionShape, sizeof(ProxyShape))) ProxyShape(this, collisionShape, transform, mass, mWorld.mMemoryManager); + // Add the proxy-shape component to the entity of the body + Vector3 localBoundsMin; + Vector3 localBoundsMax; + // TODO : Maybe this method can directly returns an AABB + proxyShape->getCollisionShape()->getLocalBounds(localBoundsMin, localBoundsMax); + + ProxyShapesComponents::ProxyShapeComponent proxyShapeComponent(proxyShape->getBroadPhaseId(), + AABB(localBoundsMin, localBoundsMax), + transform, collisionShape, mass); + mWorld.mProxyShapesComponents.addComponent(mEntity, mIsSleeping, proxyShapeComponent); + #ifdef IS_PROFILING_ACTIVE // Set the profiler diff --git a/src/components/Components.h b/src/components/Components.h new file mode 100644 index 00000000..409cd8f8 --- /dev/null +++ b/src/components/Components.h @@ -0,0 +1,93 @@ +/******************************************************************************** +* 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_COMPONENTS_H +#define REACTPHYSICS3D_COMPONENTS_H + +// Libraries +#include "configuration.h" +#include "engine/Entity.h" +#include "containers/Map.h" + +// ReactPhysics3D namespace +namespace reactphysics3d { + +// Class declarations +class MemoryAllocator; +class EntityManager; + +// Class Components +/** + * This class represent the abstract class to store components of the ECS. + */ +class Components { + + protected: + + // -------------------- Constants -------------------- // + + /// Number of components to allocated at the beginning + const uint32 INIT_ALLOCATED_COMPONENTS = 10; + + /// Number of valid entities to hit before stopping garbage collection + const uint32 GARBAGE_COLLECTION_MAX_VALID_ENTITIES = 5; + + // -------------------- Attributes -------------------- // + + /// Memory allocator + MemoryAllocator& mMemoryAllocator; + + /// Current number of components + uint32 mNbComponents; + + /// Number of allocated components + uint32 mNbAllocatedComponents; + + /// Allocated memory for all the data of the components + void* mBuffer; + + /// Map an entity to the index of its component in the array + Map mMapEntityToComponentIndex; + + public: + + // -------------------- Methods -------------------- // + + /// Constructor + Components(MemoryAllocator& allocator) + : mMemoryAllocator(allocator), mNbComponents(0), mNbAllocatedComponents(0), + mBuffer(nullptr), mMapEntityToComponentIndex(allocator) { + + } + + /// Destructor + virtual ~Components() { + + } +}; + +} + +#endif diff --git a/src/components/ProxyShapesComponents.cpp b/src/components/ProxyShapesComponents.cpp new file mode 100644 index 00000000..e7ce310e --- /dev/null +++ b/src/components/ProxyShapesComponents.cpp @@ -0,0 +1,502 @@ +/******************************************************************************** +* 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 "ProxyShapesComponents.h" +#include "engine/EntityManager.h" +#include +#include + +// We want to use the ReactPhysics3D namespace +using namespace reactphysics3d; + +// Constructor +ProxyShapesComponents::ProxyShapesComponents(MemoryAllocator& allocator) + :mMemoryAllocator(allocator), mNbComponents(0), mNbAllocatedComponents(0), + mSleepingStartIndex(0), mBuffer(nullptr), mMapEntityToComponentIndex(allocator) { + + // Allocate memory for the components data + allocate(INIT_ALLOCATED_COMPONENTS); +} + +// Destructor +ProxyShapesComponents::~ProxyShapesComponents() { + + if (mNbAllocatedComponents > 0) { + + // Destroy all the remaining components + for (uint32 i = 0; i < mNbComponents; i++) { + + // TODO : MAke sure we do not delete already deleted components + destroyComponent(i); + } + + // Size for the data of a single component (in bytes) + const size_t totalSizeBytes = mNbAllocatedComponents * COMPONENT_DATA_SIZE; + + // Release the allocated memory + mMemoryAllocator.release(mBuffer, totalSizeBytes); + } +} + +// Allocate memory for a given number of components +void ProxyShapesComponents::allocate(uint32 nbComponentsToAllocate) { + + assert(nbComponentsToAllocate > mNbAllocatedComponents); + + // Size for the data of a single component (in bytes) + const size_t totalSizeBytes = nbComponentsToAllocate * COMPONENT_DATA_SIZE; + + // Allocate memory + void* newBuffer = mMemoryAllocator.allocate(totalSizeBytes); + assert(newBuffer != nullptr); + + // New pointers to components data + Entity* newEntities = static_cast(newBuffer); + int* newBroadPhaseIds = reinterpret_cast(newEntities + nbComponentsToAllocate); + AABB* newLocalBounds = reinterpret_cast(newBroadPhaseIds + nbComponentsToAllocate); + Transform* newLocalToBodyTransforms = reinterpret_cast(newLocalBounds + nbComponentsToAllocate); + CollisionShape** newCollisionShapes = reinterpret_cast(newLocalToBodyTransforms + nbComponentsToAllocate); + decimal* newMasses = reinterpret_cast(newCollisionShapes + nbComponentsToAllocate); + uint32* newPreviousBodyProxyShapes = reinterpret_cast(newMasses + nbComponentsToAllocate); + uint32* newNextBodyProxyShapes = reinterpret_cast(newPreviousBodyProxyShapes + nbComponentsToAllocate); + + // If there was already components before + if (mNbComponents > 0) { + + // Copy component data from the previous buffer to the new one + memcpy(newEntities, mEntities, mNbComponents * sizeof(Entity)); + memcpy(newBroadPhaseIds, mBroadPhaseIds, mNbComponents * sizeof(int)); + memcpy(newLocalBounds, mLocalBounds, mNbComponents * sizeof(AABB)); + memcpy(newLocalToBodyTransforms, mLocalToBodyTransforms, mNbComponents * sizeof(Transform)); + memcpy(newCollisionShapes, mCollisionShapes, mNbComponents * sizeof(CollisionShape*)); + memcpy(newMasses, mMasses, mNbComponents * sizeof(decimal)); + memcpy(newPreviousBodyProxyShapes, mPreviousBodyProxyShapes, mNbComponents * sizeof(uint32)); + memcpy(newNextBodyProxyShapes, mNextBodyProxyShapes, mNbComponents * sizeof(uint32)); + + // Deallocate previous memory + mMemoryAllocator.release(mBuffer, mNbAllocatedComponents * COMPONENT_DATA_SIZE); + } + + mBuffer = newBuffer; + mEntities = newEntities; + mBroadPhaseIds = newBroadPhaseIds; + mLocalBounds = newLocalBounds; + mLocalToBodyTransforms = newLocalToBodyTransforms; + mCollisionShapes = newCollisionShapes; + mMasses = newMasses; + mPreviousBodyProxyShapes = newPreviousBodyProxyShapes; + mNextBodyProxyShapes = newNextBodyProxyShapes; + + mNbAllocatedComponents = nbComponentsToAllocate; +} + +// Add a new proxy-shape at the beginning of the linked-list of proxy-shapes of a given entity +// If it is the first proxy-shape for the entity, it will create the first item of the linked-list +void ProxyShapesComponents::linkProxyShapeWithEntity(Entity entity, uint32 proxyShapeComponentIndex) { + + auto it = mMapEntityToComponentIndex.find(entity); + if (it != mMapEntityToComponentIndex.end()) { + + // Get the first proxy-shape of the linked-list + uint32 firstProxyShapeIndex = (*it).second; + + assert(!hasPreviousProxyShape(firstProxyShapeIndex)); + + // Update the previous index of the first item of the list + mPreviousBodyProxyShapes[firstProxyShapeIndex] = proxyShapeComponentIndex; + + // Map the entity to the new head of the linked-list + mMapEntityToComponentIndex[entity] = proxyShapeComponentIndex; + + // Add the new proxy-shape at the beginning of the linked-list + const uint32 nextIndex = firstProxyShapeIndex; + const uint32 previousIndex = proxyShapeComponentIndex; + new (mNextBodyProxyShapes + proxyShapeComponentIndex) uint32(nextIndex); + new (mPreviousBodyProxyShapes + proxyShapeComponentIndex) uint32(previousIndex); + + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[proxyShapeComponentIndex]] == proxyShapeComponentIndex); + } + else { // If the entity does not have a proxy-shape yet + + mMapEntityToComponentIndex.add(Pair(entity, proxyShapeComponentIndex)); + + // The new proxy-shape has no previous/next components in the linked-list + new (mNextBodyProxyShapes + proxyShapeComponentIndex) uint32(proxyShapeComponentIndex); + new (mPreviousBodyProxyShapes + proxyShapeComponentIndex) uint32(proxyShapeComponentIndex); + + assert(!hasNextProxyShape(proxyShapeComponentIndex)); + } + + assert(!hasPreviousProxyShape(proxyShapeComponentIndex)); +} + +// Add a component +void ProxyShapesComponents::addComponent(Entity entity, bool isSleeping, const ProxyShapeComponent& component) { + + // If we need to allocate more components + if (mNbComponents == mNbAllocatedComponents) { + allocate(mNbAllocatedComponents * 2); + } + + uint32 index; + + // If the component to add is part of a sleeping entity + if (isSleeping) { + + // Add the component at the end of the array + index = mNbComponents; + + mSleepingStartIndex = index; + } + // If the component to add is not part of a sleeping entity + else { + + // If there already are sleeping components + if (mSleepingStartIndex != mNbComponents) { + + // Move the first sleeping component to the end of the array + moveComponentToIndex(mSleepingStartIndex, mNbComponents); + } + + index = mSleepingStartIndex; + + mSleepingStartIndex++; + } + + // Insert the new component data + new (mEntities + index) Entity(entity); + new (mBroadPhaseIds + index) int(component.broadPhaseId); + new (mLocalBounds + index) AABB(component.localBounds); + new (mLocalToBodyTransforms + index) Transform(component.localToBodyTransform); + mCollisionShapes[index] = component.collisionShape; + new (mMasses + index) decimal(component.mass); + + mNbComponents++; + + // Map the entity with the new component lookup index + linkProxyShapeWithEntity(entity, index); + + assert(mSleepingStartIndex <= mNbComponents); + + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index]] == index || !hasNextProxyShape(index)); + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index]] == index || !hasPreviousProxyShape(index)); +} + +// Move a component from a source to a destination index in the components array +// The destination location must contain a constructed object +void ProxyShapesComponents::moveComponentToIndex(uint32 srcIndex, uint32 destIndex) { + + const Entity entity = mEntities[srcIndex]; + + const bool isFirstProxyShapeOfBody = mMapEntityToComponentIndex[entity] == srcIndex; + + assert(isFirstProxyShapeOfBody || hasPreviousProxyShape(srcIndex)); + + // Copy the data of the source component to the destination location + new (mEntities + destIndex) Entity(mEntities[srcIndex]); + new (mBroadPhaseIds + destIndex) int(mBroadPhaseIds[srcIndex]); + new (mLocalBounds + destIndex) AABB(mLocalBounds[srcIndex]); + new (mLocalToBodyTransforms + destIndex) Transform(mLocalToBodyTransforms[srcIndex]); + mCollisionShapes[destIndex] = mCollisionShapes[srcIndex]; + new (mMasses + destIndex) decimal(mMasses[srcIndex]); + new (mPreviousBodyProxyShapes + destIndex) uint32(hasPreviousProxyShape(srcIndex) ? mPreviousBodyProxyShapes[srcIndex] : destIndex); + new (mNextBodyProxyShapes + destIndex) uint32(hasNextProxyShape(srcIndex) ? mNextBodyProxyShapes[srcIndex] : destIndex); + + // Update the the next proxy-shape index of the previous proxy-shape + if (hasPreviousProxyShape(destIndex)) { + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[destIndex]] == srcIndex); + mNextBodyProxyShapes[mPreviousBodyProxyShapes[destIndex]] = destIndex; + } + + // Update the the previous proxy-shape index of the next proxy-shape + if (hasNextProxyShape(destIndex)) { + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[destIndex]] == srcIndex); + mPreviousBodyProxyShapes[mNextBodyProxyShapes[destIndex]] = destIndex; + } + + // Destroy the source component + destroyComponent(srcIndex); + + // Update the entity to component index mapping if it is the first proxy-shape of the body + if (isFirstProxyShapeOfBody) { + + mMapEntityToComponentIndex[entity] = destIndex; + assert(mMapEntityToComponentIndex[mEntities[destIndex]] == destIndex); + } + + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[destIndex]] == destIndex || !hasNextProxyShape(destIndex)); + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[destIndex]] == destIndex || !hasPreviousProxyShape(destIndex)); +} + +// Swap two components in the array +void ProxyShapesComponents::swapComponents(uint32 index1, uint32 index2) { + + // Copy component 1 data + Entity entity1(mEntities[index1]); + int broadPhaseId1 = mBroadPhaseIds[index1]; + AABB localBounds1 = mLocalBounds[index1]; + Transform localToBodyTransform1 = mLocalToBodyTransforms[index1]; + CollisionShape* collisionShape1 = mCollisionShapes[index1]; + decimal mass1 = mMasses[index1]; + uint32 previousProxyShape1 = hasPreviousProxyShape(index1) ? mPreviousBodyProxyShapes[index1] : index2; + uint32 nextProxyShape1 = hasNextProxyShape(index1) ? mNextBodyProxyShapes[index1] : index2; + + const bool isFirstBodyProxyShape1 = mMapEntityToComponentIndex[mEntities[index1]] == index1; + + // Destroy component 1 + destroyComponent(index1); + + moveComponentToIndex(index2, index1); + + // Reconstruct component 1 at component 2 location + new (mEntities + index2) Entity(entity1); + new (mBroadPhaseIds + index2) int(broadPhaseId1); + new (mLocalBounds + index2) AABB(localBounds1); + new (mLocalToBodyTransforms + index2) Transform(localToBodyTransform1); + mCollisionShapes[index2] = collisionShape1; + new (mMasses + index2) decimal(mass1); + new (mPreviousBodyProxyShapes + index2) uint32(previousProxyShape1); + new (mNextBodyProxyShapes + index2) uint32(nextProxyShape1); + + // Update the the next proxy-shape index of the previous proxy-shape + if (hasPreviousProxyShape(index2)) { + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index2]] == index1); + mNextBodyProxyShapes[mPreviousBodyProxyShapes[index2]] = index2; + } + + // Update the the previous proxy-shape index of the next proxy-shape + if (hasNextProxyShape(index2)) { + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index2]] == index1); + mPreviousBodyProxyShapes[mNextBodyProxyShapes[index2]] = index2; + } + + // Update the entity to component index mapping if it is the first body proxy-shape + if (isFirstBodyProxyShape1) { + assert(!hasPreviousProxyShape(index2)); + mMapEntityToComponentIndex[entity1] = index2; + } + + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index1]] == index1 || !hasNextProxyShape(index1)); + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index2]] == index2 || !hasNextProxyShape(index2)); + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index1]] == index1 || !hasPreviousProxyShape(index1)); + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index2]] == index2 || !hasPreviousProxyShape(index2)); +} + +// Perform garbage collection to remove unused components +void ProxyShapesComponents::garbageCollection(const EntityManager& entityManager) { + + // TODO : Make sure we call this method each frame + + // We use lazy garbage collection. The idea is to pick random components and destroy + // them if their corresponding entities have been destroyed. We do this until we hit + // GARBAGE_COLLECTION_MAX_VALID_ENTITIES in a row. Therefore, it cost almost nothing + // if there are no destroyed entities and it very quickly destroys components where there + // are a lot of destroyed entities. + + uint32 nbHitValidEntitiesInARow = 0; + + // For random number generation + std::random_device rd; + std::mt19937 eng(rd()); + + while (mNbComponents > 0 && nbHitValidEntitiesInARow < GARBAGE_COLLECTION_MAX_VALID_ENTITIES) { + + // Select a random index in the components array + std::uniform_int_distribution distr(0, mNbComponents - 1); + uint32 i = distr(eng); + + // If the corresponding entity is valid + if (entityManager.isValid(mEntities[i])) { + nbHitValidEntitiesInARow++; + + continue; + } + + nbHitValidEntitiesInARow = 0; + + // Destroy the component + removeComponent(i); + } +} + +// Remove a component at a given index +void ProxyShapesComponents::removeComponent(uint32 index) { + + assert(index < mNbComponents); + + // We want to keep the arrays tightly packed. Therefore, when a component is removed, + // we replace it with the last element of the array. But we need to make sure that sleeping + // and non-sleeping components stay grouped together. + + // If the proxy-shape to destroy does not have a previous proxy-shape in the linked-list of proxy-shapes of its body + if (!hasPreviousProxyShape(index)) { + + // Remove the mapping from entity to component index + mMapEntityToComponentIndex.remove(mEntities[index]); + + // If the proxy-shape has a next proxy-shape in the linked-list + if (hasNextProxyShape(index)) { + + assert(mEntities[index] == mEntities[mNextBodyProxyShapes[index]]); + + mMapEntityToComponentIndex.add(Pair(mEntities[mNextBodyProxyShapes[index]], mNextBodyProxyShapes[index])); + } + } + + // Update the linked-list of proxy-shapes of a body when a proxy-shape is removed + if (hasPreviousProxyShape(index)) { + + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index]] == index); + assert(mEntities[index] == mEntities[mPreviousBodyProxyShapes[index]]); + + // If the current proxy-shape to remove has a next proxy-shape in the linked-list + if (hasNextProxyShape(index)) { + + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index]] == index); + + // Make sure the next proxy-shape will follow the previous proxy-shape in the linked-list + mNextBodyProxyShapes[mPreviousBodyProxyShapes[index]] = mNextBodyProxyShapes[index]; + } + else { + + mNextBodyProxyShapes[mPreviousBodyProxyShapes[index]] = mPreviousBodyProxyShapes[index]; + } + } + + // Update the linked-list of proxy-shapes of a body when a proxy-shape is removed + if (hasNextProxyShape(index)) { + + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index]] == index); + assert(mEntities[index] == mEntities[mNextBodyProxyShapes[index]]); + + // If the current proxy-shape to remove has a previous proxy-shape in the linked-list + if (hasPreviousProxyShape(index)) { + + // Make sure the previous proxy-shape will precede the next proxy-shape in the linked-list + mPreviousBodyProxyShapes[mNextBodyProxyShapes[index]] = mPreviousBodyProxyShapes[index]; + } + else { + + mPreviousBodyProxyShapes[mNextBodyProxyShapes[index]] = mNextBodyProxyShapes[index]; + } + } + + // Destroy the component + destroyComponent(index); + + // If the component to remove is sleeping + if (index >= mSleepingStartIndex) { + + // If the component is not the last one + if (index != mNbComponents - 1) { + + // We replace it by the last sleeping component + moveComponentToIndex(mNbComponents - 1, index); + } + } + else { // If the component to remove is not sleeping + + // If it not the last awake component + if (index != mSleepingStartIndex - 1) { + + // We replace it by the last awake component + moveComponentToIndex(mSleepingStartIndex - 1, index); + } + + // If there are sleeping components at the end + if (mSleepingStartIndex != mNbComponents) { + + // We replace the last awake component by the last sleeping component + moveComponentToIndex(mNbComponents - 1, index); + + mSleepingStartIndex--; + } + } + + mNbComponents--; +} + +// Return true if a given proxy-shape component has a previous proxy-shape in the linked-list of proxy-shapes of a body +bool ProxyShapesComponents::hasPreviousProxyShape(uint32 index) const { + assert(index < mNbComponents); + return mPreviousBodyProxyShapes[index] != index; +} + +// Return true if a given proxy-shape component has a next proxy-shape in the linked-list of proxy-shapes of a body +bool ProxyShapesComponents::hasNextProxyShape(uint32 index) const { + assert(index < mNbComponents); + return mNextBodyProxyShapes[index] != index; +} + +// Destroy a component at a given index +void ProxyShapesComponents::destroyComponent(uint32 index) { + + + mEntities[index].~Entity(); + mLocalBounds[index].~AABB(); + mLocalToBodyTransforms[index].~Transform(); + mCollisionShapes[index] = nullptr; +} + +// Notify if a given entity is sleeping or not +void ProxyShapesComponents::setIsEntitySleeping(Entity entity, bool isSleeping) { + + const uint32 index = mMapEntityToComponentIndex[entity]; + + // If the component was sleeping and is not sleeping anymore + if (!isSleeping && index >= mSleepingStartIndex) { + + assert(mSleepingStartIndex < mNbComponents); + + // If the sleeping component is not the first sleeping component + if (mSleepingStartIndex != index) { + + // Swap the first sleeping component with the one we need to wake up + swapComponents(index, mSleepingStartIndex); + } + + mSleepingStartIndex++; + } + // If the component was awake and must now go to sleep + else if (isSleeping && index < mSleepingStartIndex) { + + assert(mSleepingStartIndex > 0); + + // If the awake component is not the only awake component + if (index != mSleepingStartIndex - 1) { + + // Swap the last awake component with the one we need to put to sleep + swapComponents(index, mSleepingStartIndex - 1); + } + + mSleepingStartIndex--; + } + + assert(mSleepingStartIndex <= mNbComponents); + assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index]] == index || !hasNextProxyShape(index)); + assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index]] == index || !hasPreviousProxyShape(index)); +} + diff --git a/src/components/ProxyShapesComponents.h b/src/components/ProxyShapesComponents.h new file mode 100644 index 00000000..8360f256 --- /dev/null +++ b/src/components/ProxyShapesComponents.h @@ -0,0 +1,181 @@ +/******************************************************************************** +* 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_PROXY_SHAPES_COMPONENTS_H +#define REACTPHYSICS3D_PROXY_SHAPES_COMPONENTS_H + +// Libraries +#include "mathematics/Transform.h" +#include "engine/Entity.h" +#include "containers/Map.h" +#include "collision/shapes/AABB.h" + +// ReactPhysics3D namespace +namespace reactphysics3d { + +// Class declarations +class MemoryAllocator; +class EntityManager; +class AABB; +class CollisionShape; + +// Class ProxyShapesComponents +/** + * This class represent the component of the ECS that contains the proxy-shapes of the + * different entities. There can be several proxy shapes for each entity (body). We store + * the proxy shapes in a flat array but we keep two arrays with previous/next proxy shapes + * link information to quickly know all the proxy shapes of a given entity (body). We also make + * sure that proxy shapes of sleeping entities (bodies) are always stored at the end of the array. + */ +class ProxyShapesComponents { + + private: + + // -------------------- Constants -------------------- // + + /// Number of components to allocated at the beginning + const uint32 INIT_ALLOCATED_COMPONENTS = 10; + + /// Number of valid entities to hit before stopping garbage collection + const uint32 GARBAGE_COLLECTION_MAX_VALID_ENTITIES = 5; + + const size_t COMPONENT_DATA_SIZE = sizeof(Entity) + sizeof(int) + sizeof(AABB) + + sizeof(Transform) + sizeof(CollisionShape*) + sizeof(decimal) + sizeof(uint32) + + sizeof(uint32); + + // -------------------- Attributes -------------------- // + + /// Memory allocator + MemoryAllocator& mMemoryAllocator; + + /// Current number of components + uint32 mNbComponents; + + /// Number of allocated components + uint32 mNbAllocatedComponents; + + /// Index of the first component of a sleeping entity (sleeping components are stored at the end) + uint32 mSleepingStartIndex; + + /// Allocated memory for all the data of the components + void* mBuffer; + + /// Map an entity to the index of its component in the array + Map mMapEntityToComponentIndex; + + /// Array of entities of each component + Entity* mEntities; + + /// Ids of the proxy-shapes for the broad-phase algorithm + // TODO : Try to change type to uint32 + int* mBroadPhaseIds; + + /// Local-space bounds of a proxy-shape + AABB* mLocalBounds; + + /// Transform from local-space of the proxy-shape to the body-space of its body + Transform* mLocalToBodyTransforms; + + /// Pointers to the collision shapes of the proxy-shapes + CollisionShape** mCollisionShapes; + + /// Masses of the proxy-shapes + decimal* mMasses; + + /// Index of the previous proxy-shape in the same body + /// mPreviousBodyProxyShapes[i] == i means that the proxy-shape component has no previous proxy-shape + uint32* mPreviousBodyProxyShapes; + + /// Index of the next proxy-shape in the same body + /// mNextBodyProxyShapes[i] == i means that the proxy-shape component has no next proxy-shape + uint32* mNextBodyProxyShapes; + + // -------------------- Methods -------------------- // + + /// Remove a component at a given index + void removeComponent(uint32 index); + + /// Destroy a component at a given index + void destroyComponent(uint32 index); + + /// Move a component from a source to a destination index in the components array + void moveComponentToIndex(uint32 srcIndex, uint32 destIndex); + + /// Swap two components in the array + void swapComponents(uint32 index1, uint32 index2); + + /// Add a new proxy-shape at the end of the linked-list of proxy-shapes of a given entity + void linkProxyShapeWithEntity(Entity entity, uint32 proxyShapeComponentIndex); + + /// Return true if a given proxy-shape component has a previous proxy-shape in the linked-list of proxy-shapes of a body + bool hasPreviousProxyShape(uint32 index) const; + + /// Return true if a given proxy-shape component has a next proxy-shape in the linked-list of proxy-shapes of a body + bool hasNextProxyShape(uint32 index) const; + + public: + + /// Structure for the data of a proxy shape component + struct ProxyShapeComponent { + + int broadPhaseId; + AABB localBounds; + Transform localToBodyTransform; + CollisionShape* collisionShape; + decimal mass; + + /// Constructor + ProxyShapeComponent(int broadPhaseId, AABB localBounds, Transform localToBodyTransform, + CollisionShape* collisionShape, decimal mass) + :broadPhaseId(broadPhaseId), localBounds(localBounds), localToBodyTransform(localToBodyTransform), + collisionShape(collisionShape), mass(mass) { + + } + }; + + // -------------------- Methods -------------------- // + + /// Constructor + ProxyShapesComponents(MemoryAllocator& allocator); + + /// Destructor + ~ProxyShapesComponents(); + + /// Allocate memory for a given number of components + void allocate(uint32 nbComponentsToAllocate); + + /// Add a component + void addComponent(Entity entity, bool isSleeping, const ProxyShapeComponent& component); + + /// Notify if a given entity is sleeping or not + void setIsEntitySleeping(Entity entity, bool isSleeping); + + /// Perform garbage collection to remove unused components + void garbageCollection(const EntityManager& entityManager); +}; + +} + +#endif diff --git a/src/components/TransformComponents.cpp b/src/components/TransformComponents.cpp index 0d20f4b3..dbe026c8 100644 --- a/src/components/TransformComponents.cpp +++ b/src/components/TransformComponents.cpp @@ -48,6 +48,8 @@ TransformComponents::~TransformComponents() { // Destroy all the remaining components for (uint32 i = 0; i < mNbComponents; i++) { + + // TODO : MAke sure we do not delete already deleted components destroyComponent(i); } @@ -103,20 +105,22 @@ void TransformComponents::addComponent(Entity entity, bool isSleeping, const Tra uint32 index; // If the component to add is part of a sleeping entity or there are no sleeping entity - if (isSleeping || mSleepingStartIndex == mNbComponents) { + if (isSleeping) { // Add the component at the end of the array index = mNbComponents; - if (isSleeping) { - mSleepingStartIndex = index; - } + mSleepingStartIndex = index; } - // If the component to add is not part of a sleeping entity and there are others sleeping components + // If the component to add is not part of a sleeping entity else { - // Move the first sleeping component to the end of the array - moveComponentToIndex(mSleepingStartIndex, mNbComponents); + // If there already are sleeping components + if (mSleepingStartIndex != mNbComponents) { + + // Move the first sleeping component to the end of the array + moveComponentToIndex(mSleepingStartIndex, mNbComponents); + } index = mSleepingStartIndex; diff --git a/src/engine/CollisionWorld.cpp b/src/engine/CollisionWorld.cpp index e8076ad6..1d73fe9a 100644 --- a/src/engine/CollisionWorld.cpp +++ b/src/engine/CollisionWorld.cpp @@ -38,7 +38,7 @@ uint CollisionWorld::mNbWorlds = 0; // Constructor CollisionWorld::CollisionWorld(const WorldSettings& worldSettings, Logger* logger, Profiler* profiler) : mConfig(worldSettings), mEntityManager(mMemoryManager.getPoolAllocator()), - mTransformComponents(mMemoryManager.getBaseAllocator()), + mTransformComponents(mMemoryManager.getBaseAllocator()), mProxyShapesComponents(mMemoryManager.getBaseAllocator()), mCollisionDetection(this, mMemoryManager), mBodies(mMemoryManager.getPoolAllocator()), mCurrentBodyId(0), mFreeBodiesIds(mMemoryManager.getPoolAllocator()), mEventListener(nullptr), mName(worldSettings.worldName), mIsProfilerCreatedByUser(profiler != nullptr), @@ -245,6 +245,7 @@ void CollisionWorld::notifyBodySleeping(Entity entity, bool isSleeping) { // Notify all the components mTransformComponents.setIsEntitySleeping(entity, isSleeping); + mProxyShapesComponents.setIsEntitySleeping(entity, isSleeping); } // Test if the AABBs of two bodies overlap diff --git a/src/engine/CollisionWorld.h b/src/engine/CollisionWorld.h index 58dc91d5..5b44c073 100644 --- a/src/engine/CollisionWorld.h +++ b/src/engine/CollisionWorld.h @@ -34,6 +34,7 @@ #include "memory/MemoryManager.h" #include "engine/EntityManager.h" #include "components/TransformComponents.h" +#include "components/ProxyShapesComponents.h" /// Namespace reactphysics3d namespace reactphysics3d { @@ -74,6 +75,9 @@ class CollisionWorld { /// Transform Components TransformComponents mTransformComponents; + /// Proxy-Shapes Components + ProxyShapesComponents mProxyShapesComponents; + /// Reference to the collision detection CollisionDetection mCollisionDetection;