ECS library wip
This commit is contained in:
parent
190fd5e51e
commit
63008d62ff
12 changed files with 465 additions and 217 deletions
51
libs/ecs/include/psemek/ecs/detail/apply_helper.hpp
Normal file
51
libs/ecs/include/psemek/ecs/detail/apply_helper.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/detail/table.hpp>
|
||||
#include <psemek/ecs/detail/entity_list.hpp>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
template <typename ... Components>
|
||||
struct static_apply_helper
|
||||
{
|
||||
std::size_t row_count;
|
||||
entity_id const * entity_id_pointer;
|
||||
entity_data const * entity_data_pointer;
|
||||
table::component_pointer pointers[sizeof...(Components)];
|
||||
|
||||
static_apply_helper(util::span<entity_id const> entity_ids, util::span<entity_data const> entities)
|
||||
: row_count(entity_ids.size())
|
||||
, entity_id_pointer(entity_ids.data())
|
||||
, entity_data_pointer(entities.data())
|
||||
{}
|
||||
|
||||
template <typename Function>
|
||||
void apply(Function && function)
|
||||
{
|
||||
apply_impl(function, std::make_index_sequence<sizeof...(Components)>{});
|
||||
}
|
||||
|
||||
template <typename Function, std::size_t ... I>
|
||||
void apply_impl(Function && function, std::index_sequence<I...>)
|
||||
{
|
||||
auto id = *entity_id_pointer;
|
||||
auto epoch = entity_data_pointer[id].epoch;
|
||||
function(entity_handle{id, epoch}, *reinterpret_cast<Components *>(pointers[I].data) ...);
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return row_count;
|
||||
}
|
||||
|
||||
void advance()
|
||||
{
|
||||
++entity_id_pointer;
|
||||
|
||||
std::size_t i = 0;
|
||||
((pointers[i++].data += stride<Components>()), ...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/component_mask.hpp>
|
||||
#include <psemek/ecs/detail/component_mask.hpp>
|
||||
#include <psemek/util/uuid.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
#include <psemek/util/unique_sequential_storage.hpp>
|
||||
|
||||
namespace psemek::ecs
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
struct component_index
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <psemek/util/dynamic_bitset.hpp>
|
||||
|
||||
namespace psemek::ecs
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
using component_mask = util::dynamic_bitset;
|
||||
42
libs/ecs/include/psemek/ecs/detail/entity_list.hpp
Normal file
42
libs/ecs/include/psemek/ecs/detail/entity_list.hpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/entity_handle.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
struct table;
|
||||
|
||||
struct entity_data
|
||||
{
|
||||
struct table * table = nullptr;
|
||||
std::uint32_t row = 0;
|
||||
entity_epoch epoch = 0;
|
||||
};
|
||||
|
||||
struct entity_list
|
||||
{
|
||||
entity_id create(table * table, std::uint32_t row);
|
||||
void destroy(entity_id id);
|
||||
|
||||
util::span<entity_data const> get_entities() const
|
||||
{
|
||||
return entities_;
|
||||
}
|
||||
|
||||
util::span<entity_data> get_entities()
|
||||
{
|
||||
return entities_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<entity_data> entities_;
|
||||
std::vector<entity_id> free_ids_;
|
||||
|
||||
void allocate_ids();
|
||||
};
|
||||
|
||||
}
|
||||
17
libs/ecs/include/psemek/ecs/detail/stride.hpp
Normal file
17
libs/ecs/include/psemek/ecs/detail/stride.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_empty_v = std::is_empty_v<T>;
|
||||
|
||||
template <typename T>
|
||||
constexpr std::size_t stride()
|
||||
{
|
||||
return is_empty_v<T> ? 0 : sizeof(T);
|
||||
}
|
||||
|
||||
}
|
||||
203
libs/ecs/include/psemek/ecs/detail/table.hpp
Normal file
203
libs/ecs/include/psemek/ecs/detail/table.hpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/entity_handle.hpp>
|
||||
#include <psemek/ecs/detail/stride.hpp>
|
||||
#include <psemek/util/uuid.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
#include <psemek/util/assert.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
struct table
|
||||
{
|
||||
struct component_pointer
|
||||
{
|
||||
std::uint8_t * data = nullptr;
|
||||
};
|
||||
|
||||
util::span<util::uuid const> get_component_uuids() const
|
||||
{
|
||||
return component_uuids_;
|
||||
}
|
||||
|
||||
util::span<component_pointer const> get_component_pointers() const
|
||||
{
|
||||
return component_pointers_;
|
||||
}
|
||||
|
||||
std::size_t component_count() const
|
||||
{
|
||||
return component_uuids_.size();
|
||||
}
|
||||
|
||||
std::size_t row_count() const
|
||||
{
|
||||
return row_count_;
|
||||
}
|
||||
|
||||
virtual std::size_t push_row(entity_id id) = 0;
|
||||
virtual void swap_rows(std::size_t row1, std::size_t row2) = 0;
|
||||
virtual void pop_row() = 0;
|
||||
|
||||
util::span<entity_id const> get_entity_ids() const
|
||||
{
|
||||
return entity_ids_;
|
||||
}
|
||||
|
||||
virtual ~table() = default;
|
||||
|
||||
protected:
|
||||
std::vector<util::uuid> component_uuids_;
|
||||
std::vector<component_pointer> component_pointers_;
|
||||
std::size_t row_count_ = 0;
|
||||
|
||||
std::vector<entity_id> entity_ids_;
|
||||
};
|
||||
|
||||
template <typename ... Components>
|
||||
struct table_impl
|
||||
: table
|
||||
{
|
||||
table_impl(util::span<util::uuid const> component_uuids);
|
||||
|
||||
std::size_t push_row(entity_id id) override;
|
||||
std::size_t push_row_with_components(entity_id id, Components && ... components);
|
||||
void swap_rows(std::size_t row1, std::size_t row2) override;
|
||||
void pop_row() override;
|
||||
|
||||
private:
|
||||
std::size_t capacity_ = 0;
|
||||
|
||||
void reallocate();
|
||||
};
|
||||
|
||||
template <typename ... Components>
|
||||
table_impl<Components...>::table_impl(util::span<util::uuid const> component_uuids)
|
||||
{
|
||||
assert(sizeof...(Components) == component_uuids.size());
|
||||
component_uuids_.assign(component_uuids.begin(), component_uuids.end());
|
||||
component_pointers_.resize(sizeof...(Components));
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::size_t table_impl<Components...>::push_row(entity_id id)
|
||||
{
|
||||
if (row_count_ == capacity_)
|
||||
reallocate();
|
||||
|
||||
[[maybe_unused]] auto push_row_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
new (reinterpret_cast<Component *>(data) + row_count_) Component{};
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(push_row_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
++row_count_;
|
||||
|
||||
entity_ids_.push_back(id);
|
||||
|
||||
return row_count_ - 1;
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::size_t table_impl<Components...>::push_row_with_components(entity_id id, Components && ... components)
|
||||
{
|
||||
if (row_count_ == capacity_)
|
||||
reallocate();
|
||||
|
||||
[[maybe_unused]] auto push_row_impl = [&]<typename Component>(std::uint8_t * data, Component && value)
|
||||
{
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
new (reinterpret_cast<Component *>(data) + row_count_) Component{std::move(value)};
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(push_row_impl.template operator()<Components>(component_pointers_[i++].data, std::move(components)), ...);
|
||||
++row_count_;
|
||||
|
||||
entity_ids_.push_back(id);
|
||||
|
||||
return row_count_ - 1;
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::swap_rows(std::size_t row1, std::size_t row2)
|
||||
{
|
||||
[[maybe_unused]] auto swap_rows_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
auto cdata = reinterpret_cast<Component *>(data);
|
||||
std::iter_swap(cdata + row1, cdata + row2);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(swap_rows_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
|
||||
std::swap(entity_ids_[row1], entity_ids_[row2]);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::pop_row()
|
||||
{
|
||||
--row_count_;
|
||||
[[maybe_unused]] auto pop_row_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!detail::is_empty_v<Component>)
|
||||
{
|
||||
(reinterpret_cast<Component *>(data) + row_count_)->~Component();
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(pop_row_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
entity_ids_.pop_back();
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::reallocate()
|
||||
{
|
||||
std::size_t const new_capacity = capacity_ == 0 ? 64 : capacity_ * 2;
|
||||
|
||||
[[maybe_unused]] auto reallocate_impl = [&]<typename Component>(std::uint8_t * & data)
|
||||
{
|
||||
if constexpr (detail::is_empty_v<Component>)
|
||||
{
|
||||
if (!data)
|
||||
data = reinterpret_cast<std::uint8_t *>(new Component[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto new_data = new (std::align_val_t(alignof(Component))) std::uint8_t[new_capacity * sizeof(Component)];
|
||||
|
||||
auto old_begin = reinterpret_cast<Component *>(data);
|
||||
auto old_end = old_begin + row_count_;
|
||||
auto new_begin = reinterpret_cast<Component *>(new_data);
|
||||
for (; old_begin != old_end; ++old_begin, ++new_begin)
|
||||
{
|
||||
new (new_begin) Component{std::move(*old_begin)};
|
||||
old_begin->~Component();
|
||||
}
|
||||
|
||||
delete [] data;
|
||||
data = new_data;
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(reallocate_impl.template operator()<Components>(component_pointers_[i++].data), ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/component_mask.hpp>
|
||||
#include <psemek/ecs/table.hpp>
|
||||
#include <psemek/ecs/detail/component_mask.hpp>
|
||||
#include <psemek/ecs/detail/table.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::ecs
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
// TODO: store tables in a bitmask trie balanced by subtree size
|
||||
|
|
@ -13,7 +13,7 @@ namespace psemek::ecs
|
|||
struct table_container
|
||||
{
|
||||
template <typename ... Components>
|
||||
table_impl<Components...> & insert(component_mask const & mask);
|
||||
table_impl<Components...> & insert(component_mask const & mask, util::span<util::uuid const> component_uuids);
|
||||
|
||||
template <typename Function>
|
||||
void apply(Function && function, component_mask const & mask);
|
||||
|
|
@ -23,11 +23,11 @@ namespace psemek::ecs
|
|||
};
|
||||
|
||||
template <typename ... Components>
|
||||
table_impl<Components...> & table_container::insert(component_mask const & mask)
|
||||
table_impl<Components...> & table_container::insert(component_mask const & mask, util::span<util::uuid const> component_uuids)
|
||||
{
|
||||
auto & result = tables_[mask];
|
||||
if (!result)
|
||||
result = std::make_unique<table_impl<Components...>>();
|
||||
result = std::make_unique<table_impl<Components...>>(component_uuids);
|
||||
return *static_cast<table_impl<Components...> *>(result.get());
|
||||
}
|
||||
|
||||
16
libs/ecs/include/psemek/ecs/entity_accessor.hpp
Normal file
16
libs/ecs/include/psemek/ecs/entity_accessor.hpp
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/detail/table.hpp>
|
||||
|
||||
namespace psemek::ecs
|
||||
{
|
||||
|
||||
struct entity_accessor
|
||||
{
|
||||
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/component_index.hpp>
|
||||
#include <psemek/ecs/component_mask.hpp>
|
||||
#include <psemek/ecs/entity_handle.hpp>
|
||||
#include <psemek/ecs/table_container.hpp>
|
||||
#include <psemek/ecs/detail/component_index.hpp>
|
||||
#include <psemek/ecs/detail/entity_list.hpp>
|
||||
#include <psemek/ecs/detail/table_container.hpp>
|
||||
#include <psemek/ecs/detail/apply_helper.hpp>
|
||||
#include <psemek/ecs/entity_accessor.hpp>
|
||||
#include <psemek/util/span.hpp>
|
||||
|
||||
namespace psemek::ecs
|
||||
|
|
@ -11,43 +12,99 @@ namespace psemek::ecs
|
|||
|
||||
struct entity_container
|
||||
{
|
||||
entity_handle create()
|
||||
{
|
||||
// Specialization for an empty entity to
|
||||
// prevent creation of an empty uuid array
|
||||
|
||||
detail::component_mask mask;
|
||||
|
||||
auto & table = table_container_.insert<>(mask, {});
|
||||
|
||||
auto id = entity_list_.create(&table, table.row_count());
|
||||
table.push_row(id);
|
||||
|
||||
return {id, 0};
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
entity_handle create(Components && ... components)
|
||||
{
|
||||
component_mask mask = component_index_.make_component_mask(components.uuid()...);
|
||||
// TODO
|
||||
return {};
|
||||
util::uuid component_uuids[] { components.uuid()... };
|
||||
detail::component_mask mask = component_index_.make_component_mask(util::span<util::uuid const>(component_uuids));
|
||||
|
||||
auto & table = table_container_.insert<Components...>(mask, component_uuids);
|
||||
|
||||
auto id = entity_list_.create(&table, table.row_count());
|
||||
table.push_row_with_components(id, std::move(components)...);
|
||||
|
||||
return {id, entity_list_.get_entities()[id].epoch};
|
||||
}
|
||||
|
||||
bool alive(entity_handle const & entity) const;
|
||||
|
||||
void destroy(entity_handle const & entity);
|
||||
|
||||
template <typename Function>
|
||||
void apply(Function && function, util::span<util::uuid const> component_uuids)
|
||||
bool alive(entity_handle const & entity) const
|
||||
{
|
||||
component_mask mask = component_index_.make_component_mask(component_uuids);
|
||||
table_container_.apply([&](table & table){
|
||||
// TODO: extract specific component pointers and apply the function
|
||||
// to each element, using stride to advance pointers
|
||||
// TODO: maybe store UUIDS or component indices in the table to simplify
|
||||
// extracting the component pointers
|
||||
}, mask);
|
||||
return entity_list_.get_entities()[entity.id].epoch == entity.epoch;
|
||||
}
|
||||
|
||||
void destroy(entity_handle const & entity)
|
||||
{
|
||||
// Swap with the last row in that table
|
||||
auto entities = entity_list_.get_entities();
|
||||
auto & data = entities[entity.id];
|
||||
auto table_entity_ids = data.table->get_entity_ids();
|
||||
data.table->swap_rows(data.row, table_entity_ids.size() - 1);
|
||||
data.table->pop_row();
|
||||
auto swap_id = table_entity_ids[data.row];
|
||||
auto & swap_data = entities[swap_id];
|
||||
swap_data.row = data.row;
|
||||
entity_list_.destroy(entity.id);
|
||||
}
|
||||
|
||||
template <typename ... Components, typename Function>
|
||||
void apply(Function && function)
|
||||
{
|
||||
util::uuid component_uuids[] { Components::uuid() ... };
|
||||
// TODO: call function, casting the raw uint8_t component pointers
|
||||
// to actual component types
|
||||
util::uuid const component_uuids[] { Components::uuid() ... };
|
||||
|
||||
detail::component_mask mask = component_index_.make_component_mask(util::span<util::uuid const>(component_uuids));
|
||||
|
||||
table_container_.apply([&](detail::table & table){
|
||||
detail::static_apply_helper<Components...> apply_helper(table.get_entity_ids(), entity_list_.get_entities());
|
||||
|
||||
for (std::size_t i = 0; i < sizeof...(Components); ++i)
|
||||
for (std::size_t j = 0; j < table.component_count(); ++j)
|
||||
if (component_uuids[i] == table.get_component_uuids()[j])
|
||||
apply_helper.pointers[i++] = table.get_component_pointers()[j];
|
||||
|
||||
for (std::size_t i = 0; i < apply_helper.size(); ++i)
|
||||
{
|
||||
apply_helper.apply(function);
|
||||
apply_helper.advance();
|
||||
}
|
||||
|
||||
}, mask);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
Component & get(entity_handle const & entity)
|
||||
{
|
||||
util::uuid const uuid = Component::uuid();
|
||||
|
||||
auto const & data = entity_list_.get_entities()[entity.id];
|
||||
|
||||
auto const component_uuids = data.table->get_component_uuids();
|
||||
|
||||
for (std::size_t i = 0; i < component_uuids.size(); ++i)
|
||||
if (uuid == component_uuids[i])
|
||||
return *reinterpret_cast<Component *>(data.table->get_component_pointers()[i].data + detail::stride<Component>() * data.row);
|
||||
|
||||
assert(false);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable component_index component_index_;
|
||||
table_container table_container_;
|
||||
// TODO: store entity epochs
|
||||
// TODO: store entity id -> table * mapping
|
||||
detail::entity_list entity_list_;
|
||||
mutable detail::component_index component_index_;
|
||||
detail::table_container table_container_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
namespace psemek::ecs
|
||||
{
|
||||
|
|
@ -14,4 +15,10 @@ namespace psemek::ecs
|
|||
entity_epoch epoch;
|
||||
};
|
||||
|
||||
inline std::ostream & operator << (std::ostream & out, entity_handle const & handle)
|
||||
{
|
||||
out << '(' << handle.id << ',' << handle.epoch << ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,181 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/ecs/entity_handle.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace psemek::ecs
|
||||
{
|
||||
|
||||
struct table
|
||||
{
|
||||
struct component_pointer
|
||||
{
|
||||
std::uint8_t * data = nullptr;
|
||||
std::size_t stride = 0;
|
||||
};
|
||||
|
||||
component_pointer get_component(std::size_t i)
|
||||
{
|
||||
return component_pointers_[i];
|
||||
}
|
||||
|
||||
std::size_t component_count() const
|
||||
{
|
||||
return component_count_;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return size_;
|
||||
}
|
||||
|
||||
virtual std::size_t push_row(entity_id id) = 0;
|
||||
virtual void swap_rows(std::size_t row1, std::size_t row2) = 0;
|
||||
virtual void pop_row() = 0;
|
||||
|
||||
entity_id row_entity_id(std::size_t row) const
|
||||
{
|
||||
return entity_ids_[row];
|
||||
}
|
||||
|
||||
virtual ~table() = default;
|
||||
|
||||
protected:
|
||||
component_pointer const * component_pointers_ = nullptr;
|
||||
std::size_t component_count_ = 0;
|
||||
std::size_t size_ = 0;
|
||||
|
||||
std::vector<entity_id> entity_ids_;
|
||||
};
|
||||
|
||||
template <typename ... Components>
|
||||
struct table_impl
|
||||
: table
|
||||
{
|
||||
table_impl();
|
||||
|
||||
std::size_t push_row(entity_id id) override;
|
||||
void swap_rows(std::size_t row1, std::size_t row2) override;
|
||||
void pop_row() override;
|
||||
|
||||
private:
|
||||
component_pointer component_pointers_storage_[sizeof...(Components)];
|
||||
std::size_t capacity_ = 0;
|
||||
|
||||
void init_components_stride();
|
||||
void reallocate();
|
||||
};
|
||||
|
||||
template <typename ... Components>
|
||||
table_impl<Components...>::table_impl()
|
||||
{
|
||||
component_pointers_ = component_pointers_storage_;
|
||||
component_count_ = sizeof...(Components);
|
||||
init_components_stride();
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
std::size_t table_impl<Components...>::push_row(entity_id id)
|
||||
{
|
||||
if (size_ == capacity_)
|
||||
reallocate();
|
||||
|
||||
auto push_row_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!std::is_empty_v<Component>)
|
||||
{
|
||||
new (reinterpret_cast<Component *>(data) + size_) Component{};
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(push_row_impl.template operator()<Components>(component_pointers_storage_[i++].data), ...);
|
||||
++size_;
|
||||
|
||||
entity_ids_.push_back(id);
|
||||
|
||||
return size_ - 1;
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::swap_rows(std::size_t row1, std::size_t row2)
|
||||
{
|
||||
auto swap_rows_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!std::is_empty_v<Component>)
|
||||
{
|
||||
auto cdata = reinterpret_cast<Component *>(data);
|
||||
std::iter_swap(cdata + row1, cdata + row2);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(swap_rows_impl.template operator()<Components>(component_pointers_storage_[i++].data), ...);
|
||||
|
||||
std::swap(entity_ids_[row1], entity_ids_[row2]);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::pop_row()
|
||||
{
|
||||
auto row = size_ - 1;
|
||||
|
||||
auto pop_row_impl = [&]<typename Component>(std::uint8_t * data)
|
||||
{
|
||||
if constexpr (!std::is_empty_v<Component>)
|
||||
{
|
||||
(reinterpret_cast<Component *>(data) + size_)->~Component();
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(pop_row_impl.template operator()<Components>(component_pointers_storage_[i++].data), ...);
|
||||
entity_ids_.pop_back();
|
||||
--size_;
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::init_components_stride()
|
||||
{
|
||||
std::size_t i = 0;
|
||||
((component_pointers_storage_[i].stride = std::is_empty_v<Components> ? 0 : sizeof(Components), ++i), ...);
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void table_impl<Components...>::reallocate()
|
||||
{
|
||||
std::size_t const new_capacity = capacity_ == 0 ? 64 : capacity_ * 2;
|
||||
|
||||
auto reallocate_impl = [&]<typename Component>(std::uint8_t * & data)
|
||||
{
|
||||
if constexpr (std::is_empty_v<Component>)
|
||||
{
|
||||
if (!data)
|
||||
data = reinterpret_cast<std::uint8_t *>(new Component[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto new_data = new std::uint8_t[new_capacity * sizeof(Component)];
|
||||
|
||||
auto old_begin = reinterpret_cast<Component *>(data);
|
||||
auto old_end = old_begin + size_;
|
||||
auto new_begin = reinterpret_cast<Component *>(new_data);
|
||||
for (; old_begin != old_end; ++old_begin, ++new_begin)
|
||||
{
|
||||
new (new_begin) Component{std::move(*old_begin)};
|
||||
old_begin->~Component();
|
||||
}
|
||||
|
||||
delete [] data;
|
||||
data = new_data;
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t i = 0;
|
||||
(reallocate_impl.template operator()<Components>(component_pointers_storage_[i++].data), ...);
|
||||
}
|
||||
|
||||
}
|
||||
36
libs/ecs/source/detail/entity_list.cpp
Normal file
36
libs/ecs/source/detail/entity_list.cpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#include <psemek/ecs/detail/entity_list.hpp>
|
||||
|
||||
namespace psemek::ecs::detail
|
||||
{
|
||||
|
||||
entity_id entity_list::create(table * table, std::uint32_t row)
|
||||
{
|
||||
if (free_ids_.empty())
|
||||
allocate_ids();
|
||||
|
||||
auto id = free_ids_.back();
|
||||
free_ids_.pop_back();
|
||||
|
||||
entities_[id].table = table;
|
||||
entities_[id].row = row;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void entity_list::destroy(entity_id id)
|
||||
{
|
||||
entities_[id].epoch += 1;
|
||||
free_ids_.push_back(id);
|
||||
}
|
||||
|
||||
void entity_list::allocate_ids()
|
||||
{
|
||||
static constexpr std::size_t batch_size = 1024;
|
||||
|
||||
auto old_size = entities_.size();
|
||||
entities_.resize(entities_.size() + batch_size);
|
||||
for (std::size_t id = entities_.size(); id --> old_size;)
|
||||
free_ids_.push_back(id);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue