Start working on ECS
This commit is contained in:
parent
d905ff5c7f
commit
fa9b1817fe
|
@ -128,6 +128,8 @@ SET (REACTPHYSICS3D_HEADERS
|
|||
"src/constraint/HingeJoint.h"
|
||||
"src/constraint/Joint.h"
|
||||
"src/constraint/SliderJoint.h"
|
||||
"src/engine/Entity.h"
|
||||
"src/engine/EntityManager.h"
|
||||
"src/engine/CollisionWorld.h"
|
||||
"src/engine/ConstraintSolver.h"
|
||||
"src/engine/ContactSolver.h"
|
||||
|
@ -138,6 +140,7 @@ SET (REACTPHYSICS3D_HEADERS
|
|||
"src/engine/OverlappingPair.h"
|
||||
"src/engine/Timer.h"
|
||||
"src/engine/Timer.cpp"
|
||||
"src/components/TransformComponents.h"
|
||||
"src/collision/CollisionCallback.h"
|
||||
"src/collision/OverlapCallback.h"
|
||||
"src/mathematics/mathematics.h"
|
||||
|
@ -224,6 +227,9 @@ SET (REACTPHYSICS3D_SOURCES
|
|||
"src/engine/Material.cpp"
|
||||
"src/engine/OverlappingPair.cpp"
|
||||
"src/engine/Timer.cpp"
|
||||
"src/engine/Entity.cpp"
|
||||
"src/engine/EntityManager.cpp"
|
||||
"src/components/TransformComponents.cpp"
|
||||
"src/collision/CollisionCallback.cpp"
|
||||
"src/mathematics/mathematics_functions.cpp"
|
||||
"src/mathematics/Matrix2x2.cpp"
|
||||
|
|
|
@ -35,8 +35,8 @@ using namespace reactphysics3d;
|
|||
/**
|
||||
* @param id ID of the new body
|
||||
*/
|
||||
Body::Body(bodyindex id)
|
||||
: mID(id), mIsAlreadyInIsland(false), mIsAllowedToSleep(true), mIsActive(true),
|
||||
Body::Body(Entity entity, bodyindex id)
|
||||
: mID(id), mEntity(entity), mIsAlreadyInIsland(false), mIsAllowedToSleep(true), mIsActive(true),
|
||||
mIsSleeping(false), mSleepTime(0), mUserData(nullptr) {
|
||||
|
||||
#ifdef IS_LOGGING_ACTIVE
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
// Libraries
|
||||
#include <cassert>
|
||||
#include "configuration.h"
|
||||
#include "engine/Entity.h"
|
||||
|
||||
/// Namespace reactphysics3d
|
||||
namespace reactphysics3d {
|
||||
|
@ -50,8 +51,12 @@ class Body {
|
|||
// -------------------- Attributes -------------------- //
|
||||
|
||||
/// ID of the body
|
||||
// TODO : Remove this
|
||||
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)
|
||||
bool mIsAlreadyInIsland;
|
||||
|
||||
|
@ -88,7 +93,7 @@ class Body {
|
|||
// -------------------- Methods -------------------- //
|
||||
|
||||
/// Constructor
|
||||
Body(bodyindex id);
|
||||
Body(Entity entity, bodyindex id);
|
||||
|
||||
/// Deleted copy-constructor
|
||||
Body(const Body& body) = delete;
|
||||
|
@ -102,6 +107,9 @@ class Body {
|
|||
/// Return the ID of the body
|
||||
bodyindex getId() const;
|
||||
|
||||
/// Return the corresponding entity of the body
|
||||
Entity getEntity() const;
|
||||
|
||||
/// Return whether or not the body is allowed to sleep
|
||||
bool isAllowedToSleep() const;
|
||||
|
||||
|
@ -157,6 +165,14 @@ inline bodyindex Body::getId() const {
|
|||
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 True if the body is allowed to sleep and false otherwise
|
||||
|
|
|
@ -39,8 +39,8 @@ using namespace reactphysics3d;
|
|||
* @param world The physics world where the body is created
|
||||
* @param id ID of the body
|
||||
*/
|
||||
CollisionBody::CollisionBody(const Transform& transform, CollisionWorld& world, bodyindex id)
|
||||
: Body(id), mType(BodyType::DYNAMIC), mTransform(transform), mProxyCollisionShapes(nullptr),
|
||||
CollisionBody::CollisionBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id)
|
||||
: Body(entity, id), mType(BodyType::DYNAMIC), mTransform(transform), mProxyCollisionShapes(nullptr),
|
||||
mNbCollisionShapes(0), mContactManifoldsList(nullptr), mWorld(world) {
|
||||
|
||||
#ifdef IS_PROFILING_ACTIVE
|
||||
|
|
|
@ -118,7 +118,7 @@ class CollisionBody : public Body {
|
|||
// -------------------- Methods -------------------- //
|
||||
|
||||
/// Constructor
|
||||
CollisionBody(const Transform& transform, CollisionWorld& world, bodyindex id);
|
||||
CollisionBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id);
|
||||
|
||||
/// Destructor
|
||||
virtual ~CollisionBody() override;
|
||||
|
|
|
@ -39,8 +39,8 @@ using namespace reactphysics3d;
|
|||
* @param world The world where the body has been added
|
||||
* @param id The ID of the body
|
||||
*/
|
||||
RigidBody::RigidBody(const Transform& transform, CollisionWorld& world, bodyindex id)
|
||||
: CollisionBody(transform, world, id), mArrayIndex(0), mInitMass(decimal(1.0)),
|
||||
RigidBody::RigidBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id)
|
||||
: CollisionBody(transform, world, entity, id), mArrayIndex(0), mInitMass(decimal(1.0)),
|
||||
mCenterOfMassLocal(0, 0, 0), mCenterOfMassWorld(transform.getPosition()),
|
||||
mIsGravityEnabled(true), mMaterial(world.mConfig), mLinearDamping(decimal(0.0)), mAngularDamping(decimal(0.0)),
|
||||
mJointsList(nullptr), mIsCenterOfMassSetByUser(false), mIsInertiaTensorSetByUser(false) {
|
||||
|
|
|
@ -134,7 +134,7 @@ class RigidBody : public CollisionBody {
|
|||
// -------------------- Methods -------------------- //
|
||||
|
||||
/// Constructor
|
||||
RigidBody(const Transform& transform, CollisionWorld& world, bodyindex id);
|
||||
RigidBody(const Transform& transform, CollisionWorld& world, Entity entity, bodyindex id);
|
||||
|
||||
/// Destructor
|
||||
virtual ~RigidBody() override;
|
||||
|
|
190
src/components/TransformComponents.cpp
Normal file
190
src/components/TransformComponents.cpp
Normal 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();
|
||||
}
|
140
src/components/TransformComponents.h
Normal file
140
src/components/TransformComponents.h
Normal 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
|
|
@ -53,8 +53,6 @@ using uint = unsigned int;
|
|||
using uchar = unsigned char;
|
||||
using ushort = unsigned short;
|
||||
using luint = long unsigned int;
|
||||
using bodyindex = luint;
|
||||
using bodyindexpair = Pair<bodyindex, bodyindex>;
|
||||
|
||||
using int8 = std::int8_t;
|
||||
using uint8 = std::uint8_t;
|
||||
|
@ -63,6 +61,10 @@ using uint16 = std::uint16_t;
|
|||
using int32 = std::int32_t;
|
||||
using uint32 = std::uint32_t;
|
||||
|
||||
// TODO : Delete this
|
||||
using bodyindex = luint;
|
||||
using bodyindexpair = Pair<bodyindex, bodyindex>;
|
||||
|
||||
// ------------------- Enumerations ------------------- //
|
||||
|
||||
/// Position correction technique used in the constraint solver (for joints).
|
||||
|
|
|
@ -37,7 +37,9 @@ uint CollisionWorld::mNbWorlds = 0;
|
|||
|
||||
// Constructor
|
||||
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),
|
||||
mIsProfilerCreatedByUser(profiler != nullptr),
|
||||
mIsLoggerCreatedByUser(logger != nullptr) {
|
||||
|
@ -138,16 +140,22 @@ CollisionWorld::~CollisionWorld() {
|
|||
*/
|
||||
CollisionBody* CollisionWorld::createCollisionBody(const Transform& transform) {
|
||||
|
||||
// Create a new entity for the body
|
||||
Entity entity = mEntityManager.createEntity();
|
||||
|
||||
// Get the next available body ID
|
||||
bodyindex bodyID = computeNextAvailableBodyId();
|
||||
|
||||
// Largest index cannot be used (it is used for invalid index)
|
||||
assert(bodyID < std::numeric_limits<reactphysics3d::bodyindex>::max());
|
||||
|
||||
// Add a transform component
|
||||
mTransformComponents.addComponent(entity, TransformComponents::TransformComponent(transform));
|
||||
|
||||
// Create the collision body
|
||||
CollisionBody* collisionBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool,
|
||||
sizeof(CollisionBody)))
|
||||
CollisionBody(transform, *this, bodyID);
|
||||
CollisionBody(transform, *this, entity, bodyID);
|
||||
|
||||
assert(collisionBody != nullptr);
|
||||
|
||||
|
@ -189,6 +197,9 @@ void CollisionWorld::destroyCollisionBody(CollisionBody* collisionBody) {
|
|||
// Reset the contact manifold list of the body
|
||||
collisionBody->resetContactManifoldsList();
|
||||
|
||||
// Destroy the corresponding entity
|
||||
mEntityManager.destroyEntity(collisionBody->getEntity());
|
||||
|
||||
// Call the destructor of the collision body
|
||||
collisionBody->~CollisionBody();
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include "collision/CollisionDetection.h"
|
||||
#include "constraint/Joint.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include "engine/EntityManager.h"
|
||||
#include "components/TransformComponents.h"
|
||||
|
||||
/// Namespace reactphysics3d
|
||||
namespace reactphysics3d {
|
||||
|
@ -66,6 +68,12 @@ class CollisionWorld {
|
|||
/// Configuration of the physics world
|
||||
WorldSettings mConfig;
|
||||
|
||||
/// Entity Manager for the ECS
|
||||
EntityManager mEntityManager;
|
||||
|
||||
/// Transform Components
|
||||
TransformComponents mTransformComponents;
|
||||
|
||||
/// Reference to the collision detection
|
||||
CollisionDetection mCollisionDetection;
|
||||
|
||||
|
@ -190,6 +198,7 @@ class CollisionWorld {
|
|||
friend class CollisionDetection;
|
||||
friend class CollisionBody;
|
||||
friend class RigidBody;
|
||||
friend class ProxyShape;
|
||||
friend class ConvexMeshShape;
|
||||
};
|
||||
|
||||
|
|
|
@ -417,15 +417,21 @@ void DynamicsWorld::solvePositionCorrection() {
|
|||
*/
|
||||
RigidBody* DynamicsWorld::createRigidBody(const Transform& transform) {
|
||||
|
||||
// Create a new entity for the body
|
||||
Entity entity = mEntityManager.createEntity();
|
||||
|
||||
// Compute the body ID
|
||||
bodyindex bodyID = computeNextAvailableBodyId();
|
||||
|
||||
// Largest index cannot be used (it is used for invalid index)
|
||||
assert(bodyID < std::numeric_limits<reactphysics3d::bodyindex>::max());
|
||||
|
||||
// Add a transform component
|
||||
mTransformComponents.addComponent(entity, TransformComponents::TransformComponent(transform));
|
||||
|
||||
// Create the rigid body
|
||||
RigidBody* rigidBody = new (mMemoryManager.allocate(MemoryManager::AllocationType::Pool,
|
||||
sizeof(RigidBody))) RigidBody(transform, *this, bodyID);
|
||||
sizeof(RigidBody))) RigidBody(transform, *this, entity, bodyID);
|
||||
assert(rigidBody != nullptr);
|
||||
|
||||
// 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
|
||||
rigidBody->resetContactManifoldsList();
|
||||
|
||||
// Destroy the corresponding entity
|
||||
mEntityManager.destroyEntity(rigidBody->getEntity());
|
||||
|
||||
// Call the destructor of the rigid body
|
||||
rigidBody->~RigidBody();
|
||||
|
||||
|
|
60
src/engine/Entity.cpp
Normal file
60
src/engine/Entity.cpp
Normal 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
144
src/engine/Entity.h
Normal 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
|
75
src/engine/EntityManager.cpp
Normal file
75
src/engine/EntityManager.cpp
Normal 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);
|
||||
}
|
80
src/engine/EntityManager.h
Normal file
80
src/engine/EntityManager.h
Normal 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
|
Loading…
Reference in New Issue
Block a user