Merge branch 'entitylist-refactor'

This commit is contained in:
Lynix
2017-05-11 20:35:09 +02:00
14 changed files with 344 additions and 215 deletions

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2017 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#include <NDK/EntityList.hpp>
#include <NDK/World.hpp>
namespace Ndk
{
const EntityHandle& EntityList::iterator::operator*() const
{
return m_list->GetWorld()->GetEntity(static_cast<EntityId>(m_nextEntityId));
}
}

View File

@@ -48,7 +48,15 @@ namespace Ndk
{
m_forceRenderQueueInvalidation = true; //< Hackfix until lights and particles are handled by culling list
m_cameras.Remove(entity);
for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
{
if (it->GetObject() == entity)
{
m_cameras.erase(it);
break;
}
}
m_directionalLights.Remove(entity);
m_drawables.Remove(entity);
m_lights.Remove(entity);
@@ -74,14 +82,23 @@ namespace Ndk
if (entity->HasComponent<CameraComponent>() && entity->HasComponent<NodeComponent>())
{
m_cameras.Insert(entity);
m_cameras.emplace_back(entity);
std::sort(m_cameras.begin(), m_cameras.end(), [](const EntityHandle& handle1, const EntityHandle& handle2)
{
return handle1->GetComponent<CameraComponent>().GetLayer() < handle2->GetComponent<CameraComponent>().GetLayer();
});
}
else
m_cameras.Remove(entity);
{
for (auto it = m_cameras.begin(); it != m_cameras.end(); ++it)
{
if (it->GetObject() == entity)
{
m_cameras.erase(it);
break;
}
}
}
if (entity->HasComponent<GraphicsComponent>() && entity->HasComponent<NodeComponent>())
{
@@ -181,7 +198,7 @@ namespace Ndk
GraphicsComponent& graphicsComponent = drawable->GetComponent<GraphicsComponent>();
graphicsComponent.EnsureBoundingVolumeUpdate();
}
bool forceInvalidation = false;
std::size_t visibilityHash = m_drawableCulling.Cull(camComponent.GetFrustum(), &forceInvalidation);

View File

@@ -60,29 +60,51 @@ namespace Ndk
const EntityHandle& World::CreateEntity()
{
EntityId id;
EntityBlock* entBlock;
if (!m_freeIdList.empty())
{
// We get an identifier
id = m_freeIdList.back();
m_freeIdList.pop_back();
entBlock = &m_entities[id];
entBlock->handle.Reset(&entBlock->entity); //< Reset handle (as it was reset when entity got destroyed)
m_entityBlocks[id] = entBlock;
}
else
{
// We allocate a new entity
id = static_cast<Ndk::EntityId>(m_entities.size());
id = static_cast<Ndk::EntityId>(m_entityBlocks.size());
// We can't use emplace_back due to the scope
m_entities.push_back(Entity(this, id));
if (m_entities.capacity() > m_entities.size())
{
NazaraAssert(m_waitingEntities.empty(), "There should be no waiting entities if space is available in main container");
m_entities.push_back(Entity(this, id)); //< We can't use emplace_back due to the scope
entBlock = &m_entities.back();
}
else
{
// Pushing to entities would reallocate vector and thus, invalidate EntityHandles (which we don't want until world update)
// To prevent this, allocate them into a separate vector and move them at update
// For now, we are counting on m_entities grow strategy to keep allocation frequency low
m_waitingEntities.emplace_back(std::make_unique<EntityBlock>(Entity(this, id)));
entBlock = m_waitingEntities.back().get();
}
if (id >= m_entityBlocks.size())
m_entityBlocks.resize(id + 1);
m_entityBlocks[id] = entBlock;
}
// We initialise the entity and we add it to the list of alive entities
Entity& entity = m_entities[id].entity;
entity.Create();
// We initialize the entity and we add it to the list of alive entities
entBlock->entity.Create();
m_aliveEntities.emplace_back(&entity);
m_entities[id].aliveIndex = m_aliveEntities.size() - 1;
m_aliveEntities.Insert(&entBlock->entity);
return m_aliveEntities.back();
return entBlock->handle;
}
/*!
@@ -96,8 +118,9 @@ namespace Ndk
// First, destruction of entities, then handles
// This is made to avoid that handle warn uselessly entities before their destruction
m_entities.clear();
m_entityBlocks.clear();
m_aliveEntities.clear();
m_aliveEntities.Clear();
m_dirtyEntities.Clear();
m_killedEntities.Clear();
}
@@ -120,7 +143,7 @@ namespace Ndk
return EntityHandle::InvalidHandle;
}
EntityHandle clone = CreateEntity();
const EntityHandle& clone = CreateEntity();
const Nz::Bitset<>& componentBits = original->GetComponentBits();
for (std::size_t i = componentBits.FindFirst(); i != componentBits.npos; i = componentBits.FindNext(i))
@@ -132,40 +155,6 @@ namespace Ndk
return GetEntity(clone->GetId());
}
/*!
* \brief Kills an entity
*
* \param Pointer to the entity
*
* \remark No change is done if entity is invalid
*/
void World::KillEntity(Entity* entity)
{
if (IsEntityValid(entity))
m_killedEntities.UnboundedSet(entity->GetId(), true);
}
/*!
* \brief Gets an entity
* \return A constant reference to the modified entity
*
* \param id Identifier of the entity
*
* \remark Produces a NazaraError if entity identifier is not valid
*/
const EntityHandle& World::GetEntity(EntityId id)
{
if (IsEntityIdValid(id))
return m_aliveEntities[m_entities[id].aliveIndex];
else
{
NazaraError("Invalid ID");
return EntityHandle::InvalidHandle;
}
}
/*!
* \brief Updates the world
*
@@ -177,45 +166,43 @@ namespace Ndk
if (!m_orderedSystemsUpdated)
ReorderSystems();
// Move waiting entities to entity list
if (!m_waitingEntities.empty())
{
constexpr std::size_t MinEntityCapacity = 10; //< We want to be able to grow maximum entity count by at least ten without going to the waiting list
m_entities.reserve(m_entities.size() + m_waitingEntities.size() + MinEntityCapacity);
for (auto& blockPtr : m_waitingEntities)
m_entities.push_back(std::move(*blockPtr));
m_waitingEntities.clear();
// Update entity blocks pointers
for (std::size_t i = 0; i < m_entities.size(); ++i)
m_entityBlocks[i] = &m_entities[i];
}
// Handle killed entities before last call
for (std::size_t i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i))
{
EntityBlock& block = m_entities[i];
Entity& entity = block.entity;
NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range");
NazaraAssert(entity.IsValid(), "Entity must be valid");
Entity* entity = &m_entityBlocks[i]->entity;
// Destruction of the entity (invalidation of handle by the same way)
entity.Destroy();
entity->Destroy();
// Send back the identifier of the entity to the free queue
m_freeIdList.push_back(entity.GetId());
// We take out the handle from the list of alive entities
// With the idiom swap and pop
NazaraAssert(block.aliveIndex < m_aliveEntities.size(), "Alive index out of range");
if (block.aliveIndex < m_aliveEntities.size() - 1) // If it's not the last handle
{
EntityHandle& lastHandle = m_aliveEntities.back();
EntityHandle& myHandle = m_aliveEntities[block.aliveIndex];
myHandle = std::move(lastHandle);
// We don't forget to update the index associated to the entity
m_entities[myHandle->GetId()].aliveIndex = block.aliveIndex;
}
m_aliveEntities.pop_back();
m_freeIdList.push_back(entity->GetId());
}
m_killedEntities.Reset();
// Handle of entities which need an update from the systems
for (std::size_t i = m_dirtyEntities.FindFirst(); i != m_dirtyEntities.npos; i = m_dirtyEntities.FindNext(i))
{
NazaraAssert(i < m_entities.size(), "Entity index out of range");
NazaraAssert(i < m_entityBlocks.size(), "Entity index out of range");
Entity* entity = &m_entities[i].entity;
Entity* entity = &m_entityBlocks[i]->entity;
// Check entity validity (as it could have been reported as dirty and killed during the same iteration)
if (!entity->IsValid())
@@ -242,7 +229,7 @@ namespace Ndk
}
else
{
// No, it shouldn't, remove it if it's part of the system
// No it shouldn't, remove it if it's part of the system
if (partOfSystem)
system->RemoveEntity(entity);
}