preparing serialization of entities
This commit is contained in:
+52
-12
@@ -7,6 +7,7 @@
|
|||||||
#include <Core/Logging/LogManager.h>
|
#include <Core/Logging/LogManager.h>
|
||||||
#include <Core/Logging/LogTypes.h>
|
#include <Core/Logging/LogTypes.h>
|
||||||
#include <Core/Thread/ThreadContext.h>
|
#include <Core/Thread/ThreadContext.h>
|
||||||
|
#include <Entity/EntityManager.h>
|
||||||
#include <Graphics/MeshRenderer.h>
|
#include <Graphics/MeshRenderer.h>
|
||||||
|
|
||||||
#ifdef JULIET_ENABLE_IMGUI
|
#ifdef JULIET_ENABLE_IMGUI
|
||||||
@@ -30,41 +31,80 @@ void AddToWorld(NonNullPtr<World> world, NonNullPtr<Entity> entity)
|
|||||||
|
|
||||||
void RemoveWorldEntity(World& world, size_t index) {}
|
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));
|
Assert(IsValid(filename));
|
||||||
|
|
||||||
TempArena temp = scratch_begin(nullptr, 0);
|
auto& entityManager = GetEntityManager();
|
||||||
|
|
||||||
if (data.loading)
|
if (ar.loading)
|
||||||
{
|
{
|
||||||
// Load
|
// 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
|
else
|
||||||
{
|
{
|
||||||
// Save
|
// 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
|
// Headers
|
||||||
auto* header = ArenaPushStruct<WorldFileHeader>(temp.Arena);
|
auto* header = ArenaPushStruct<WorldFileHeader>(ar.arena);
|
||||||
header->Magic = kWorldMagic;
|
header->Magic = kWorldMagic;
|
||||||
header->Version = kWorldVersion;
|
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
|
// Write
|
||||||
|
index_t endPos = ArenaPos(ar.arena);
|
||||||
ByteBuffer writeBuffer = { .Data = reinterpret_cast<Byte*>(header), .Size = endPos - beginPos };
|
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);
|
Assert(writeBuffer.Size == written);
|
||||||
|
|
||||||
IOClose(stream);
|
IOClose(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
scratch_end(temp);
|
|
||||||
//
|
//
|
||||||
// uint32 entityCount = static_cast<uint32>(world.Entities.Size());
|
// uint32 entityCount = static_cast<uint32>(world.Entities.Size());
|
||||||
// size_t totalBytes = sizeof(WorldFileHeader) + static_cast<size_t>(entityCount) * sizeof(WorldEntityDiskRecord);
|
// 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"))
|
if (ImGui::Button("Save World"))
|
||||||
{
|
{
|
||||||
archive data{ .loading = false };
|
archive data{ .arena = temp.Arena, .loading = false };
|
||||||
Serialize(data, world, path);
|
Serialize(data, world, path);
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("Load World"))
|
if (ImGui::Button("Load World"))
|
||||||
{
|
{
|
||||||
archive data{ .loading = true };
|
archive data{ .arena = temp.Arena, .loading = true };
|
||||||
Serialize(data, world, path);
|
Serialize(data, world, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,6 @@ struct WorldFileHeader
|
|||||||
uint32 Magic = 0x444C574A; // 'JWLD' in little-endian
|
uint32 Magic = 0x444C574A; // 'JWLD' in little-endian
|
||||||
uint32 Version = 1;
|
uint32 Version = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WorldEntityDiskRecord
|
|
||||||
{
|
|
||||||
float X = 0.0f;
|
|
||||||
float Y = 0.0f;
|
|
||||||
float Z = 0.0f;
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
constexpr uint32 kWorldMagic = 0x444C574A; // 'JWLD'
|
constexpr uint32 kWorldMagic = 0x444C574A; // 'JWLD'
|
||||||
|
|||||||
@@ -1,3 +1,25 @@
|
|||||||
#include <Entity/Entity.h>
|
#include <Entity/Entity.h>
|
||||||
|
|
||||||
|
#include <Core/Common/serialization.h>
|
||||||
|
|
||||||
DEFINE_ENTITY(Inert);
|
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
@@ -7,24 +7,41 @@
|
|||||||
#include <Engine/Class.h>
|
#include <Engine/Class.h>
|
||||||
|
|
||||||
#define DECLARE_ENTITY() \
|
#define DECLARE_ENTITY() \
|
||||||
Entity* Base; \
|
Entity* Base; \
|
||||||
static const Class* Kind;
|
static Class* Kind;
|
||||||
|
|
||||||
// Will register the class globally at launch
|
// Will register the class globally at launch
|
||||||
#define DEFINE_ENTITY(entity) \
|
#define DEFINE_ENTITY(entity) \
|
||||||
constexpr Class entityKind##entity = \
|
Class entityKind##entity = \
|
||||||
MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, sizeof(entity), alignof(entity), nullptr); \
|
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) \
|
#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); \
|
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;
|
struct EntityManager;
|
||||||
using DerivedType = void*;
|
using DerivedType = void*;
|
||||||
using EntityID = uint64_t;
|
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
|
// clang-format off
|
||||||
#define ENTITY_TYPE_LIST(X) X(Inert),
|
#define ENTITY_TYPE_LIST(X) X(Inert),
|
||||||
|
|
||||||
@@ -41,33 +58,25 @@ inline const char* kEntity_type_names[] = {
|
|||||||
ENTITY_TYPE_LIST(AS_STR)
|
ENTITY_TYPE_LIST(AS_STR)
|
||||||
"Count"
|
"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)
|
#define ENTITY(kind) ToUnderlying(Entity_Type::kind)
|
||||||
// clang-format on
|
// 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
|
// Can reinterpret cast to this to have the offset of Base and Kind for any entity
|
||||||
struct entity_template
|
struct entity_template
|
||||||
{
|
{
|
||||||
DECLARE_ENTITY();
|
DECLARE_ENTITY();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Inert
|
|
||||||
{
|
|
||||||
DECLARE_ENTITY()
|
|
||||||
|
|
||||||
index_t MeshInstance = indexMax;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
template <typename EntityType>
|
template <typename EntityType>
|
||||||
concept EntityConcept = requires(EntityType entity) {
|
concept EntityConcept = requires(EntityType entity) {
|
||||||
@@ -105,3 +114,5 @@ template <typename EntityType>
|
|||||||
Assert(IsA<EntityType>(entity));
|
Assert(IsA<EntityType>(entity));
|
||||||
return static_cast<EntityType*>(entity->Derived);
|
return static_cast<EntityType*>(entity->Derived);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serialize(archive& ar, NonNullPtr<Entity> entity);
|
||||||
|
|||||||
@@ -28,7 +28,13 @@ void InitEntityManager(NonNullPtr<World> world)
|
|||||||
|
|
||||||
void ShutdownEntityManager()
|
void ShutdownEntityManager()
|
||||||
{
|
{
|
||||||
GetEntityManager().Entities.Destroy();
|
auto& manager = GetEntityManager();
|
||||||
|
for (typed_entity_array& type : manager.by_type)
|
||||||
|
{
|
||||||
|
ArenaRelease(type.arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.Entities.Destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityManager& GetEntityManager()
|
EntityManager& GetEntityManager()
|
||||||
@@ -37,8 +43,6 @@ EntityManager& GetEntityManager()
|
|||||||
return *entityManager;
|
return *entityManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Serialize(NonNullPtr<EntityManager> entityManager) {}
|
|
||||||
|
|
||||||
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity)
|
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity)
|
||||||
{
|
{
|
||||||
base->ID = EntityManager::ID++;
|
base->ID = EntityManager::ID++;
|
||||||
@@ -61,6 +65,11 @@ entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedTyp
|
|||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RegisterBaseEntity(EntityManager& manager, Entity&& base)
|
||||||
|
{
|
||||||
|
GetEntityManager().Entities.PushBack(std::move(base));
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateEntityManager(EntityManager& manager)
|
void UpdateEntityManager(EntityManager& manager)
|
||||||
{
|
{
|
||||||
// Todo : inert by definition dont move, but this is for test
|
// Todo : inert by definition dont move, but this is for test
|
||||||
|
|||||||
@@ -27,6 +27,6 @@ struct EntityManager
|
|||||||
void InitEntityManager(NonNullPtr<World> world);
|
void InitEntityManager(NonNullPtr<World> world);
|
||||||
void ShutdownEntityManager();
|
void ShutdownEntityManager();
|
||||||
EntityManager& GetEntityManager();
|
EntityManager& GetEntityManager();
|
||||||
void Serialize(NonNullPtr<EntityManager> entityManager);
|
|
||||||
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity);
|
entity_template* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity);
|
||||||
|
void RegisterBaseEntity(EntityManager& manager, Entity&& base);
|
||||||
void UpdateEntityManager(EntityManager& manager);
|
void UpdateEntityManager(EntityManager& manager);
|
||||||
|
|||||||
@@ -5,13 +5,6 @@
|
|||||||
|
|
||||||
struct EntityManager;
|
struct EntityManager;
|
||||||
|
|
||||||
struct SerializedEntityTest
|
|
||||||
{
|
|
||||||
DECLARE_ENTITY()
|
|
||||||
|
|
||||||
int A = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GameMode
|
enum class GameMode
|
||||||
{
|
{
|
||||||
Editor,
|
Editor,
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Juliet.h>
|
||||||
|
|
||||||
struct Arena;
|
struct Arena;
|
||||||
|
|
||||||
struct archive
|
struct archive
|
||||||
{
|
{
|
||||||
Arena* arena;
|
Arena* arena;
|
||||||
bool loading;
|
void* base_ptr;
|
||||||
|
index_t offset;
|
||||||
|
bool loading;
|
||||||
};
|
};
|
||||||
|
|
||||||
void serialize(archive* ar, void* data, size_t size);
|
JULIET_API void serialize(archive& ar, void* data, size_t size);
|
||||||
|
|
||||||
#define serialize_elem(ar, val) serialize((ar), &(val), sizeof(val))
|
#define serialize_elem(ar, val) serialize((ar), &(val), sizeof(val))
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Core/Common/CRC32.h>
|
|
||||||
#include <Entity/Entity.h>
|
|
||||||
#include <Juliet.h>
|
#include <Juliet.h>
|
||||||
|
|
||||||
|
#include <Core/Common/CRC32.h>
|
||||||
|
#include <Core/Common/String.h>
|
||||||
|
|
||||||
struct archive;
|
struct archive;
|
||||||
|
|
||||||
using serialize_fct_type = void (*)(archive*, void* payload);
|
using serialize_fct_type = void (*)(archive*, void* payload);
|
||||||
|
|||||||
@@ -4,16 +4,18 @@
|
|||||||
#include <Core/Memory/MemoryArena.h>
|
#include <Core/Memory/MemoryArena.h>
|
||||||
#include <Core/Memory/Utils.h>
|
#include <Core/Memory/Utils.h>
|
||||||
|
|
||||||
void serialize(archive* ar, void* data, size_t size)
|
void serialize(archive& ar, void* data, size_t size)
|
||||||
{
|
{
|
||||||
if (ar->loading)
|
if (ar.loading)
|
||||||
{
|
{
|
||||||
auto* ptr = ArenaPushSize(ar->arena, size, 8, false JULIET_DEBUG_PARAM("serialized load field"));
|
uint8* ptr = static_cast<uint8*>(ar.base_ptr) + ar.offset;
|
||||||
MemCopy(data, ptr, size);
|
MemCopy(data, ptr, size);
|
||||||
|
ar.offset += size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto* ptr = ArenaPushSize(ar->arena, size, 8, false JULIET_DEBUG_PARAM("serialized save field"));
|
auto* ptr = ArenaPushSize(ar.arena, size, 8, false JULIET_DEBUG_PARAM("serialized save field"));
|
||||||
MemCopy(ptr, data, size);
|
MemCopy(ptr, data, size);
|
||||||
|
ar.offset += size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
#include <Engine/Class.h>
|
||||||
Reference in New Issue
Block a user