Add ProxyShapesComponents
This commit is contained in:
parent
96b02cfcca
commit
4e438d3ccc
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
93
src/components/Components.h
Normal file
93
src/components/Components.h
Normal file
|
@ -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<Entity, uint32> mMapEntityToComponentIndex;
|
||||
|
||||
public:
|
||||
|
||||
// -------------------- Methods -------------------- //
|
||||
|
||||
/// Constructor
|
||||
Components(MemoryAllocator& allocator)
|
||||
: mMemoryAllocator(allocator), mNbComponents(0), mNbAllocatedComponents(0),
|
||||
mBuffer(nullptr), mMapEntityToComponentIndex(allocator) {
|
||||
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
virtual ~Components() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
502
src/components/ProxyShapesComponents.cpp
Normal file
502
src/components/ProxyShapesComponents.cpp
Normal file
|
@ -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 <cassert>
|
||||
#include <random>
|
||||
|
||||
// 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<Entity*>(newBuffer);
|
||||
int* newBroadPhaseIds = reinterpret_cast<int*>(newEntities + nbComponentsToAllocate);
|
||||
AABB* newLocalBounds = reinterpret_cast<AABB*>(newBroadPhaseIds + nbComponentsToAllocate);
|
||||
Transform* newLocalToBodyTransforms = reinterpret_cast<Transform*>(newLocalBounds + nbComponentsToAllocate);
|
||||
CollisionShape** newCollisionShapes = reinterpret_cast<CollisionShape**>(newLocalToBodyTransforms + nbComponentsToAllocate);
|
||||
decimal* newMasses = reinterpret_cast<decimal*>(newCollisionShapes + nbComponentsToAllocate);
|
||||
uint32* newPreviousBodyProxyShapes = reinterpret_cast<uint32*>(newMasses + nbComponentsToAllocate);
|
||||
uint32* newNextBodyProxyShapes = reinterpret_cast<uint32*>(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, uint32>(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<uint32> 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<Entity, uint32>(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));
|
||||
}
|
||||
|
181
src/components/ProxyShapesComponents.h
Normal file
181
src/components/ProxyShapesComponents.h
Normal file
|
@ -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<Entity, uint32> 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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user