GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 58, 0 excluded
0.0%
Functions:
0 of 6, 0 excluded
0.0%
Branches:
0 of 28, 0 excluded
0.0%

apps/runner/src/eu/runner/entity.h
Line Branch Exec Source
1 #pragma once
2
3 namespace core
4 {
5 /// external type
6 struct Obb {};
7
8 template<typename T, typename F>
9 void update_and_erase(std::vector<T>* asrc, F&& update)
10 {
11 assert(asrc);
12 std::vector<T>& src = *asrc;
13
14 std::size_t index = 0;
15 std::size_t count = src.size();
16 while(index < count)
17 {
18 const bool remove = update(src[index]);
19 if(remove)
20 {
21 const auto last_index = count-1;
22 if(index != last_index)
23 {
24 std::swap(src[index], src[last_index]);
25 }
26 count -= 1;
27 src.pop_back();
28 }
29 else
30 {
31 index += 1;
32 }
33 }
34 }
35 }
36
37
38 /** \addtogroup entity Entity Components
39 * \brief A "ECS"
40 *
41 * Reference: https://www.youtube.com/watch?v=jjEsB611kxs&t=7072s
42
43 * ## Design thoughts:
44 * Should we have a single instance or a per-entity instance.
45 * Currently it's written as a per-entity instance.
46 *
47 * ### Pro:
48 * * Simpler
49 * * We can be sure it's thread safe as it won't integrate with other streads
50 *
51 * ### Cons:
52 * * Ugly
53 * * Wastes memory as we need a instance of the system per entity
54 *
55 * @{
56 */
57
58
59 namespace eu::runner
60 {
61
62
63 ///////////////////////////////////////////////////////////////////////////////////////////////
64 // forward declare
65
66 struct EntitySystemWithPrio;
67 struct EntitySystemUpdateStageList;
68 struct EntitySystemUpdate;
69
70 struct WorldSystemWithPrio;
71 struct WorldSystemUpdateStageList;
72 struct WorldSystemUpdate;
73
74 struct Entity;
75 struct Component;
76 struct ComponentType;
77 struct ComponentFactory;
78 struct SpatialComponent;
79 struct RequestedComponents;
80 struct EntitySystem;
81 struct EntitySystemType;
82 struct EntitySystemFactory;
83 struct WorldSystem;
84 struct WorldSystemType;
85 struct WorldSystemFactory;
86 struct World;
87
88
89
90
91 ///////////////////////////////////////////////////////////////////////////////////////////////
92 // enums
93
94 enum class UpdateStage
95 {
96 start_frame,
97 before_physics,
98 physics,
99 after_physics,
100 end_frame
101 };
102 constexpr unsigned int UpdateStageCount = static_cast<unsigned int>(UpdateStage::end_frame) + 1;
103
104 enum class EntityState
105 {
106 /// all components are unloaded
107 unloaded,
108
109 /// all components are loaded
110 loaded,
111
112 /// entity has been "turned on" in the world and entity components have been registered with all systems
113 activated
114 };
115
116 enum class ComponentState
117 {
118 /// initial state
119 unloaded,
120
121 /// resource load is in progress
122 loading,
123
124 /// all resources loaded successfully
125 loaded,
126
127 /// some or all resources failed to load
128 load_failed,
129
130 /// component has been initialized (automatically called after loading successfull)
131 initialized
132 };
133
134
135
136
137 ///////////////////////////////////////////////////////////////////////////////////////////////
138 // "headers"
139
140 struct Alive
141 {
142 void kill();
143 void update_for_frame();
144
145 bool is_pending_removal() const;
146 bool delete_owner() const;
147 private:
148 int health = 0;
149 };
150 template<typename T>
151 concept HasAlive = requires(T a)
152 {
153 { a.alive } -> std::same_as<Alive&>;
154 };
155
156
157 /// assumes there's an Alive called alive on T
158 template<HasAlive T>
159 void update_and_remove_alives
160 (
161 std::vector<std::unique_ptr<T>>* alives,
162 std::vector<std::unique_ptr<T>>* deads
163 )
164 {
165 core::update_and_erase(deads,
166 [](std::unique_ptr<T>& c) -> bool
167 {
168 c->alive.update_for_frame();
169 return c->alive.delete_owner();
170 }
171 );
172 core::update_and_erase(alives,
173 [deads](std::unique_ptr<T>& c) -> bool
174 {
175 c->alive.update_for_frame();
176 if(c->alive.is_pending_removal())
177 {
178 // move to dead to keep alive for a few more frames
179 deads->emplace_back(std::move(c));
180 return true;
181 }
182 else
183 {
184 return false;
185 }
186 }
187 );
188 }
189
190
191 struct EntitySystemWithPrio
192 {
193 EntitySystemWithPrio(EntitySystem* s, int p);
194
195 EntitySystem* system;
196 int prio;
197 };
198
199 struct EntitySystemUpdateStageList
200 {
201 void update(UpdateStage stage);
202 void add(EntitySystem* sys, int prio);
203 void remove(EntitySystem* sys);
204
205 private:
206 std::vector<EntitySystemWithPrio> systems;
207 // can't iterate a std::priority_queue so let's not use that for now
208 };
209
210 /** Updates EntitySystem in the right order.
211 */
212 struct EntitySystemUpdate
213 {
214 std::array<EntitySystemUpdateStageList, UpdateStageCount> systems;
215
216 void update(UpdateStage stage);
217 void add(EntitySystem* sys, UpdateStage stage, int prio);
218 void remove(EntitySystem* sys, UpdateStage stage);
219 };
220
221
222 /**
223 * A generic thing that you can't derive from.
224 * @todo flesh out documentation
225 */
226 struct Entity
227 {
228 Guid guid;
229 std::vector<std::unique_ptr<Component>> components;
230 std::vector<std::unique_ptr<Component>> dead_components;
231 EntitySystemUpdate systems;
232
233 // dynamically add/remove components
234
235 Component* root_component = nullptr;
236 bool is_spatial_entity() const;
237
238 /** Turn the entity on in the world.
239 * * register entity for all systems
240 * * create update list
241 * * create entity attachment
242 *
243 * activate components/local systems parallelized with one entity per thread
244 * activate world systems with one system per thread
245 */
246 void activate();
247
248 /// Turn the entity off, remove entity from all systems
249 void deactivate();
250
251 void update(UpdateStage stage);
252
253 /// Load all components (resource, memory...)
254 void load();
255
256 /// Unload all components (resource, memory...)
257 void unload();
258
259 Alive alive;
260 };
261
262 void attach(World* world, Entity* parent_id, Entity* child_id);
263
264
265 /** Basic data storage.
266 *
267 * Properties that define settings and resource.
268 *
269 * * Components have no access to other components.
270 * * Components have no access to the entity.
271 * * Components have no default update.
272 * * Inheritance of components is allowed.
273 * * Can contain logic if needed (example: animation graph component contains everything related to the animation graph) and be viewed as a black box.
274 *
275 * Loading state switching between @ref ComponentState
276 ```graphviz
277 digraph G
278 {
279 unloaded -> loading [label="load()"];
280 loading -> unloaded [label="unload()"];
281
282 loading_failed -> unloaded [label="unload()"];
283 loaded -> unloaded [label="unload()"];
284
285 loading -> loading_failed;
286 loading -> loaded;
287
288 loaded -> initialized[label="initialize()"];
289 initialized -> loaded[label="shutdown()"];
290 }
291 ```
292 */
293 struct Component
294 {
295 virtual ~Component() = default;
296
297 Guid guid;
298
299 /// for debug and tools
300 std::string name;
301
302 Alive alive;
303
304 // settings that are serialized
305 // resources that are loaded
306
307 virtual void on_load();
308 virtual void on_unload();
309
310 virtual void on_initialize();
311 virtual void on_shutdown();
312 };
313
314
315 struct ComponentType
316 {
317 virtual ~ComponentType() = default;
318
319 Hsh name;
320
321 /// can the entity have many components of this type
322 bool max_one_per_entity;
323
324 virtual Component* create() = 0;
325 };
326
327 /** Creates a new component type for built-in components.
328 *
329 * ```
330 * CUSTOM_COMPONENT(CustomComponent, custom, "custom-name");
331 * ```
332 */
333 #define CUSTOM_COMPONENT(TYPE, NAME, HASH)\
334 struct TYPE : ComponentType\
335 {\
336 constexpr TYPE() : ComponentType{HASH} {}\
337 std::unique_ptr<Component> create() const override;\
338 };\
339 constexpr TYPE NAME;
340
341 /** Helpful factory to create Component.
342 */
343 struct ComponentFactory
344 {
345 void add(const ComponentType* name);
346 const ComponentType* from_name_or_null(Hsh name) const;
347 private:
348 std::unordered_map<Hsh, const ComponentType*> types;
349 };
350
351
352 /**
353 * This is the only components that can reference other components.
354 * All components in graph must belong to the same entity (so same thread), with the exception of spatial entities that are attached to other spatial entitites.
355 * It's still safe since it's hidden from the user and sheduling ensures it's updated in order and on the same thread.
356 */
357 struct SpatialComponent : Component
358 {
359 // prefer to use set_local_transform
360 m4 local_transform;
361 m4 global_transform;
362
363 void set_local_transform(const m4& m);
364
365 void update_world_transform();
366
367 private:
368 /// non-inclusive bounds in local space
369 // core::Obb local_bounds;
370
371 /// non-inclusive bounds in world space
372 // core::Obb world_bounds;
373
374 // parent spatial components + socket attachment
375 // Entity* parent;
376
377 /// can reference other spatial components: https://youtu.be/jjEsB611kxs?t=6639
378 /// @todo find out if this can reference entities in other components or not
379 std::vector<SpatialComponent*> children;
380 };
381
382
383 /** Required and optional components for EntitySystem and WorldSystem.
384 Contains required and optional components for a system to work.
385 With tooling a user can see if the added system is missing components.
386
387 Example: Character animation system requires Animation and Skeletal Mesh and has a optional Cloth.
388 */
389 struct RequestedComponents
390 {
391 std::vector<const ComponentType*> required;
392 std::vector<const ComponentType*> optional;
393 };
394
395 /** A local system for a entity.
396 *
397 * only one system of a specified type is allowed per entity
398 *
399 * * It's manually added to a entity
400 * * It can have entity specific data
401 * * It can't reference other entities
402 *
403 * Examples
404 * * animation (uses animation and mesh components)
405 * * movement
406 * * targeting
407 */
408 struct EntitySystem
409 {
410 virtual ~EntitySystem() = default;
411
412 /// get requested components
413 /// @todo move to a entity system type
414 virtual RequestedComponents get_component_requests() = 0;
415
416 /**
417 * ## Design thought:
418 *
419 * Who removes this from the update list when it's destroyed?
420 * Better to return a register request and let the caller add/remove stages+prios to the update list?
421 */
422 virtual void register_updates(EntitySystemUpdate* updates) = 0;
423
424 /// Called by EntitySystemUpdate
425 virtual void update(UpdateStage stage) = 0;
426
427 /**
428 * ## Design thoughts:
429 *
430 * Should we get a callback for each component, or a general... here is a the latest state of the components you care about.
431 * The current setup is probably easier to implement but requirements could be different from actual usage
432 *
433 * Keep in mind that the system is per entity so it's just getting references to components and storing them as member variables
434 */
435
436 /// a component was added or activated on the entity
437 virtual void component_was_added(Component* c) = 0;
438
439 /// a component was removed or deactivated on the entity
440 virtual void component_was_removed(Component* c) = 0;
441 };
442
443
444 struct EntitySystemType
445 {
446 Hsh name;
447
448 EntitySystemType(Hsh);
449 virtual ~EntitySystemType() = default;
450
451 virtual std::unique_ptr<EntitySystem> create() = 0;
452 };
453
454 struct EntitySystemFactory
455 {
456 void add(const EntitySystemType* ny);
457 const EntitySystemType* from_name_or_null(Hsh name);
458
459 private:
460 std::unordered_map<Hsh, const EntitySystemType*> types;
461 };
462
463 /** A global system for the world.
464 *
465 * Only one system of a specified type is allowed per world
466 *
467 * Primary role is to handle global world state.
468 * Secondary role is to do data transfer between entities.
469 *
470 *
471 * Examples:
472 * * skeletal mesh
473 * * physics
474 * * static mesh
475 * * navmesh
476 *
477 * @todo Add types similar to ComponentType and ComponentFactory
478 */
479 struct WorldSystem
480 {
481 virtual ~WorldSystem() = default;
482 virtual void register_updates(WorldSystemUpdate* updates) = 0;
483
484 /*
485 Design thought: if this is used to transfer data between components,
486 should there be some ui hint system similar to get_component_requests?
487 */
488
489 /// Called by WorldSystemUpdate
490 virtual void update(UpdateStage stage) = 0;
491
492 virtual void system_was_added_to_world() = 0;
493 virtual void system_was_removed_from_world() = 0;
494
495 /// a component was added or activated on a entity
496 virtual void component_was_added(Entity* ent, Component* c) = 0;
497
498 /// a component was removed or deactivated on a entity
499 virtual void component_was_removed(Entity* ent, Component* c) = 0;
500 };
501
502 struct WorldSystemType
503 {
504 Hsh name;
505
506 WorldSystemType(Hsh);
507 virtual ~WorldSystemType() = default;
508
509 virtual std::unique_ptr<WorldSystem> create() = 0;
510 };
511
512 struct WorldSystemFactory
513 {
514 void add(const WorldSystemType* ty);
515 const WorldSystemType* from_name_or_null(Hsh name);
516
517 private:
518 std::unordered_map<Hsh, const WorldSystemType*> types;
519 };
520
521 struct WorldSystemWithPrio
522 {
523 WorldSystemWithPrio(WorldSystem* s, int p);
524
525 WorldSystem* system;
526 int prio;
527 };
528
529 struct WorldSystemUpdateStageList
530 {
531 void update(UpdateStage stage);
532 void add(WorldSystem* sys, int prio);
533 void remove(WorldSystem* sys);
534
535 private:
536 std::vector<WorldSystemWithPrio> systems;
537 };
538
539 /** Updates WorldSystem.
540 */
541 struct WorldSystemUpdate
542 {
543 void update(UpdateStage);
544
545 void add(WorldSystem*, UpdateStage, int prio);
546 void remove(WorldSystem*, UpdateStage stage);
547
548 private:
549 std::array<WorldSystemUpdateStageList, UpdateStageCount> systems;
550 };
551
552 struct World
553 {
554 void update(UpdateStage s);
555
556 private:
557 std::vector<std::unique_ptr<Entity>> entities;
558 std::vector<std::unique_ptr<WorldSystem>> systems;
559 WorldSystemUpdate system_update;
560 };
561
562 }
563
564
565 /**
566 * @}
567 */
568