vkmEngine 1.0.0
A C++ game engine · vkmengine.com
Loading...
Searching...
No Matches
scene.h
1#pragma once
2
3#include <cstdint>
4#include <memory>
5#include <tuple>
6#include <utility>
7#include <vector>
8
9#include "l_assert.h"
10
11#include "ecs/component/core/hierarchy.h"
12#include "core/memory/type_registry.h"
13#include "ecs/environment.h"
14#include "ecs/physics_settings.h"
15#include "ecs/entity.h"
16#include "ecs/scene_observer.h"
17#include "core/memory/slot_allocator.h"
18#include "core/memory/sparse_set.h"
19#include "core/memory/types.h"
20
21namespace Vkm::Engine {
22
29class Scene {
30 public:
31 Scene() = default;
32 ~Scene() = default;
33
34 Scene(const Scene& other) = delete;
35 Scene& operator=(const Scene& other) = delete;
36
37 Scene(Scene && other) = delete;
38 Scene& operator=(Scene && other) = delete;
39
40 public:
51 bool recycled = false;
52 const EntityId id{m_entityAllocator.allocate(&recycled)};
53 if (recycled) {
54 m_components.forEach([&](ISparseSet& set) { set.removeIfPresent(id.slot()); });
55 }
56 return id;
57 }
58
69 EntityId createEntityAt(uint32_t index) {
70 const EntityId id{m_entityAllocator.allocateAt(index)};
71 if (id) {
72 m_components.forEach([&](ISparseSet& set) { set.removeIfPresent(id.slot()); });
73 }
74 return id;
75 }
76
86 VKM_ASSERT(isAlive(id), "Scene::destroyEntity called with dead/stale entity");
87 if (!isAlive(id)) return;
88
89 // Before tear-down, while the entity and its components are still intact.
90 for (ISceneObserver* observer : m_observers) {
91 observer->onEntityDestroyed(id);
92 }
93 detachFromHierarchy(*this, id);
94
95 m_components.forEach([&](ISparseSet& set) { set.removeIfPresent(id.slot()); });
96 m_entityAllocator.free(id.key);
97 }
98
99 bool isAlive(EntityId id) const { return m_entityAllocator.has(id.key); }
100 bool isAliveAtIndex(uint32_t index) const { return m_entityAllocator.isAliveAtIndex(index); }
101 size_t entityCount() const { return m_entityAllocator.size(); }
102
111 EntityId entityAt(uint32_t index) const {
112 return EntityId{m_entityAllocator.handleAt(index)};
113 }
114
115 public:
127 template<typename T>
128 auto& add(EntityId entity, T && component) {
129 VKM_ASSERT(isAlive(entity), "Scene::add called with dead/stale entity");
130 using U = std::remove_cv_t<std::remove_reference_t<T>>;
131 VKM_ASSERT(!has<U>(entity), "Scene::add: entity already carries this component type");
132 return getStorage<U>().add(entity.slot(), std::forward<T>(component));
133 }
134
144 template<typename T>
145 void remove(EntityId entity) {
146 VKM_ASSERT(isAlive(entity), "Scene::remove called with dead/stale entity");
147 if (!isAlive(entity)) return;
148 auto* store = findStorage<T>();
149 if (store && store->contains(entity.slot())) {
150 store->remove(entity.slot());
151 }
152 }
153
161 template<typename T>
162 bool has(EntityId entity) const {
163 if (!isAlive(entity)) return false;
164 auto* store = findStorage<T>();
165 return store && store->contains(entity.slot());
166 }
167
175 template<typename T>
176 T& get(EntityId entity) {
177 VKM_ASSERT(isAlive(entity), "Scene::get called with dead/stale entity");
178 auto* store = findStorage<T>();
179 VKM_ASSERT(store, "Scene::get called for unregistered component type");
180 return store->get(entity.slot());
181 }
182
190 template<typename T>
191 const T& get(EntityId entity) const {
192 VKM_ASSERT(isAlive(entity), "Scene::get called with dead/stale entity");
193 auto* store = findStorage<T>();
194 VKM_ASSERT(store, "Scene::get called for unregistered component type");
195 return store->get(entity.slot());
196 }
197
205 template<typename T>
206 T* tryGet(EntityId entity) {
207 if (!isAlive(entity)) return nullptr;
208 auto* store = findStorage<T>();
209 if (!store || !store->contains(entity.slot())) return nullptr;
210 return &store->get(entity.slot());
211 }
212
214 template<typename T>
215 const T* tryGet(EntityId entity) const {
216 if (!isAlive(entity)) return nullptr;
217 const auto* store = findStorage<T>();
218 if (!store || !store->contains(entity.slot())) return nullptr;
219 return &store->get(entity.slot());
220 }
221
228 template<typename T>
229 size_t count() const {
230 auto* store = findStorage<T>();
231 return store ? store->size() : 0;
232 }
233
234 public:
246 template<typename First, typename... Rest, typename Fn>
247 void forEach(Fn&& fn) {
248 // findStorage: creating the set here would flip a later storage<T>()
249 // from null, which a caller may branch on.
250 auto* firstStorage = findStorage<First>();
251 if (!firstStorage) return;
252
253 if constexpr (sizeof...(Rest) == 0) {
254 firstStorage->forEach([&](uint32_t entityIdx, First& first) {
255 EntityId eid = entityAt(entityIdx);
256 fn(eid, first);
257 });
258 } else {
259 auto restStorages = std::make_tuple(findStorage<Rest>()...);
260 if (!(std::get<SparseSet<Rest>*>(restStorages) && ...)) return;
261
262 firstStorage->forEach([&](uint32_t entityIdx, First& first) {
263 if (!(std::get<SparseSet<Rest>*>(restStorages)->contains(entityIdx) && ...)) return;
264
265 EntityId eid = entityAt(entityIdx);
266 fn(eid, first, std::get<SparseSet<Rest>*>(restStorages)->get(entityIdx)...);
267 });
268 }
269 }
270
271 template<typename First, typename... Rest, typename Fn>
272 void forEach(Fn&& fn) const {
273 auto* firstStorage = findStorage<First>();
274 if (!firstStorage) return;
275
276 if constexpr (sizeof...(Rest) == 0) {
277 firstStorage->forEach([&](uint32_t entityIdx, const First& first) {
278 EntityId eid = entityAt(entityIdx);
279 fn(eid, first);
280 });
281 } else {
282 auto restStorages = std::make_tuple(findStorage<Rest>()...);
283 if (!(std::get<const SparseSet<Rest>*>(restStorages) && ...)) return;
284
285 firstStorage->forEach([&](uint32_t entityIdx, const First& first) {
286 if (!(std::get<const SparseSet<Rest>*>(restStorages)->contains(entityIdx) && ...)) return;
287
288 EntityId eid = entityAt(entityIdx);
289 fn(eid, first, std::get<const SparseSet<Rest>*>(restStorages)->get(entityIdx)...);
290 });
291 }
292 }
293
300 template<typename Fn>
301 void forEachEntity(Fn&& fn) const {
302 m_entityAllocator.forEach([&](uint32_t idx) {
303 fn(entityAt(idx));
304 });
305 }
306
307 public:
315 void clear() {
316 // Every entity goes at once, so no detachFromHierarchy is needed.
317 m_components.clear();
318 m_entityAllocator.clear();
319 m_environment = Environment{};
320 m_physics = PhysicsSettings{};
321 ++m_epoch;
322 }
323
327 void compact() {
328 m_components.forEach([](ISparseSet& set) { set.compact(); });
329 }
330
339 void swap(Scene& other) noexcept {
340 using std::swap;
341 m_entityAllocator.swap(other.m_entityAllocator);
342 m_components.swap(other.m_components);
343 swap(m_environment, other.m_environment);
344 swap(m_physics, other.m_physics);
345 ++m_epoch;
346 ++other.m_epoch;
347 }
348
356 void addObserver(ISceneObserver* observer) {
357 m_observers.push_back(observer);
358 }
359
366 for (auto it = m_observers.begin(); it != m_observers.end(); ++it) {
367 if (*it == observer) {
368 m_observers.erase(it);
369 return;
370 }
371 }
372 }
373
374 public:
383 template<typename T>
384 SparseSet<T>* storage() { return findStorage<T>(); }
385
386 template<typename T>
387 const SparseSet<T>* storage() const { return findStorage<T>(); }
388
397 uint64_t epoch() const { return m_epoch; }
398
404 Environment& environment() { return m_environment; }
405 const Environment& environment() const { return m_environment; }
406
412 PhysicsSettings& physics() { return m_physics; }
413 const PhysicsSettings& physics() const { return m_physics; }
414
415 private:
422 template<typename T>
423 SparseSet<T>& getStorage() {
424 ISparseSet& base = m_components.ensure<T>([] { return std::make_unique<SparseSet<T>>(); });
425 return static_cast<SparseSet<T>&>(base);
426 }
427
434 template<typename T>
435 SparseSet<T>* findStorage() {
436 return static_cast<SparseSet<T>*>(m_components.find<T>());
437 }
438
439 template<typename T>
440 const SparseSet<T>* findStorage() const {
441 return static_cast<const SparseSet<T>*>(m_components.find<T>());
442 }
443
444 private:
445 Environment m_environment;
446 PhysicsSettings m_physics;
447
448 uint64_t m_epoch = 0;
449
450 SlotAllocator m_entityAllocator;
451 TypeRegistry<ISparseSet> m_components;
452 std::vector<ISceneObserver*> m_observers;
453};
454
467template <typename First, typename... Rest, typename Pred>
468EntityId findLowestSlot(const Scene& scene, Pred pred) {
469 EntityId found{};
470 scene.forEach<First, Rest...>([&](EntityId id, const First& first, const Rest&... rest) {
471 if (!pred(first, rest...)) return;
472 if (!found || id.slot() < found.slot()) found = id;
473 });
474 return found;
475}
476
477} // namespace Vkm::Engine
Observer notified by a Scene when an entity is destroyed.
Definition scene_observer.h:13
Type-erased interface for SparseSet, so one registry holds every type.
Definition sparse_set.h:15
virtual void compact()=0
Release the sparse array's slack, keeping every live element.
virtual void removeIfPresent(uint32_t key)=0
Remove the element at the given key, if this set holds one.
Central registry managing entities and an open set of component types.
Definition scene.h:29
const T * tryGet(EntityId entity) const
The entity's T, or null when it has none.
Definition scene.h:215
void removeObserver(ISceneObserver *observer)
Unregister an observer added with addObserver.
Definition scene.h:365
uint64_t epoch() const
Identity of the world these entities belong to, bumped by clear() and swap().
Definition scene.h:397
void forEachEntity(Fn &&fn) const
Invoke fn(EntityId) for every live entity in this scene.
Definition scene.h:301
void clear()
Drop every component set and reset the entity allocator, the environment and the physics settings in ...
Definition scene.h:315
void forEach(Fn &&fn)
Iterate all live components densely (no holes).
Definition scene.h:247
size_t count() const
Number of live components of type T.
Definition scene.h:229
T * tryGet(EntityId entity)
The entity's T, or null when it has none.
Definition scene.h:206
void destroyEntity(EntityId id)
Destroy an entity by removing all of its components and recycling its slot.
Definition scene.h:85
T & get(EntityId entity)
Get a mutable reference to an entity's component of type T.
Definition scene.h:176
EntityId createEntity()
Create a new entity and assign a unique EntityId.
Definition scene.h:50
PhysicsSettings & physics()
The scene's physics world parameters, saved with it.
Definition scene.h:412
bool has(EntityId entity) const
Check if an entity has a component of type T.
Definition scene.h:162
SparseSet< T > * storage()
Direct access to the typed SparseSet for component type T.
Definition scene.h:384
void addObserver(ISceneObserver *observer)
Register an observer, notified at the start of every destroyEntity.
Definition scene.h:356
const T & get(EntityId entity) const
Get a const reference to an entity's component of type T.
Definition scene.h:191
EntityId createEntityAt(uint32_t index)
Allocate an entity at the requested slot index.
Definition scene.h:69
auto & add(EntityId entity, T &&component)
Give an entity a component it does not have yet.
Definition scene.h:128
Environment & environment()
The scene's lighting environment, saved with it.
Definition scene.h:404
void compact()
Compact every component SparseSet to reclaim wasted memory.
Definition scene.h:327
void remove(EntityId entity)
Take a component of type T off an entity.
Definition scene.h:145
void swap(Scene &other) noexcept
Swap internal state with another Scene.
Definition scene.h:339
EntityId entityAt(uint32_t index) const
The full id of the entity in slot index, generation included.
Definition scene.h:111
bool isAliveAtIndex(uint32_t index) const
Check whether index currently holds a live slot.
Definition slot_allocator.h:104
size_t size() const
Number of live slots.
Definition slot_allocator.h:192
bool has(StorageIndex id) const
Test whether a handle is still valid.
Definition slot_allocator.h:78
Dense-packed storage indexed by external uint32_t keys.
Definition sparse_set.h:50
Names one entity of one Scene, for as long as that slot holds it.
Definition entity.h:23
The scene's lighting environment: sky, night sky and fog.
Definition environment.h:130
The physics world's own parameters, read once per fixed step.
Definition physics_settings.h:14