-
Notifications
You must be signed in to change notification settings - Fork 57
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#pragma once | ||
|
||
#include <stdint.h> | ||
#include <vector> | ||
#include <limits> | ||
|
||
|
||
namespace sp { | ||
|
||
// A sparseset is a more optimized version of a map<> with an important constrain: | ||
// The key has to be an integer type with a limited range, the smaller the better. | ||
// This gives optimized cache performance when iterating over all entities, but still allows quick lookup of individual entries. | ||
template<typename T> class SparseSet final | ||
{ | ||
public: | ||
bool has(uint32_t index) | ||
{ | ||
if (index >= sparse.size()) | ||
return false; | ||
return sparse[index] < dense.size(); | ||
} | ||
|
||
T& get(uint32_t index) | ||
{ | ||
return data[sparse[index]]; | ||
} | ||
|
||
bool set(uint32_t index, const T& value) | ||
{ | ||
if (has(index)) { | ||
data[sparse[index]] = value; | ||
return false; | ||
} | ||
if (sparse.size() <= index) | ||
sparse.resize(index + 1, std::numeric_limits<uint32_t>::max()); | ||
sparse[index] = dense.size(); | ||
dense.push_back(index); | ||
data.emplace_back(value); | ||
return true; | ||
} | ||
|
||
bool remove(uint32_t index) | ||
{ | ||
if (!has(index)) | ||
return false; | ||
uint32_t moved_index = dense.back(); | ||
dense[sparse[index]] = moved_index; | ||
data[sparse[index]] = data.back(); | ||
sparse[moved_index] = sparse[index]; | ||
sparse[index] = std::numeric_limits<uint32_t>::max(); | ||
|
||
dense.pop_back(); | ||
data.pop_back(); | ||
return true; | ||
} | ||
|
||
class Iterator | ||
{ | ||
public: | ||
Iterator(SparseSet& _set, size_t _dense_index) : set(_set), dense_index(_dense_index) {} | ||
|
||
bool operator!=(const Iterator& other) const { return dense_index != other.dense_index; } | ||
void operator++() { dense_index--; } | ||
std::pair<uint32_t, T&> operator*() { return {set.dense[dense_index], set.data[dense_index]}; } | ||
std::pair<uint32_t, const T&> operator*() const { return {set.dense[dense_index], set.data[dense_index]}; } | ||
|
||
bool atEnd() { return dense_index == std::numeric_limits<size_t>::max(); } | ||
private: | ||
SparseSet& set; | ||
size_t dense_index; | ||
}; | ||
|
||
Iterator begin() { return Iterator(*this, dense.size() - 1); } | ||
Iterator end() { return Iterator(*this, std::numeric_limits<size_t>::max()); } | ||
private: | ||
std::vector<uint32_t> sparse; | ||
std::vector<uint32_t> dense; | ||
std::vector<T> data; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#include "ecs/component.h" | ||
|
||
namespace sp::ecs { | ||
|
||
static std::vector<ComponentStorageBase*> all_component_storage; | ||
|
||
ComponentStorageBase::ComponentStorageBase() | ||
{ | ||
all_component_storage.push_back(this); | ||
} | ||
|
||
void ComponentStorageBase::destroyAll(uint32_t index) | ||
{ | ||
for(auto storage : all_component_storage) | ||
storage->destroy(index); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma once | ||
|
||
#include "container/sparseset.h" | ||
|
||
namespace sp::ecs { | ||
|
||
class ComponentStorageBase { | ||
public: | ||
ComponentStorageBase(); | ||
|
||
protected: | ||
static void destroyAll(uint32_t index); | ||
virtual void destroy(uint32_t index) = 0; | ||
|
||
friend class Entity; | ||
}; | ||
|
||
template<typename T> class ComponentStorage : public ComponentStorageBase { | ||
void destroy(uint32_t index) override | ||
{ | ||
sparseset.remove(index); | ||
} | ||
|
||
SparseSet<T> sparseset; | ||
|
||
static inline ComponentStorage<T> storage; | ||
|
||
friend class Entity; | ||
template<class, class...> friend class Query; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
#include "entity.h" | ||
#include "component.h" | ||
#include <vector> | ||
|
||
namespace sp::ecs { | ||
|
||
static std::vector<uint32_t> entity_version; | ||
static std::vector<uint32_t> free_list; | ||
|
||
Entity Entity::create() | ||
{ | ||
Entity e; | ||
if (free_list.empty()) { | ||
e.index = entity_version.size(); | ||
e.version = 0; | ||
entity_version.push_back(0); | ||
} else { | ||
e.index = free_list.back(); | ||
free_list.pop_back(); | ||
e.version = entity_version[e.index]; | ||
} | ||
return e; | ||
} | ||
|
||
Entity Entity::fromIndex(uint32_t index) | ||
{ | ||
Entity e; | ||
e.index = index; | ||
e.version = entity_version[index]; | ||
return e; | ||
} | ||
|
||
Entity::operator bool() const | ||
{ | ||
if (index == std::numeric_limits<uint32_t>::max()) | ||
return false; | ||
return entity_version[index] == version; | ||
} | ||
|
||
void Entity::destroy() | ||
{ | ||
if (!*this) | ||
return; | ||
ComponentStorageBase::destroyAll(index); | ||
|
||
// By increasing the version number, everything else will know this entity no longer exists. | ||
entity_version[index] += 1; | ||
free_list.push_back(index); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#pragma once | ||
|
||
#include <stdint.h> | ||
#include <limits> | ||
|
||
#include "component.h" | ||
|
||
|
||
namespace sp::ecs { | ||
|
||
// An entity is a lightweight, copyable reference to entity components. | ||
// Entities have to be created with the create() function, and explicitly destroy()ed. | ||
class Entity final { | ||
public: | ||
Entity() = default; | ||
|
||
static Entity create(); | ||
void destroy(); | ||
|
||
explicit operator bool() const; | ||
|
||
template<class T> T* getComponent() | ||
{ | ||
if (!hasComponent<T>()) | ||
return nullptr; | ||
return &ComponentStorage<T>::storage.sparseset.get(index); | ||
} | ||
template<class T> T& addComponent() | ||
{ | ||
ComponentStorage<T>::storage.sparseset.set(index, {}); | ||
return ComponentStorage<T>::storage.sparseset.get(index); | ||
} | ||
template<class T, class... ARGS> T& addComponent(ARGS&&... args) | ||
{ | ||
ComponentStorage<T>::storage.sparseset.set(index, T{std::forward<ARGS>(args)...}); | ||
return ComponentStorage<T>::storage.sparseset.get(index); | ||
} | ||
template<class T> bool hasComponent() | ||
{ | ||
return ComponentStorage<T>::storage.sparseset.has(index); | ||
} | ||
|
||
private: | ||
static Entity fromIndex(uint32_t index); | ||
|
||
uint32_t index = std::numeric_limits<uint32_t>::max(); | ||
uint32_t version = std::numeric_limits<uint32_t>::max(); | ||
|
||
template<class, class...> friend class Query; | ||
}; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#pragma once | ||
|
||
#include "ecs/entity.h" | ||
#include "ecs/component.h" | ||
|
||
|
||
namespace sp::ecs { | ||
|
||
template<class PRIMARY, class... T> class Query { | ||
public: | ||
class Iterator { | ||
public: | ||
Iterator(int) : iterator(ComponentStorage<PRIMARY>::storage.sparseset.begin()) { checkForSkip(); } | ||
Iterator() : iterator(ComponentStorage<PRIMARY>::storage.sparseset.end()) {} | ||
|
||
bool operator!=(const Iterator& other) const { return iterator != other.iterator; } | ||
void operator++() { ++iterator; checkForSkip(); } | ||
std::tuple<Entity, PRIMARY&, T&...> operator*() { | ||
auto [index, primary] = *iterator; | ||
return {Entity::fromIndex(index), primary, ComponentStorage<T>::storage.sparseset.get(index)...}; | ||
} | ||
|
||
private: | ||
void checkForSkip() { | ||
if (iterator.atEnd()) return; | ||
if constexpr (sizeof...(T) > 0) { | ||
if (!checkIfHasAll<T...>()) { | ||
++iterator; | ||
} | ||
} | ||
} | ||
template<typename T2, typename... ARGS> bool checkIfHasAll() { | ||
auto index = (*iterator).first; | ||
if (!ComponentStorage<T2>::storage.sparseset.has(index)) | ||
return false; | ||
if constexpr (sizeof...(ARGS) > 0) | ||
return checkIfHasAll<ARGS...>(); | ||
return true; | ||
} | ||
|
||
typename SparseSet<PRIMARY>::Iterator iterator; | ||
}; | ||
|
||
Iterator begin() { | ||
return Iterator(0); | ||
} | ||
|
||
Iterator end() { | ||
return Iterator(); | ||
} | ||
}; | ||
|
||
} |
be84d36
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TinyCI build failure:
[/home/tinyci/builds/daid/SeriousProton/_build_native:make -j 2] returned [2]: