preparing serialization of entities

This commit is contained in:
2026-09-06 12:08:31 -04:00
parent d7825270ee
commit 62e6284976
11 changed files with 138 additions and 62 deletions
+52 -12
View File
@@ -7,6 +7,7 @@
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Thread/ThreadContext.h>
#include <Entity/EntityManager.h>
#include <Graphics/MeshRenderer.h>
#ifdef JULIET_ENABLE_IMGUI
@@ -30,41 +31,80 @@ void AddToWorld(NonNullPtr<World> world, NonNullPtr<Entity> entity)
void RemoveWorldEntity(World& world, size_t index) {}
void Serialize(archive& data, World& world, String filename)
void Serialize(archive& ar, World& world, String filename)
{
Assert(IsValid(filename));
TempArena temp = scratch_begin(nullptr, 0);
auto& entityManager = GetEntityManager();
if (data.loading)
if (ar.loading)
{
// Load
ByteBuffer fileBuffer = LoadFile(temp.Arena, filename);
ByteBuffer fileBuffer = LoadFile(ar.arena, filename);
if (fileBuffer.Size >= sizeof(WorldFileHeader))
{
ar.base_ptr = fileBuffer.Data;
WorldFileHeader header;
serialize_elem(ar, header);
Assert(header.Magic == kWorldMagic);
Assert(header.Version == kWorldVersion);
for (typed_entity_array& type : entityManager.by_type)
{
serialize_elem(ar, type.count);
if (type.count > 0)
{
// Unserialize the base entity to get informations
Entity entity;
serialize(ar, &entity);
}
}
}
}
else
{
// Save
IOStream* stream = IOFromFile(temp.Arena, filename, WrapString("wb"));
IOStream* stream = IOFromFile(ar.arena, filename, WrapString("wb"));
index_t beginPos = ArenaPos(temp.Arena);
index_t beginPos = ArenaPos(ar.arena);
// Headers
auto* header = ArenaPushStruct<WorldFileHeader>(temp.Arena);
auto* header = ArenaPushStruct<WorldFileHeader>(ar.arena);
header->Magic = kWorldMagic;
header->Version = kWorldVersion;
index_t endPos = ArenaPos(temp.Arena);
ar.base_ptr = header;
// TODO : Move to a one file per entity model
for (typed_entity_array& type : entityManager.by_type)
{
serialize_elem(ar, type.count);
auto* element = type.array;
uint8* rawElement = reinterpret_cast<uint8*>(element);
size_t stride = element->Base->Kind->size_of;
for (index_t idx = 0; idx < type.count; ++idx)
{
// Todo : utils
// Get base entity from type
Entity* entity = reinterpret_cast<entity_template*>(rawElement + (idx * stride))->Base;
serialize(ar, entity);
}
}
// Write
index_t endPos = ArenaPos(ar.arena);
ByteBuffer writeBuffer = { .Data = reinterpret_cast<Byte*>(header), .Size = endPos - beginPos };
size_t written = IOWrite(stream, writeBuffer);
size_t written = IOWrite(stream, writeBuffer);
Assert(writeBuffer.Size == written);
IOClose(stream);
}
scratch_end(temp);
//
// uint32 entityCount = static_cast<uint32>(world.Entities.Size());
// size_t totalBytes = sizeof(WorldFileHeader) + static_cast<size_t>(entityCount) * sizeof(WorldEntityDiskRecord);
@@ -196,13 +236,13 @@ void RenderWorldEditorUI(World& world)
if (ImGui::Button("Save World"))
{
archive data{ .loading = false };
archive data{ .arena = temp.Arena, .loading = false };
Serialize(data, world, path);
}
ImGui::SameLine();
if (ImGui::Button("Load World"))
{
archive data{ .loading = true };
archive data{ .arena = temp.Arena, .loading = true };
Serialize(data, world, path);
}
-7
View File
@@ -16,13 +16,6 @@ struct WorldFileHeader
uint32 Magic = 0x444C574A; // 'JWLD' in little-endian
uint32 Version = 1;
};
struct WorldEntityDiskRecord
{
float X = 0.0f;
float Y = 0.0f;
float Z = 0.0f;
};
#pragma pack(pop)
constexpr uint32 kWorldMagic = 0x444C574A; // 'JWLD'
+22
View File
@@ -1,3 +1,25 @@
#include <Entity/Entity.h>
#include <Core/Common/serialization.h>
DEFINE_ENTITY(Inert);
void serialize(archive& ar, NonNullPtr<Entity> entity)
{
serialize_elem(ar, entity->ID);
if (ar.loading)
{
serialize_elem(ar, entity->Kind->kind);
}
else
{
uint8 kind;
serialize_elem(ar, kind);
entity->Kind = kEntity_type_class_ptr[kind];
}
serialize_elem(ar, entity->X);
serialize_elem(ar, entity->Y);
serialize_elem(ar, entity->Z);
}
+34 -23
View File
@@ -7,24 +7,41 @@
#include <Engine/Class.h>
#define DECLARE_ENTITY() \
Entity* Base; \
static const Class* Kind;
Entity* Base; \
static Class* Kind;
// Will register the class globally at launch
#define DEFINE_ENTITY(entity) \
constexpr Class entityKind##entity = \
Class entityKind##entity = \
MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, sizeof(entity), alignof(entity), nullptr); \
const Class* entity::Kind = &entityKind##entity;
Class* entity::Kind = &entityKind##entity;
#define DEFINE_ENTITY_SERIALIZED(entity, serialize_fct) \
constexpr Class entityKind##entity = \
Class entityKind##entity = \
MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, sizeof(entity), alignof(entity), serialize_fct); \
const Class* entity::Kind = &entityKind##entity;
Class* entity::Kind = &entityKind##entity;
struct EntityManager;
using DerivedType = void*;
using EntityID = uint64_t;
struct Entity final
{
EntityID ID = 0;
Class* Kind = nullptr;
DerivedType Derived = nullptr;
float X = 0.0f;
float Y = 0.0f;
float Z = 0.0f;
};
struct Inert
{
DECLARE_ENTITY()
index_t MeshInstance = indexMax;
};
// clang-format off
#define ENTITY_TYPE_LIST(X) X(Inert),
@@ -41,33 +58,25 @@ inline const char* kEntity_type_names[] = {
ENTITY_TYPE_LIST(AS_STR)
"Count"
};
#undef AS_STR
#define AS_CLASS(name) name::Kind
inline Class* kEntity_type_class_ptr[]
{
ENTITY_TYPE_LIST(AS_CLASS)
nullptr
};
#undef AS_CLASS
#define ENTITY(kind) ToUnderlying(Entity_Type::kind)
// clang-format on
struct Entity final
{
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) {
@@ -105,3 +114,5 @@ template <typename EntityType>
Assert(IsA<EntityType>(entity));
return static_cast<EntityType*>(entity->Derived);
}
void serialize(archive& ar, NonNullPtr<Entity> entity);
+12 -3
View File
@@ -28,7 +28,13 @@ void InitEntityManager(NonNullPtr<World> world)
void ShutdownEntityManager()
{
GetEntityManager().Entities.Destroy();
auto& manager = GetEntityManager();
for (typed_entity_array& type : manager.by_type)
{
ArenaRelease(type.arena);
}
manager.Entities.Destroy();
}
EntityManager& GetEntityManager()
@@ -37,8 +43,6 @@ EntityManager& GetEntityManager()
return *entityManager;
}
void Serialize(NonNullPtr<EntityManager> entityManager) {}
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity)
{
base->ID = EntityManager::ID++;
@@ -61,6 +65,11 @@ entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedTyp
return ptr;
}
void RegisterBaseEntity(EntityManager& manager, Entity&& base)
{
GetEntityManager().Entities.PushBack(std::move(base));
}
void UpdateEntityManager(EntityManager& manager)
{
// Todo : inert by definition dont move, but this is for test
+1 -1
View File
@@ -27,6 +27,6 @@ struct EntityManager
void InitEntityManager(NonNullPtr<World> world);
void ShutdownEntityManager();
EntityManager& GetEntityManager();
void Serialize(NonNullPtr<EntityManager> entityManager);
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity);
void RegisterBaseEntity(EntityManager& manager, Entity&& base);
void UpdateEntityManager(EntityManager& manager);
-7
View File
@@ -5,13 +5,6 @@
struct EntityManager;
struct SerializedEntityTest
{
DECLARE_ENTITY()
int A = 0;
};
enum class GameMode
{
Editor,