Improving entity manager and entity support for a simpler version, made some cleanup on the road to support serialization of world

This commit is contained in:
2026-08-31 23:05:20 -04:00
parent 4180622d6a
commit cb615091ca
17 changed files with 607 additions and 88 deletions
+3
View File
@@ -0,0 +1,3 @@
#include <Entity/Entity.h>
DEFINE_ENTITY(Inert);
+66 -27
View File
@@ -1,68 +1,107 @@
#pragma once
#pragma once
#include <Core/Common/CoreUtils.h>
#include <Core/Common/EnumUtils.h>
#include <Core/Memory/Allocator.h>
#include <Core/Memory/MemoryArena.h>
#include <Engine/Class.h>
#include <Entity/EntityManager.h>
#define DECLARE_ENTITY() \
Entity* Base; \
Entity* Base; \
static const Class* Kind;
// Will register the class globally at launch
#define DEFINE_ENTITY(entity) \
constexpr Class entityKind##entity(#entity, sizeof(#entity) / sizeof(char)); \
const Class* entity::Kind = &entityKind##entity;
constexpr Class entityKind##entity = \
MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, sizeof(entity), alignof(entity), nullptr); \
const Class* entity::Kind = &entityKind##entity;
#define DEFINE_ENTITY_SERIALIZED(entity, serialize_fct) \
constexpr Class entityKind##entity = \
MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, sizeof(entity), alignof(entity), serialize_fct); \
const Class* entity::Kind = &entityKind##entity;
struct EntityManager;
using DerivedType = void*;
using EntityID = uint64_t;
// clang-format off
#define ENTITY_TYPE_LIST(X) X(Inert),
#define AS_ENUM(name) name
enum class Entity_Type : uint8
{
ENTITY_TYPE_LIST(AS_ENUM)
Count
};
#undef AS_ENUM
#define AS_STR(name) #name
inline const char* kEntity_type_names[] = {
ENTITY_TYPE_LIST(AS_STR)
"Count"
};
#define ENTITY(kind) ToUnderlying(Entity_Type::kind)
// clang-format on
struct Entity final
{
EntityID ID;
const Class* Kind;
DerivedType Derived;
float X, Y;
index_t MeshInstance = indexMax;
EntityID ID = 0;
const Class* Kind = nullptr;
DerivedType Derived = nullptr;
float X = 0.0f;
float Y = 0.0f;
float Z = 0.0f;
};
// Can reinterpret cast to this to have the offset of Base and Kind for any entity
struct entity_template
{
DECLARE_ENTITY();
};
struct Inert
{
DECLARE_ENTITY()
index_t MeshInstance = indexMax;
};
//
template <typename EntityType>
concept EntityConcept = requires(EntityType entity) {
requires std::same_as<decltype(entity.Kind), const Class*>;
{ EntityType::Kind } -> std::convertible_to<const Class*>;
requires std::same_as<decltype(entity.Base), Entity*>;
};
template <typename EntityType>
requires EntityConcept<EntityType>
bool IsA(const Entity* entity)
[[nodiscard]] bool IsA(const Entity* entity)
{
Assert(entity != nullptr);
return entity->Kind == EntityType::Kind;
}
template <typename EntityType>
requires EntityConcept<EntityType>
EntityType* MakeEntity(EntityManager& manager, float x, float y)
[[nodiscard]] EntityType* MakeEntity(EntityManager& manager, float x, float y, float z)
{
auto* arena = manager.Arena;
EntityType* result = ArenaPushStruct<EntityType>(arena);
Entity base;
base.X = x;
base.Y = y;
base.Derived = result;
base.Kind = EntityType::Kind;
manager.Entities.PushBack(base);
EntityType result;
Entity base;
base.X = x;
base.Y = y;
base.Z = z;
base.Kind = EntityType::Kind;
result->Base = manager.Entities.Back();
RegisterEntity(manager, &base);
return result;
return (EntityType*)RegisterEntity(manager, &base, &result);
}
template <typename EntityType>
requires EntityConcept<EntityType>
EntityType* DownCast(Entity* entity)
[[nodiscard]] EntityType* DownCast(Entity* entity)
{
Assert(entity != nullptr);
Assert(IsA<EntityType>(entity));
return static_cast<EntityType*>(entity->Derived);
}
+40 -7
View File
@@ -1,6 +1,9 @@
#include <Entity/EntityManager.h>
#include <Entity/EntityManager.h>
#include <Core/Common/EnumUtils.h>
#include <Data/World.h>
#include <Entity/Entity.h>
#include <game.h>
#include <Graphics/MeshRenderer.h>
EntityID EntityManager::ID = 0;
@@ -12,7 +15,15 @@ void InitEntityManager(NonNullPtr<World> world)
newManager->Entities.Create(world->WorldArena JULIET_DEBUG_PARAM("Entities"));
newManager->Arena = ArenaAllocate({ .Name = "Entity Arena" });
ArenaParams by_type_params{ .ReserveSize = Kilobytes(1),
.CommitSize = Kilobytes(1),
.Name = "" JULIET_DEBUG_ONLY(, .CanReserveMore = false) };
for (uint8 i = 0; i < ENTITY(Count); ++i)
{
by_type_params.Name = kEntity_type_names[i];
newManager->by_type[i].arena = ArenaAllocate(by_type_params);
newManager->by_type[i].array = nullptr;
}
}
void ShutdownEntityManager()
@@ -26,18 +37,40 @@ EntityManager& GetEntityManager()
return *entityManager;
}
void RegisterEntity(EntityManager& /*manager*/, Entity* entity)
void Serialize(NonNullPtr<EntityManager> entityManager) {}
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity)
{
entity->ID = EntityManager::ID++;
base->ID = EntityManager::ID++;
base->Derived = entity;
manager.Entities.PushBack(*base);
auto* ptr = (entity_template*)ArenaPushSize(manager.by_type[base->Kind->kind].arena, base->Kind->size_of, base->Kind->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[base->Kind->kind]));
MemCopy(ptr, entity, base->Kind->size_of);
manager.by_type[base->Kind->kind].count += 1;
ptr->Base = manager.Entities.Back();
if (manager.by_type[base->Kind->kind].array == nullptr)
{
manager.by_type[base->Kind->kind].array = ptr;
}
return ptr;
}
void UpdateEntityManager(EntityManager& manager)
{
for (Entity& ent : manager.Entities)
// Todo : inert by definition dont move, but this is for test
auto& by_type = manager.by_type[ENTITY(Inert)];
for (index_t i = 0; i < by_type.count; ++i)
{
if (ent.MeshInstance != indexMax)
Inert* inert = reinterpret_cast<Inert*>(by_type.array) + i;
if (inert->MeshInstance != indexMax)
{
SetMeshInstanceTransform(ent.MeshInstance, MatrixTranslation(ent.X, ent.Y, 0.0f));
SetMeshInstanceTransform(inert->MeshInstance, MatrixTranslation(inert->Base->X, inert->Base->Y, inert->Base->Z));
}
}
}
+20 -12
View File
@@ -1,24 +1,32 @@
#pragma once
#pragma once
#include <Core/Common/CoreTypes.h>
#include <Core/Container/Vector.h>
#include <game.h>
using EntityID = uint64_t;
#include <Entity/Entity.h>
struct Entity;
struct World;
struct typed_entity_array
{
Arena* arena;
entity_template* array;
size_t count;
};
struct EntityManager
{
static EntityID ID;
Arena* Arena;
// TODO: Should be a pool
VectorArena<Entity, 1024> Entities;
VectorArena<Entity, 100'000> Entities;
typed_entity_array by_type[ENTITY(Count)];
};
void InitEntityManager(NonNullPtr<World> world);
void ShutdownEntityManager();
EntityManager& GetEntityManager();
void RegisterEntity(EntityManager& manager, Entity* entity);
void UpdateEntityManager(EntityManager& manager);
void InitEntityManager(NonNullPtr<World> world);
void ShutdownEntityManager();
EntityManager& GetEntityManager();
void Serialize(NonNullPtr<EntityManager> entityManager);
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity);
void UpdateEntityManager(EntityManager& manager);