Start working on ECS

This commit is contained in:
Daniel Chappuis 2018-12-23 23:18:05 +01:00
parent d905ff5c7f
commit fa9b1817fe
17 changed files with 756 additions and 14 deletions

View File

@ -128,6 +128,8 @@ SET (REACTPHYSICS3D_HEADERS
"src/constraint/HingeJoint.h" "src/constraint/HingeJoint.h"
"src/constraint/Joint.h" "src/constraint/Joint.h"
"src/constraint/SliderJoint.h" "src/constraint/SliderJoint.h"
"src/engine/Entity.h"
"src/engine/EntityManager.h"
"src/engine/CollisionWorld.h" "src/engine/CollisionWorld.h"
"src/engine/ConstraintSolver.h" "src/engine/ConstraintSolver.h"
"src/engine/ContactSolver.h" "src/engine/ContactSolver.h"
@ -138,6 +140,7 @@ SET (REACTPHYSICS3D_HEADERS
"src/engine/OverlappingPair.h" "src/engine/OverlappingPair.h"
"src/engine/Timer.h" "src/engine/Timer.h"
"src/engine/Timer.cpp" "src/engine/Timer.cpp"
"src/components/TransformComponents.h"
"src/collision/CollisionCallback.h" "src/collision/CollisionCallback.h"
"src/collision/OverlapCallback.h" "src/collision/OverlapCallback.h"
"src/mathematics/mathematics.h" "src/mathematics/mathematics.h"
@ -224,6 +227,9 @@ SET (REACTPHYSICS3D_SOURCES
"src/engine/Material.cpp" "src/engine/Material.cpp"
"src/engine/OverlappingPair.cpp" "src/engine/OverlappingPair.cpp"
"src/engine/Timer.cpp" "src/engine/Timer.cpp"
"src/engine/Entity.cpp"
"src/engine/EntityManager.cpp"
"src/components/TransformComponents.cpp"
"src/collision/CollisionCallback.cpp" "src/collision/CollisionCallback.cpp"
"src/mathematics/mathematics_functions.cpp" "src/mathematics/mathematics_functions.cpp"
"src/mathematics/Matrix2x2.cpp" "src/mathematics/Matrix2x2.cpp"

View File

@ -35,8 +35,8 @@ using namespace reactphysics3d;
/** /**
* @param id ID of the new body * @param id ID of the new body
*/ */
Body::Body(bodyindex id) Body::Body(Entity entity, bodyindex id)
: mID(id), mIsAlreadyInIsland(false), mIsAllowedToSleep(true), mIsActive(true), : mID(id), mEntity(entity), mIsAlreadyInIsland(false), mIsAllowedToSleep(true), mIsActive(true),
mIsSleeping(false), mSleepTime(0), mUserData(nullptr) { mIsSleeping(false), mSleepTime(0), mUserData(nullptr) {
#ifdef IS_LOGGING_ACTIVE #ifdef IS_LOGGING_ACTIVE

View File

@ -29,6 +29,7 @@
// Libraries // Libraries
#include <cassert> #include <cassert>
#include "configuration.h" #include "configuration.h"
#include "engine/Entity.h"
/// Namespace reactphysics3d /// Namespace reactphysics3d
namespace reactphysics3d { namespace reactphysics3d {
@ -50,8 +51,12 @@ class Body {
// -------------------- Attributes -------------------- // // -------------------- Attributes -------------------- //
/// ID of the body /// ID of the body
// TODO : Remove this
bodyindex mID; bodyindex mID;
/// Identifier of the entity in the ECS
Entity mEntity;
/// True if the body has already been added in an island (for sleeping technique) /// True if the body has already been added in an island (for sleeping technique)
bool mIsAlreadyInIsland; bool mIsAlreadyInIsland;
@ -88,7 +93,7 @@ class Body {
// -------------------- Methods -------------------- // // -------------------- Methods -------------------- //
/// Constructor /// Constructor
Body(bodyindex id); Body(Entity entity, bodyindex id);
/// Deleted copy-constructor /// Deleted copy-constructor
Body(const Body& body) = delete; Body(const Body& body) = delete;
@ -102,6 +107,9 @@ class Body {
/// Return the ID of the body /// Return the ID of the body
bodyindex getId() const; bodyindex getId() const;
/// Return the corresponding entity of the body
Entity getEntity() const;
/// Return whether or not the body is allowed to sleep /// Return whether or not the body is allowed to sleep
bool isAllowedToSleep() const; bool isAllowedToSleep() const;
@ -157,6 +165,14 @@ inline bodyindex Body::getId() const {
return mID; return mID;
} }
// Return the corresponding entity of the body
/**
* @return The entity of the body
*/
inline Entity Body::getEntity() const {
return mEntity;
}
// Return whether or not the body is allowed to sleep // Return whether or not the body is allowed to sleep
/** /**
* @return True if the body is allowed to sleep and false otherwise * @return True if the body is allowed to sleep and false otherwise

View File

@ -39,8 +39,8 @@ using namespace reactphysics3d;
* @param world The physics world where the body is created * @param world The physics world where the body is created
* @param id ID of the body * @param id ID of the body
*/ */
CollisionBody::CollisionBody(const Transform& transform, CollisionWorld& world, bodyindex id) CollisionBody::CollisionBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id)
: Body(id), mType(BodyType::DYNAMIC), mTransform(transform), mProxyCollisionShapes(nullptr), : Body(entity, id), mType(BodyType::DYNAMIC), mTransform(transform), mProxyCollisionShapes(nullptr),
mNbCollisionShapes(0), mContactManifoldsList(nullptr), mWorld(world) { mNbCollisionShapes(0), mContactManifoldsList(nullptr), mWorld(world) {
#ifdef IS_PROFILING_ACTIVE #ifdef IS_PROFILING_ACTIVE

View File

@ -118,7 +118,7 @@ class CollisionBody : public Body {
// -------------------- Methods -------------------- // // -------------------- Methods -------------------- //
/// Constructor /// Constructor
CollisionBody(const Transform& transform, CollisionWorld& world, bodyindex id); CollisionBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id);
/// Destructor /// Destructor
virtual ~CollisionBody() override; virtual ~CollisionBody() override;

View File

@ -39,8 +39,8 @@ using namespace reactphysics3d;
* @param world The world where the body has been added * @param world The world where the body has been added
* @param id The ID of the body * @param id The ID of the body
*/ */
RigidBody::RigidBody(const Transform& transform, CollisionWorld& world, bodyindex id) RigidBody::RigidBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id)
: CollisionBody(transform, world, id), mArrayIndex(0), mInitMass(decimal(1.0)), : CollisionBody(transform, world, entity, id), mArrayIndex(0), mInitMass(decimal(1.0)),
mCenterOfMassLocal(0, 0, 0), mCenterOfMassWorld(transform.getPosition()), mCenterOfMassLocal(0, 0, 0), mCenterOfMassWorld(transform.getPosition()),
mIsGravityEnabled(true), mMaterial(world.mConfig), mLinearDamping(decimal(0.0)), mAngularDamping(decimal(0.0)), mIsGravityEnabled(true), mMaterial(world.mConfig), mLinearDamping(decimal(0.0)), mAngularDamping(decimal(0.0)),
mJointsList(nullptr), mIsCenterOfMassSetByUser(false), mIsInertiaTensorSetByUser(false) { mJointsList(nullptr), mIsCenterOfMassSetByUser(false), mIsInertiaTensorSetByUser(false) {

View File

@ -134,7 +134,7 @@ class RigidBody : public CollisionBody {
// -------------------- Methods -------------------- // // -------------------- Methods -------------------- //
/// Constructor /// Constructor
RigidBody(const Transform& transform, CollisionWorld& world, bodyindex id); RigidBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id);
/// Destructor /// Destructor
virtual ~RigidBody() override; virtual ~RigidBody() override;

View File

@ -0,0 +1,190 @@
/********************************************************************************
* 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 "TransformComponents.h"
#include "engine/EntityManager.h"
#include <cassert>
#include <random>
// We want to use the ReactPhysics3D namespace
using namespace reactphysics3d;
// Constructor
TransformComponents::TransformComponents(MemoryAllocator& allocator)
:mMemoryAllocator(allocator), mNbComponents(0), mNbAllocatedComponents(0),
mBuffer(nullptr), mMapEntityToComponentIndex(allocator) {
// Allocate memory for the components data
allocate(INIT_ALLOCATED_COMPONENTS);
}
// Destructor
TransformComponents::~TransformComponents() {
if (mNbAllocatedComponents > 0) {
// Destroy all the remaining components
for (uint32 i = 0; i < mNbComponents; i++) {
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 TransformComponents::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);
Transform* newTransforms = reinterpret_cast<Transform*>(newEntities + mNbAllocatedComponents);
// If there was already components before
if (mNbAllocatedComponents > 0) {
// Copy component data from the previous buffer to the new one
memcpy(newTransforms, mTransforms, mNbComponents * sizeof(Transform));
memcpy(newEntities, mEntities, mNbComponents * sizeof(Entity));
// Deallocate previous memory
mMemoryAllocator.release(mBuffer, mNbAllocatedComponents * COMPONENT_DATA_SIZE);
}
mBuffer = newBuffer;
mEntities = newEntities;
mTransforms = newTransforms;
mNbAllocatedComponents = nbComponentsToAllocate;
}
// Add a component
void TransformComponents::addComponent(Entity entity, const TransformComponent& component) {
// If we need to allocate more components
if (mNbComponents == mNbAllocatedComponents) {
allocate(mNbAllocatedComponents * 2);
}
// Map the entity with the new component lookup index
mMapEntityToComponentIndex.add(Pair<Entity, uint32>(entity, mNbComponents));
// Insert the new component data
new (mEntities + mNbComponents) Entity(entity);
new (mTransforms + mNbComponents) Transform(component.transform);
mNbComponents++;
}
// Perform garbage collection to remove unused components
void TransformComponents::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 TransformComponents::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
const uint32 lastIndex = mNbComponents - 1;
Entity entity = mEntities[index];
Entity lastEntity = mEntities[lastIndex];
if (mNbComponents > 1 && index != lastIndex) {
// Replace the data of the component to destroy by the data of the last component
mEntities[index] = mEntities[lastIndex];
mTransforms[index] = mTransforms[lastIndex];
// Update the entity to component index mapping
mMapEntityToComponentIndex[lastEntity] = index;
}
else {
// Call the destructors on the component values
destroyComponent(index);
}
// Update the entity to component index mapping
mMapEntityToComponentIndex.remove(entity);
mNbComponents--;
}
// Destroy a component at a given index
void TransformComponents::destroyComponent(uint32 index) {
mEntities[index].~Entity();
mTransforms[index].~Transform();
}

View File

@ -0,0 +1,140 @@
/********************************************************************************
* 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_TRANSFORM_COMPONENTS_H
#define REACTPHYSICS3D_TRANSFORM_COMPONENTS_H
// Libraries
#include "mathematics/Transform.h"
#include "engine/Entity.h"
#include "containers/Map.h"
// ReactPhysics3D namespace
namespace reactphysics3d {
// Class declarations
class MemoryAllocator;
class EntityManager;
// Class TransformComponents
/**
* This class represent the component of the ECS that contains the transforms of the
* different entities. The position and orientation of the bodies are stored there.
*/
class TransformComponents {
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(Transform);
// -------------------- 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;
/// Array of entities of each component
Entity* mEntities;
/// Array of transform of each component
Transform* mTransforms;
// -------------------- Methods -------------------- //
/// Remove a component at a given index
void removeComponent(uint32 index);
/// Destroy a component at a given index
void destroyComponent(uint32 index);
public:
/// Structure for the data of a transform component
struct TransformComponent {
const Transform& transform;
/// Constructor
TransformComponent(const Transform& transform) : transform(transform) {
}
};
// -------------------- Methods -------------------- //
/// Constructor
TransformComponents(MemoryAllocator& allocator);
/// Destructor
~TransformComponents();
/// Allocate memory for a given number of components
void allocate(uint32 nbComponentsToAllocate);
/// Add a component
void addComponent(Entity entity, const TransformComponent& component);
/// Perform garbage collection to remove unused components
void garbageCollection(const EntityManager& entityManager);
/// Return the transform of an entity
Transform& getTransform(Entity entity) const;
/// Set the transform of an entity
void setTransform(Entity entity, const Transform& transform);
};
// Return the transform of an entity
inline Transform& TransformComponents::getTransform(Entity entity) const {
return mTransforms[mMapEntityToComponentIndex[entity]];
}
// Set the transform of an entity
inline void TransformComponents::setTransform(Entity entity, const Transform& transform) {
mTransforms[mMapEntityToComponentIndex[entity]] = transform;
}
}
#endif

View File

@ -53,8 +53,6 @@ using uint = unsigned int;
using uchar = unsigned char; using uchar = unsigned char;
using ushort = unsigned short; using ushort = unsigned short;
using luint = long unsigned int; using luint = long unsigned int;
using bodyindex = luint;
using bodyindexpair = Pair<bodyindex, bodyindex>;
using int8 = std::int8_t; using int8 = std::int8_t;
using uint8 = std::uint8_t; using uint8 = std::uint8_t;
@ -63,6 +61,10 @@ using uint16 = std::uint16_t;
using int32 = std::int32_t; using int32 = std::int32_t;
using uint32 = std::uint32_t; using uint32 = std::uint32_t;
// TODO : Delete this
using bodyindex = luint;
using bodyindexpair = Pair<bodyindex, bodyindex>;
// ------------------- Enumerations ------------------- // // ------------------- Enumerations ------------------- //
/// Position correction technique used in the constraint solver (for joints). /// Position correction technique used in the constraint solver (for joints).

View File

@ -37,7 +37,9 @@ uint CollisionWorld::mNbWorlds = 0;
// Constructor // Constructor
CollisionWorld::CollisionWorld(const WorldSettings& worldSettings, Logger* logger, Profiler* profiler) CollisionWorld::CollisionWorld(const WorldSettings& worldSettings, Logger* logger, Profiler* profiler)
: mConfig(worldSettings), mCollisionDetection(this, mMemoryManager), mBodies(mMemoryManager.getPoolAllocator()), mCurrentBodyId(0), : mConfig(worldSettings), mEntityManager(mMemoryManager.getPoolAllocator()),
mTransformComponents(mMemoryManager.getBaseAllocator()),
mCollisionDetection(this, mMemoryManager), mBodies(mMemoryManager.getPoolAllocator()), mCurrentBodyId(0),
mFreeBodiesIds(mMemoryManager.getPoolAllocator()), mEventListener(nullptr), mName(worldSettings.worldName), mFreeBodiesIds(mMemoryManager.getPoolAllocator()), mEventListener(nullptr), mName(worldSettings.worldName),
mIsProfilerCreatedByUser(profiler != nullptr), mIsProfilerCreatedByUser(profiler != nullptr),
mIsLoggerCreatedByUser(logger != nullptr) { mIsLoggerCreatedByUser(logger != nullptr) {
@ -138,16 +140,22 @@ CollisionWorld::~CollisionWorld() {
*/ */
CollisionBody* CollisionWorld::createCollisionBody(const Transform& transform) { CollisionBody* CollisionWorld::createCollisionBody(const Transform& transform) {
// Create a new entity for the body
Entity entity = mEntityManager.createEntity();
// Get the next available body ID // Get the next available body ID
bodyindex bodyID = computeNextAvailableBodyId(); bodyindex bodyID = computeNextAvailableBodyId();
// Largest index cannot be used (it is used for invalid index) // Largest index cannot be used (it is used for invalid index)
assert(bodyID < std::numeric_limits<reactphysics3d::bodyindex>::max()); assert(bodyID < std::numeric_limits<reactphysics3d::bodyindex>::max());
// Add a transform component
mTransformComponents.addComponent(entity, TransformComponents::TransformComponent(transform));
// Create the collision body // Create the collision body
CollisionBody* collisionBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, CollisionBody* collisionBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool,
sizeof(CollisionBody))) sizeof(CollisionBody)))
CollisionBody(transform, *this, bodyID); CollisionBody(transform, *this, entity, bodyID);
assert(collisionBody != nullptr); assert(collisionBody != nullptr);
@ -189,6 +197,9 @@ void CollisionWorld::destroyCollisionBody(CollisionBody* collisionBody) {
// Reset the contact manifold list of the body // Reset the contact manifold list of the body
collisionBody->resetContactManifoldsList(); collisionBody->resetContactManifoldsList();
// Destroy the corresponding entity
mEntityManager.destroyEntity(collisionBody->getEntity());
// Call the destructor of the collision body // Call the destructor of the collision body
collisionBody->~CollisionBody(); collisionBody->~CollisionBody();

View File

@ -32,6 +32,8 @@
#include "collision/CollisionDetection.h" #include "collision/CollisionDetection.h"
#include "constraint/Joint.h" #include "constraint/Joint.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "engine/EntityManager.h"
#include "components/TransformComponents.h"
/// Namespace reactphysics3d /// Namespace reactphysics3d
namespace reactphysics3d { namespace reactphysics3d {
@ -66,6 +68,12 @@ class CollisionWorld {
/// Configuration of the physics world /// Configuration of the physics world
WorldSettings mConfig; WorldSettings mConfig;
/// Entity Manager for the ECS
EntityManager mEntityManager;
/// Transform Components
TransformComponents mTransformComponents;
/// Reference to the collision detection /// Reference to the collision detection
CollisionDetection mCollisionDetection; CollisionDetection mCollisionDetection;
@ -190,6 +198,7 @@ class CollisionWorld {
friend class CollisionDetection; friend class CollisionDetection;
friend class CollisionBody; friend class CollisionBody;
friend class RigidBody; friend class RigidBody;
friend class ProxyShape;
friend class ConvexMeshShape; friend class ConvexMeshShape;
}; };

View File

@ -417,15 +417,21 @@ void DynamicsWorld::solvePositionCorrection() {
*/ */
RigidBody* DynamicsWorld::createRigidBody(const Transform& transform) { RigidBody* DynamicsWorld::createRigidBody(const Transform& transform) {
// Create a new entity for the body
Entity entity = mEntityManager.createEntity();
// Compute the body ID // Compute the body ID
bodyindex bodyID = computeNextAvailableBodyId(); bodyindex bodyID = computeNextAvailableBodyId();
// Largest index cannot be used (it is used for invalid index) // Largest index cannot be used (it is used for invalid index)
assert(bodyID < std::numeric_limits<reactphysics3d::bodyindex>::max()); assert(bodyID < std::numeric_limits<reactphysics3d::bodyindex>::max());
// Add a transform component
mTransformComponents.addComponent(entity, TransformComponents::TransformComponent(transform));
// Create the rigid body // Create the rigid body
RigidBody* rigidBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool, RigidBody* rigidBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool,
sizeof(RigidBody))) RigidBody(transform, *this, bodyID); sizeof(RigidBody))) RigidBody(transform, *this, entity, bodyID);
assert(rigidBody != nullptr); assert(rigidBody != nullptr);
// Add the rigid body to the physics world // Add the rigid body to the physics world
@ -471,6 +477,9 @@ void DynamicsWorld::destroyRigidBody(RigidBody* rigidBody) {
// Reset the contact manifold list of the body // Reset the contact manifold list of the body
rigidBody->resetContactManifoldsList(); rigidBody->resetContactManifoldsList();
// Destroy the corresponding entity
mEntityManager.destroyEntity(rigidBody->getEntity());
// Call the destructor of the rigid body // Call the destructor of the rigid body
rigidBody->~RigidBody(); rigidBody->~RigidBody();

60
src/engine/Entity.cpp Normal file
View File

@ -0,0 +1,60 @@
/********************************************************************************
* 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 "Entity.h"
#include <cassert>
using namespace reactphysics3d;
// Static members initialization
const uint32 Entity::ENTITY_INDEX_BITS = 24;
const uint32 Entity::ENTITY_INDEX_MASK = (1 << Entity::ENTITY_INDEX_BITS) - 1;
const uint32 Entity::ENTITY_GENERATION_BITS = 8;
const uint32 Entity::ENTITY_GENERATION_MASK = (1 << Entity::ENTITY_GENERATION_BITS) - 1;
const uint32 Entity::MINIMUM_FREE_INDICES = 1024;
// Constructor
Entity::Entity(uint32 index, uint32 generation)
:id((index & ENTITY_INDEX_MASK) | ((generation & ENTITY_GENERATION_MASK) << ENTITY_INDEX_BITS)) {
uint32 test1 = index & ENTITY_INDEX_MASK;
uint32 test2 = (generation & ENTITY_INDEX_MASK) << ENTITY_INDEX_BITS;
uint32 test3 = test1 | test2;
uint32 test = getIndex();
assert(getIndex() == index);
assert(getGeneration() == generation);
}
// Assignment operator
Entity& Entity::operator=(const Entity& entity) {
if (&entity != this) {
id = entity.id;
}
return *this;
}

144
src/engine/Entity.h Normal file
View File

@ -0,0 +1,144 @@
/********************************************************************************
* 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_ENTITY_H
#define REACTPHYSICS3D_ENTITY_H
// Libraries
#include "configuration.h"
/// Namespace reactphysics3d
namespace reactphysics3d {
// Structure Entity
/**
* This class is used to identify an entity in the Entity-Component-System.
* Entities are used for bodies in the physics engine. The id of an entity is
* a 32 bits integer that is separated in two parts. The index and the generation
* parts. The idea is that the index part directly gives us the index of the entity
* in a lookup array. However, we want to support deletion of the entities. That's why
* we have the generation part. This number is used to distinguish the entities created
* at the same index in the array. If it is the case, we will increase the generation number
* each time.
*
* We use 24 bits for the index. Therefore, we support 16 millions simultaneous entries.
* We use 8 bits for the generation. Therefore, we support 256 entries created at the same index.
* To prevent reaching the 256 entries too fast, we make sure that we do not reuse the same index
* slot too ofen. To do that, we put recycled indices in a queue and only reuse values from that
* queue when it contains at least MINIMUM_FREE_INDICES values. It means, an id will reappear until
* its index has run 256 laps through the queue. It means that we must create and destroy at least
* 256 * MINIMUM_FREE_INDICES entities until an id can reappear. This seems reasonably safe.
*
* This implementation is based on the following article:
* http://bitsquid.blogspot.com/2014/08/building-data-oriented-entity-system.html
*/
struct Entity {
private:
/// Number of bits reserved for the index
static const uint32 ENTITY_INDEX_BITS;
/// Mask for the index part of the id
static const uint32 ENTITY_INDEX_MASK;
/// Number of bits reserved for the generation number
static const uint32 ENTITY_GENERATION_BITS;
/// Mask for the generation part of the id
static const uint32 ENTITY_GENERATION_MASK;
/// Minimum of free indices in the queue before we reuse one from the queue
static const uint32 MINIMUM_FREE_INDICES;
public:
// -------------------- Attributes -------------------- //
/// Id of the entity
uint32 id;
// -------------------- Methods -------------------- //
/// Constructor
Entity(uint32 index, uint32 generation);
/// Return the lookup index of the entity in a array
uint32 getIndex() const;
/// Return the generation number of the entity
uint32 getGeneration() const;
/// Assignment operator
Entity& operator=(const Entity& entity);
/// Equality operator
bool operator==(const Entity& entity) const;
/// Inequality operator (it != end())
bool operator!=(const Entity& entity) const;
// -------------------- Friendship -------------------- //
friend class EntityManager;
};
// Return the lookup index of the entity in a array
inline uint32 Entity::getIndex() const {
return id & ENTITY_INDEX_MASK;
}
// Return the generation number of the entity
inline uint32 Entity::getGeneration() const {
return (id >> ENTITY_INDEX_BITS) & ENTITY_GENERATION_MASK;
}
// Equality operator
inline bool Entity::operator==(const Entity& entity) const {
return entity.id == id;
}
// Inequality operator (it != end())
inline bool Entity::operator!=(const Entity& entity) const {
return entity.id != id;
}
}
// Hash function for a reactphysics3d Entity
namespace std {
template <> struct hash<reactphysics3d::Entity> {
size_t operator()(const reactphysics3d::Entity& entity) const {
return entity.id;
}
};
}
#endif

View File

@ -0,0 +1,75 @@
/********************************************************************************
* 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 "EntityManager.h"
#include "Entity.h"
using namespace reactphysics3d;
// Constructor
EntityManager::EntityManager(MemoryAllocator& allocator)
:mGenerations(allocator), mFreeIndices(allocator) {
}
// Create a new entity
Entity EntityManager::createEntity() {
uint32 index;
// If there are already enough free indices to start using them
if (mFreeIndices.size() > Entity::MINIMUM_FREE_INDICES) {
// Recycle an index from the free indices
index = mFreeIndices.getFront();
mFreeIndices.popFront();
}
else {
// We start at generation 0
mGenerations.add(0);
// Create a new indice
index = static_cast<uint32>(mGenerations.size()) - 1;
assert(index < (1 << Entity::ENTITY_INDEX_BITS));
}
// Return a new entity
return Entity(index, mGenerations[index]);
}
// Destroy an entity
void EntityManager::destroyEntity(Entity entity) {
const uint32 index = entity.getIndex();
// Increment the generation of this index
mGenerations[index]++;
// Add the index into the deque of free indices
mFreeIndices.addBack(index);
}

View File

@ -0,0 +1,80 @@
/********************************************************************************
* 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_ENTITY_MANAGER_H
#define REACTPHYSICS3D_ENTITY_MANAGER_H
// Libraries
#include "configuration.h"
#include "containers/List.h"
#include "containers/Deque.h"
#include "engine/Entity.h"
/// Namespace reactphysics3d
namespace reactphysics3d {
// Class EntityManager
/**
* This class is responsible to manage the entities of the ECS.
*/
class EntityManager {
private:
// -------------------- Attributes -------------------- //
/// List storing the generations of the created entities
List<uint8> mGenerations;
/// Deque with the indices of destroyed entities that can be reused
Deque<uint32> mFreeIndices;
// -------------------- Methods -------------------- //
public:
// -------------------- Methods -------------------- //
/// Constructor
EntityManager(MemoryAllocator& allocator);
/// Create a new entity
Entity createEntity();
/// Destroy an entity
void destroyEntity(Entity entity);
/// Return true if the entity is still valid (not destroyed)
bool isValid(Entity entity) const;
};
// Return true if the entity is still valid (not destroyed)
inline bool EntityManager::isValid(Entity entity) const {
return mGenerations[entity.getIndex()] == entity.getGeneration();
}
}
#endif