PixelBullet  0.0.1
A C++ game engine
Loading...
Searching...
No Matches
scene.h
1#pragma once
2
4#include "pixelbullet/scene/component_storage.h"
5#include "pixelbullet/scene/entity_id.h"
6#include "pixelbullet/scene/scene_environment_settings.h"
7#include "pixelbullet/serialization/node_reader.h"
8
9#include <cstdint>
10#include <functional>
11#include <memory>
12#include <stdexcept>
13#include <string>
14#include <string_view>
15#include <type_traits>
16#include <typeindex>
17#include <unordered_map>
18#include <vector>
19
20namespace pixelbullet
21{
22class SceneSerializer;
23
24enum class SceneComponentDefaults
25{
26 None,
27 Engine,
28};
29
30class Scene
31{
32public:
33 explicit Scene(SceneComponentDefaults defaults = SceneComponentDefaults::Engine);
34
35 EntityId CreateEntity();
36 void DestroyEntity(EntityId entity_id);
37
38 [[nodiscard]] bool IsAlive(EntityId entity_id) const
39 {
40 return entity_id.Index() < entities_.size() && entities_[entity_id.Index()] == entity_id;
41 }
42
43 [[nodiscard]] std::vector<EntityId> GetEntities() const
44 {
45 std::vector<EntityId> entities;
46 entities.reserve(entities_.size());
47 for (const EntityId entity : entities_)
48 {
49 if (entity)
50 {
51 entities.push_back(entity);
52 }
53 }
54 return entities;
55 }
56
57 template <typename T, typename... Args>
58 T& AddComponent(EntityId entity_id, Args&&... args)
59 {
60 ASSERT(IsAlive(entity_id), "Cannot add component to stale or invalid entity");
61 auto storage = GetOrCreateComponentStorage<T>();
62 T& component = storage->components.emplace(entity_id.Index(), std::forward<Args>(args)...);
63 ++mutation_generation_;
64 return component;
65 }
66
67 template <typename T>
68 void RemoveComponent(EntityId entity_id)
69 {
70 ASSERT(IsAlive(entity_id), "Cannot remove component from stale or invalid entity");
71 auto* storage = TryGetComponentStorage<T>();
72 if (storage && storage->components.erase(entity_id.Index()))
73 {
74 ++mutation_generation_;
75 }
76 }
77
78 template <typename T>
79 bool HasComponent(EntityId entity_id) const
80 {
81 if (!IsAlive(entity_id))
82 {
83 return false;
84 }
85
86 const auto type_id = std::type_index(typeid(T));
87 auto it = component_storage_.find(type_id);
88 if (it != component_storage_.end())
89 {
90 const auto* storage = static_cast<const ComponentStorage<T>*>(it->second.get());
91 return storage->components.contains(entity_id.Index());
92 }
93 return false;
94 }
95
96 template <typename T>
97 void RegisterComponent(std::string_view serialization_name)
98 {
99 auto safe_deserialize = [](T& component, const Node& node, const std::string_view diagnostic_path) -> SerializationResult
100 {
101 if constexpr (serialization::NodeReadable<T>)
102 {
103 serialization::NodeReader reader{ std::string(diagnostic_path) };
104 if (!TryReadNode(node, component, reader))
105 {
106 return reader.ToResult();
107 }
108 return SerializationResult{ true, {} };
109 }
110 else
111 {
112 (void)component;
113 (void)node;
114 return SerializationResult{
115 false,
116 "Component type '" + std::string(typeid(T).name()) + "' does not support safe deserialization.",
117 { SerializationDiagnostic{ .path = std::string(diagnostic_path),
118 .message = "Component type does not support safe deserialization." } },
119 };
120 }
121 };
122 RegisterComponentType(std::type_index(typeid(T)), std::string(serialization_name),
123 [safe_deserialize]() { return std::make_unique<ComponentStorage<T>>(safe_deserialize); });
124 }
125
126 void RegisterComponentTypesFrom(const Scene& source);
127 [[nodiscard]] std::vector<std::string> GetRegisteredComponentNames() const;
128
129 template <typename T>
130 T& GetComponent(EntityId entity_id)
131 {
132 T* component = TryGetComponent<T>(entity_id);
133 ASSERT(component, "Component not found");
134 return *component;
135 }
136
137 template <typename T>
138 const T& GetComponent(EntityId entity_id) const
139 {
140 const T* component = TryGetComponent<T>(entity_id);
141 ASSERT(component, "Component not found");
142 return *component;
143 }
144
145 template <typename T>
146 T* TryGetComponent(EntityId entity_id)
147 {
148 if (!IsAlive(entity_id))
149 {
150 return nullptr;
151 }
152
153 auto* storage = TryGetComponentStorage<T>();
154 if (!storage || !storage->components.contains(entity_id.Index()))
155 {
156 return nullptr;
157 }
158
159 return &storage->components.at(entity_id.Index());
160 }
161
162 template <typename T>
163 const T* TryGetComponent(EntityId entity_id) const
164 {
165 if (!IsAlive(entity_id))
166 {
167 return nullptr;
168 }
169
170 const auto* storage = TryGetComponentStorage<T>();
171 if (!storage || !storage->components.contains(entity_id.Index()))
172 {
173 return nullptr;
174 }
175
176 return &storage->components.at(entity_id.Index());
177 }
178
179 template <typename T>
180 const SparseSet<T>& GetComponentSet() const
181 {
182 const auto type_id = std::type_index(typeid(T));
183 auto it = component_storage_.find(type_id);
184 if (it != component_storage_.end())
185 {
186 const auto* storage = static_cast<const ComponentStorage<T>*>(it->second.get());
187 return storage->components;
188 }
189 throw std::runtime_error("Component not found");
190 }
191
192 template <typename T, typename Func>
193 void Each(Func&& func)
194 {
195 auto* storage = TryGetComponentStorage<T>();
196 if (!storage)
197 {
198 return;
199 }
200
201 for (const auto& entry : storage->components)
202 {
203 if (entry.id >= entities_.size())
204 {
205 continue;
206 }
207
208 const EntityId entity = entities_[entry.id];
209 if (!entity)
210 {
211 continue;
212 }
213
214 func(entity, storage->components.at(entry.id));
215 }
216 }
217
218 template <typename T, typename Func>
219 void Each(Func&& func) const
220 {
221 const auto* storage = TryGetComponentStorage<T>();
222 if (!storage)
223 {
224 return;
225 }
226
227 for (const auto& entry : storage->components)
228 {
229 if (entry.id >= entities_.size())
230 {
231 continue;
232 }
233
234 const EntityId entity = entities_[entry.id];
235 if (!entity)
236 {
237 continue;
238 }
239
240 func(entity, storage->components.at(entry.id));
241 }
242 }
243
244 SceneEnvironmentSettings& GetEnvironmentSettings() noexcept
245 {
246 return environment_settings_;
247 }
248
249 const SceneEnvironmentSettings& GetEnvironmentSettings() const noexcept
250 {
251 return environment_settings_;
252 }
253
254 void SetEnvironmentSettings(SceneEnvironmentSettings settings) noexcept
255 {
256 environment_settings_ = std::move(settings);
257 ++mutation_generation_;
258 }
259
260 [[nodiscard]] uint64_t GetMutationGeneration() const noexcept
261 {
262 return mutation_generation_;
263 }
264
265 template <typename T, typename U, typename Func>
266 void Each(Func&& func)
267 {
268 auto* primary = TryGetComponentStorage<T>();
269 auto* secondary = TryGetComponentStorage<U>();
270 if (!primary || !secondary)
271 {
272 return;
273 }
274
275 if (secondary->components.size() < primary->components.size())
276 {
277 for (const auto& entry : secondary->components)
278 {
279 if (entry.id >= entities_.size())
280 {
281 continue;
282 }
283
284 const EntityId entity = entities_[entry.id];
285 if (!entity || !primary->components.contains(entry.id))
286 {
287 continue;
288 }
289
290 func(entity, primary->components.at(entry.id), secondary->components.at(entry.id));
291 }
292 return;
293 }
294
295 for (const auto& entry : primary->components)
296 {
297 if (entry.id >= entities_.size())
298 {
299 continue;
300 }
301
302 const EntityId entity = entities_[entry.id];
303 if (!entity || !secondary->components.contains(entry.id))
304 {
305 continue;
306 }
307
308 func(entity, primary->components.at(entry.id), secondary->components.at(entry.id));
309 }
310 }
311
312 template <typename T, typename U, typename Func>
313 void Each(Func&& func) const
314 {
315 const auto* primary = TryGetComponentStorage<T>();
316 const auto* secondary = TryGetComponentStorage<U>();
317 if (!primary || !secondary)
318 {
319 return;
320 }
321
322 if (secondary->components.size() < primary->components.size())
323 {
324 for (const auto& entry : secondary->components)
325 {
326 if (entry.id >= entities_.size())
327 {
328 continue;
329 }
330
331 const EntityId entity = entities_[entry.id];
332 if (!entity || !primary->components.contains(entry.id))
333 {
334 continue;
335 }
336
337 func(entity, primary->components.at(entry.id), secondary->components.at(entry.id));
338 }
339 return;
340 }
341
342 for (const auto& entry : primary->components)
343 {
344 if (entry.id >= entities_.size())
345 {
346 continue;
347 }
348
349 const EntityId entity = entities_[entry.id];
350 if (!entity || !secondary->components.contains(entry.id))
351 {
352 continue;
353 }
354
355 func(entity, primary->components.at(entry.id), secondary->components.at(entry.id));
356 }
357 }
358
359 friend Node& operator<<(Node& node, const Scene& scene);
360 friend const Node& operator>>(const Node& node, Scene& scene);
361 friend class SceneSerializer;
362
363private:
364 using ComponentTypeId = std::type_index;
365 using ComponentStorageFactory = std::function<std::unique_ptr<ComponentStorageBase>()>;
366
367 struct ComponentRegistration
368 {
369 std::string name;
370 ComponentStorageFactory factory;
371 };
372
373 void ClearSceneState();
374 void RegisterComponentType(ComponentTypeId type_id, std::string serialization_name, ComponentStorageFactory factory);
375 ComponentStorageBase* FindOrCreateStorage(ComponentTypeId type_id, std::string_view serialization_name);
376 [[nodiscard]] const ComponentRegistration* FindComponentRegistration(ComponentTypeId type_id) const;
377
378private:
379 std::vector<EntityId> entities_;
380 std::vector<uint32_t> entity_generations_;
381 std::vector<uint32_t> free_entities_;
382 std::unordered_map<ComponentTypeId, std::unique_ptr<ComponentStorageBase>> component_storage_;
383 std::unordered_map<ComponentTypeId, ComponentRegistration> component_registrations_;
384 std::unordered_map<std::string, ComponentTypeId> component_types_by_name_;
385 std::unordered_map<std::string, ComponentStorageFactory> component_storage_factories_by_name_;
386 SceneEnvironmentSettings environment_settings_;
387 uint64_t mutation_generation_ = 1;
388
389private:
390 template <typename T>
391 ComponentStorage<T>* GetOrCreateComponentStorage()
392 {
393 const auto type_id = std::type_index(typeid(T));
394 auto it = component_storage_.find(type_id);
395 if (it != component_storage_.end())
396 {
397 return static_cast<ComponentStorage<T>*>(it->second.get());
398 }
399 else
400 {
401 auto storage = std::make_unique<ComponentStorage<T>>();
402 ComponentStorage<T>* ptr = storage.get();
403 component_storage_[type_id] = std::move(storage);
404 return ptr;
405 }
406 }
407
408 template <typename T>
409 ComponentStorage<T>* TryGetComponentStorage()
410 {
411 const auto type_id = std::type_index(typeid(T));
412 auto it = component_storage_.find(type_id);
413 if (it == component_storage_.end())
414 {
415 return nullptr;
416 }
417 return static_cast<ComponentStorage<T>*>(it->second.get());
418 }
419
420 template <typename T>
421 const ComponentStorage<T>* TryGetComponentStorage() const
422 {
423 const auto type_id = std::type_index(typeid(T));
424 auto it = component_storage_.find(type_id);
425 if (it == component_storage_.end())
426 {
427 return nullptr;
428 }
429 return static_cast<const ComponentStorage<T>*>(it->second.get());
430 }
431};
432
433void RegisterEngineSceneComponents(Scene& scene);
434
435} // namespace pixelbullet
Provides assertion and panic mechanisms with optional custom formatting.
#define ASSERT(condition,...)
Asserts that a condition is true.
Definition assert.h:142
Definition entity_id.h:12
Represents a hierarchical node capable of storing various data types and supporting YAML serializatio...
Definition node.h:49
Definition scene.h:31
Definition sparse_set.h:16
Definition node_reader.h:48
Definition node_reader.h:459
Definition component_storage.h:18
Definition component_storage.h:31
Definition scene_environment_settings.h:14
Definition serialization_result.h:9
Definition serialization_result.h:17