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:
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Entity/Entity.h>
|
||||
|
||||
struct StaticMesh
|
||||
{
|
||||
DECLARE_ENTITY()
|
||||
};
|
||||
DEFINE_ENTITY(StaticMesh);
|
||||
@@ -0,0 +1,278 @@
|
||||
#include <Data/World.h>
|
||||
|
||||
#include <Core/Common/CoreUtils.h>
|
||||
#include <Core/Common/serialization.h>
|
||||
#include <Core/HAL/Filesystem/Filesystem.h>
|
||||
#include <Core/HAL/IO/IOStream.h>
|
||||
#include <Core/Logging/LogManager.h>
|
||||
#include <Core/Logging/LogTypes.h>
|
||||
#include <Core/Thread/ThreadContext.h>
|
||||
#include <Graphics/MeshRenderer.h>
|
||||
|
||||
#ifdef JULIET_ENABLE_IMGUI
|
||||
#include <imgui.h>
|
||||
#endif
|
||||
|
||||
void InitWorld(NonNullPtr<World> world, NonNullPtr<Arena> arena)
|
||||
{
|
||||
world->WorldArena = arena.Get();
|
||||
}
|
||||
|
||||
void ShutdownWorld(NonNullPtr<World> world)
|
||||
{
|
||||
world->WorldArena = nullptr;
|
||||
}
|
||||
|
||||
void AddToWorld(NonNullPtr<World> world, NonNullPtr<Entity> entity)
|
||||
{
|
||||
// RegisterEntity(*world->EntityManager, entity.Get());
|
||||
}
|
||||
|
||||
void RemoveWorldEntity(World& world, size_t index) {}
|
||||
|
||||
void Serialize(archive& data, World& world, String filename)
|
||||
{
|
||||
Assert(IsValid(filename));
|
||||
|
||||
TempArena temp = scratch_begin(nullptr, 0);
|
||||
|
||||
if (data.loading)
|
||||
{
|
||||
// Load
|
||||
ByteBuffer fileBuffer = LoadFile(temp.Arena, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Save
|
||||
IOStream* stream = IOFromFile(temp.Arena, filename, WrapString("wb"));
|
||||
|
||||
index_t beginPos = ArenaPos(temp.Arena);
|
||||
|
||||
// Headers
|
||||
auto* header = ArenaPushStruct<WorldFileHeader>(temp.Arena);
|
||||
header->Magic = kWorldMagic;
|
||||
header->Version = kWorldVersion;
|
||||
|
||||
index_t endPos = ArenaPos(temp.Arena);
|
||||
|
||||
// Write
|
||||
ByteBuffer writeBuffer = { .Data = reinterpret_cast<Byte*>(header), .Size = endPos - beginPos };
|
||||
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);
|
||||
//
|
||||
// ArenaParams tempParams = { .ReserveSize = Megabytes(1), .Name = "WorldSaveArena" };
|
||||
// Arena* tempArena = ArenaAllocate(tempParams);
|
||||
// if (!tempArena)
|
||||
// {
|
||||
// LogError(LogCategory::Game, "SaveWorld: Failed to allocate memory for saving world.");
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// auto deferRelease = Defer([&]() { ArenaRelease(tempArena); });
|
||||
//
|
||||
// uint8* bufferData = ArenaPushArray<uint8, false>(tempArena, totalBytes);
|
||||
// if (!bufferData)
|
||||
// {
|
||||
// LogError(LogCategory::Game, "SaveWorld: Failed to allocate buffer in arena.");
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// auto* header = reinterpret_cast<WorldFileHeader*>(bufferData);
|
||||
// header->Magic = kWorldMagic;
|
||||
// header->Version = kWorldVersion;
|
||||
// header->EntityCount = entityCount;
|
||||
// header->Reserved = 0;
|
||||
//
|
||||
// auto* records = reinterpret_cast<WorldEntityDiskRecord*>(bufferData + sizeof(WorldFileHeader));
|
||||
// for (size_t i = 0; i < entityCount; ++i)
|
||||
// {
|
||||
// const Entity& ent = world.Entities[i];
|
||||
// records[i].X = ent.X;
|
||||
// records[i].Y = ent.Y;
|
||||
// records[i].Z = ent.Z;
|
||||
// }
|
||||
//
|
||||
// IOStream* stream = IOFromFile(tempArena, filename, WrapString("wb"));
|
||||
// if (!stream)
|
||||
// {
|
||||
// LogError(LogCategory::Game, "SaveWorld: Failed to open file for writing: %s", CStr(filename));
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// ByteBuffer writeBuffer = { .Data = reinterpret_cast<Byte*>(bufferData), .Size = totalBytes };
|
||||
//
|
||||
// size_t written = IOWrite(stream, writeBuffer);
|
||||
// IOClose(stream);
|
||||
//
|
||||
// if (written != totalBytes)
|
||||
// {
|
||||
// LogError(LogCategory::Game, "SaveWorld: Failed to write complete world data to %s (wrote %zu / %zu bytes)",
|
||||
// CStr(filename), written, totalBytes);
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// LogMessage(LogCategory::Game, "World successfully saved to %s (%u entities)", CStr(filename), entityCount);
|
||||
// return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool LoadWorld(World& world, String filename)
|
||||
{
|
||||
Assert(IsValid(filename));
|
||||
// Assert(world.WorldArena != nullptr);
|
||||
//
|
||||
// TempArena loadArena = scratch_begin(nullptr, 0);
|
||||
//
|
||||
// ByteBuffer fileBuffer = LoadFile(loadArena.Arena, filename);
|
||||
// if (fileBuffer.Data && fileBuffer.Size < sizeof(WorldFileHeader))
|
||||
// {
|
||||
// const WorldFileHeader* header = reinterpret_cast<const WorldFileHeader*>(fileBuffer.Data);
|
||||
// const bool invalidMagicNum = header->Magic != kWorldMagic;
|
||||
// const bool invalidVersion = header->Version != kWorldVersion;
|
||||
// if (invalidMagicNum)
|
||||
// {
|
||||
// LogError(LogCategory::Game, "LoadWorld: Invalid magic in world file: %s (expected 0x%08X, got 0x%08X)",
|
||||
// CStr(filename), kWorldMagic, header->Magic);
|
||||
// }
|
||||
//
|
||||
// if (invalidVersion)
|
||||
// {
|
||||
// LogError(LogCategory::Game, "LoadWorld: Unsupported world file version: %u in %s", header->Version, CStr(filename));
|
||||
// }
|
||||
//
|
||||
// size_t expectedSize = sizeof(WorldFileHeader) + static_cast<size_t>(header->EntityCount) * sizeof(WorldEntityDiskRecord);
|
||||
// const bool invalidSize = fileBuffer.Size < expectedSize;
|
||||
// if (invalidSize)
|
||||
// {
|
||||
// LogError(LogCategory::Game, "LoadWorld: Corrupted file %s: size %zu < expected %zu for %u entities",
|
||||
// CStr(filename), fileBuffer.Size, expectedSize, header->EntityCount);
|
||||
// }
|
||||
//
|
||||
// if (!invalidMagicNum && !invalidVersion && !invalidSize)
|
||||
// {
|
||||
//
|
||||
// // world.Entities.Clear();
|
||||
// //
|
||||
// // const auto* records = reinterpret_cast<const WorldEntityDiskRecord*>(
|
||||
// // reinterpret_cast<const uint8*>(fileBuffer.Data) + sizeof(WorldFileHeader));
|
||||
// //
|
||||
// // for (uint32 i = 0; i < header->EntityCount; ++i)
|
||||
// // {
|
||||
// // Entity ent;
|
||||
// // (void)AddWorldEntity(world, records[i].X, records[i].Y, records[i].Z);
|
||||
// // }
|
||||
//
|
||||
// LogMessage(LogCategory::Game, "World successfully loaded from %s (%u entities)", CStr(filename), header->EntityCount);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// LogError(LogCategory::Game, "LoadWorld: Failed to read world file or file is too small: %s", CStr(filename));
|
||||
// }
|
||||
//
|
||||
// scratch_end(loadArena);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef JULIET_EDITOR
|
||||
void RenderWorldEditorUI(World& world)
|
||||
{
|
||||
if (ImGui::Begin("World Editor"))
|
||||
{
|
||||
TempArena temp = scratch_begin(nullptr, 0);
|
||||
static char worldFilePath[256] = "../world.bin";
|
||||
ImGui::InputText("World File", worldFilePath, sizeof(worldFilePath));
|
||||
|
||||
String path = GetAssetPath(temp.Arena, WrapString(worldFilePath));
|
||||
|
||||
if (ImGui::Button("Save World"))
|
||||
{
|
||||
archive data{ .loading = false };
|
||||
Serialize(data, world, path);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Load World"))
|
||||
{
|
||||
archive data{ .loading = true };
|
||||
Serialize(data, world, path);
|
||||
}
|
||||
|
||||
scratch_end(temp);
|
||||
|
||||
// ImGui::SameLine();
|
||||
// if (ImGui::Button("Clear All"))
|
||||
// {
|
||||
// ClearWorld(world);
|
||||
// }
|
||||
//
|
||||
// ImGui::Separator();
|
||||
//
|
||||
// if (ImGui::Button("Add Entity"))
|
||||
// {
|
||||
// (void)AddWorldEntity(world, 0.0f, 0.0f, 0.0f);
|
||||
// }
|
||||
//
|
||||
// ImGui::Text("Entity Count: %zu", world.Entities.Size());
|
||||
// ImGui::Separator();
|
||||
//
|
||||
// static int selectedEntity = -1;
|
||||
// if (selectedEntity >= static_cast<int>(world.Entities.Size()))
|
||||
// {
|
||||
// selectedEntity = -1;
|
||||
// }
|
||||
//
|
||||
// ImGui::BeginChild("EntityList", ImVec2(180, 200), true);
|
||||
// for (size_t i = 0; i < world.Entities.Size(); ++i)
|
||||
// {
|
||||
// char label[64];
|
||||
// snprintf(label, sizeof(label), "Entity #%zu (ID: %llu)", i, world.Entities[i].ID);
|
||||
// if (ImGui::Selectable(label, selectedEntity == static_cast<int>(i)))
|
||||
// {
|
||||
// selectedEntity = static_cast<int>(i);
|
||||
// }
|
||||
// }
|
||||
// ImGui::EndChild();
|
||||
//
|
||||
// ImGui::SameLine();
|
||||
//
|
||||
// ImGui::BeginChild("EntityInspector", ImVec2(0, 200), true);
|
||||
// if (selectedEntity >= 0 && selectedEntity < static_cast<int>(world.Entities.Size()))
|
||||
// {
|
||||
// Entity& ent = world.Entities[static_cast<size_t>(selectedEntity)];
|
||||
// ImGui::Text("Selected: Entity #%d", selectedEntity);
|
||||
// ImGui::Text("ID: %llu", ent.ID);
|
||||
//
|
||||
// float pos[3] = { ent.X, ent.Y, ent.Z };
|
||||
// if (ImGui::DragFloat3("Position", pos, 0.1f))
|
||||
// {
|
||||
// ent.X = pos[0];
|
||||
// ent.Y = pos[1];
|
||||
// ent.Z = pos[2];
|
||||
// UpdateWorld(world);
|
||||
// }
|
||||
//
|
||||
// if (ImGui::Button("Delete Entity"))
|
||||
// {
|
||||
// RemoveWorldEntity(world, static_cast<size_t>(selectedEntity));
|
||||
// selectedEntity = -1;
|
||||
// UpdateWorld(world);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ImGui::Text("Select an entity to edit its properties.");
|
||||
// }
|
||||
// ImGui::EndChild();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Common/CoreTypes.h>
|
||||
#include <Core/Common/NonNullPtr.h>
|
||||
#include <Core/Common/String.h>
|
||||
#include <Core/Container/Vector.h>
|
||||
#include <Core/Memory/MemoryArena.h>
|
||||
#include <Entity/Entity.h>
|
||||
|
||||
struct archive;
|
||||
struct EntityManager;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
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'
|
||||
constexpr uint32 kWorldVersion = 1;
|
||||
|
||||
struct World
|
||||
{
|
||||
Arena* WorldArena = nullptr;
|
||||
EntityManager* EntityManager = nullptr;
|
||||
};
|
||||
|
||||
void InitWorld(NonNullPtr<World> world, NonNullPtr<Arena> arena);
|
||||
void ShutdownWorld(NonNullPtr<World> world);
|
||||
|
||||
void AddToWorld(NonNullPtr<World> world, NonNullPtr<Entity> entity);
|
||||
void RemoveWorldEntity(World& world, size_t index);
|
||||
|
||||
void Serialize(archive& data, World& world, String filename);
|
||||
|
||||
#if JULIET_EDITOR
|
||||
void RenderWorldEditorUI(World& world);
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#include <Entity/Entity.h>
|
||||
|
||||
DEFINE_ENTITY(Inert);
|
||||
+66
-27
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <UnitTest/WorldUnitTest.h>
|
||||
|
||||
#if JULIET_DEBUG
|
||||
|
||||
#include <Core/Common/CoreUtils.h>
|
||||
#include <Core/HAL/IO/IOStream.h>
|
||||
#include <Core/Logging/LogManager.h>
|
||||
#include <Core/Logging/LogTypes.h>
|
||||
#include <Core/Memory/MemoryArena.h>
|
||||
#include <Data/World.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
void WorldUnitTest()
|
||||
{
|
||||
LogMessage(LogCategory::Game, "Running World Unit Tests...");
|
||||
}
|
||||
} // namespace UnitTest
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Juliet.h>
|
||||
|
||||
#if JULIET_DEBUG
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
void WorldUnitTest();
|
||||
} // namespace UnitTest
|
||||
|
||||
#endif
|
||||
+38
-9
@@ -1,12 +1,13 @@
|
||||
#include <game.h>
|
||||
#include <game.h>
|
||||
|
||||
#include <Controller/DebugCameraController.h>
|
||||
#include <Core/Common/serialization.h>
|
||||
#include <Core/HAL/Filesystem/Filesystem.h>
|
||||
#include <Core/HAL/Keyboard/Keyboard.h>
|
||||
#include <Core/JulietInit.h>
|
||||
#include <Core/Logging/LogManager.h>
|
||||
#include <Core/Memory/MemoryArena.h>
|
||||
#include <Data/StaticMesh.h>
|
||||
#include <Data/World.h>
|
||||
#include <Entity/Entity.h>
|
||||
#include <Entity/EntityManager.h>
|
||||
#include <Graphics/Camera.h>
|
||||
@@ -15,8 +16,20 @@
|
||||
|
||||
#if JULIET_DEBUG
|
||||
#include <Debug/DebugTopBar.h>
|
||||
#include <UnitTest/WorldUnitTest.h>
|
||||
#endif
|
||||
|
||||
// namespace
|
||||
// {
|
||||
// void serialize_test(archive* ar, void* payload)
|
||||
// {
|
||||
// SerializedEntityTest* test = reinterpret_cast<SerializedEntityTest*>(payload);
|
||||
// serialize_elem(ar, test->A);
|
||||
// }
|
||||
// } // namespace
|
||||
//
|
||||
// DEFINE_ENTITY_SERIALIZED(SerializedEntityTest, serialize_test)
|
||||
|
||||
namespace
|
||||
{
|
||||
GameState* gGameState = nullptr;
|
||||
@@ -31,13 +44,16 @@ extern "C" JULIET_API void __cdecl GameShutdown()
|
||||
{
|
||||
printf("Shutting down game...\n");
|
||||
|
||||
if (gGameState && gGameState->World)
|
||||
{
|
||||
ShutdownWorld(gGameState->World);
|
||||
}
|
||||
|
||||
ShutdownEntityManager();
|
||||
}
|
||||
|
||||
extern "C" JULIET_API void __cdecl GameUpdate(GameData* params, [[maybe_unused]] float deltaTime)
|
||||
{
|
||||
|
||||
gGameState = params->GameState;
|
||||
if (!gGameState)
|
||||
{
|
||||
@@ -55,20 +71,29 @@ extern "C" JULIET_API void __cdecl GameUpdate(GameData* params, [[maybe_unused]]
|
||||
ReserveCamera(4);
|
||||
|
||||
// Bootstrap world
|
||||
auto* worldArena = ArenaAllocate({ .ReserveSize = Megabytes(1), .Name = "World Arena" });
|
||||
World* world = ArenaPushStruct<World>(worldArena JULIET_DEBUG_PARAM("World"));
|
||||
gameState->World = world;
|
||||
gameState->World->WorldArena = worldArena;
|
||||
auto* worldArena = ArenaAllocate({ .ReserveSize = Megabytes(1), .Name = "World Arena" });
|
||||
World* world = ArenaPushStruct<World>(worldArena JULIET_DEBUG_PARAM("World"));
|
||||
gameState->World = world;
|
||||
InitWorld(gameState->World, worldArena);
|
||||
|
||||
#if JULIET_DEBUG
|
||||
UnitTest::WorldUnitTest();
|
||||
#endif
|
||||
|
||||
// Entity Use case
|
||||
InitEntityManager(gameState->World);
|
||||
auto& manager = GetEntityManager();
|
||||
|
||||
NonNullPtr mesh = MakeEntity<StaticMesh>(manager, 1.f, 2.f);
|
||||
NonNullPtr mesh = MakeEntity<Inert>(manager, 1.f, 2.f, 0.f);
|
||||
|
||||
MeshAssetID cubeAsset = GetCubePrimitiveMeshAssetID();
|
||||
MeshInstanceID meshInst = CreateMeshInstance(cubeAsset, 0, MatrixIdentity());
|
||||
mesh->Base->MeshInstance = meshInst;
|
||||
mesh->MeshInstance = meshInst;
|
||||
|
||||
NonNullPtr mesh2 = MakeEntity<Inert>(manager, 4.f, 0.f, 2.f);
|
||||
|
||||
MeshInstanceID meshInst2 = CreateMeshInstance(cubeAsset, 0, MatrixIdentity());
|
||||
mesh2->MeshInstance = meshInst2;
|
||||
|
||||
// Summer at 2pm lighting
|
||||
Vector3 sunDirection = { -0.2f, -0.9f, -0.3f };
|
||||
@@ -100,6 +125,10 @@ extern "C" JULIET_API void __cdecl GameUpdate(GameData* params, [[maybe_unused]]
|
||||
{
|
||||
gGameState->Mode = GameMode::Play;
|
||||
}
|
||||
|
||||
#if JULIET_EDITOR
|
||||
RenderWorldEditorUI(*gGameState->World);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gGameState->Mode == GameMode::Play)
|
||||
|
||||
+11
-9
@@ -1,13 +1,15 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <Core/Memory/MemoryArena.h>
|
||||
#include <Data/World.h>
|
||||
|
||||
struct EntityManager;
|
||||
|
||||
struct World
|
||||
struct SerializedEntityTest
|
||||
{
|
||||
Arena* WorldArena;
|
||||
EntityManager* EntityManager;
|
||||
DECLARE_ENTITY()
|
||||
|
||||
int A = 0;
|
||||
};
|
||||
|
||||
enum class GameMode
|
||||
@@ -19,14 +21,14 @@ enum class GameMode
|
||||
|
||||
struct GameState
|
||||
{
|
||||
Arena* TotalArena;
|
||||
Arena* TotalArena = nullptr;
|
||||
|
||||
World* World;
|
||||
World* World = nullptr;
|
||||
|
||||
GameMode Mode = GameMode::Editor;
|
||||
|
||||
float TotalTime;
|
||||
int Score;
|
||||
float TotalTime = 0.0f;
|
||||
int Score = 0;
|
||||
};
|
||||
|
||||
extern GameState* GetGameState();
|
||||
[[nodiscard]] extern GameState* GetGameState();
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
struct Arena;
|
||||
|
||||
struct archive
|
||||
{
|
||||
Arena* arena;
|
||||
bool loading;
|
||||
};
|
||||
|
||||
void serialize(archive* ar, void* data, size_t size);
|
||||
|
||||
#define serialize_elem(ar, val) serialize((ar), &(val), sizeof(val))
|
||||
@@ -16,6 +16,6 @@ void thread_context_release(NonNullPtr<thread_context> ctx);
|
||||
void thread_context_select(NonNullPtr<thread_context> ctx);
|
||||
thread_context* thread_context_current();
|
||||
|
||||
Arena* thread_context_get_scratch(Arena** conflicts, size_t count);
|
||||
TempArena scratch_begin(Arena** conflicts, size_t count);
|
||||
void scratch_end(TempArena scratch);
|
||||
Arena* thread_context_get_scratch(Arena** conflicts, size_t count);
|
||||
JULIET_API TempArena scratch_begin(Arena** conflicts, size_t count);
|
||||
JULIET_API void scratch_end(TempArena scratch);
|
||||
|
||||
@@ -1,27 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Common/CRC32.h>
|
||||
#include <Entity/Entity.h>
|
||||
#include <Juliet.h>
|
||||
|
||||
struct archive;
|
||||
|
||||
using serialize_fct_type = void (*)(archive*, void* payload);
|
||||
|
||||
struct Class
|
||||
{
|
||||
uint32 CRC;
|
||||
uint8 kind;
|
||||
|
||||
serialize_fct_type serialize_fct;
|
||||
size_t size_of;
|
||||
size_t alignment;
|
||||
|
||||
#if JULIET_DEBUG
|
||||
String Name;
|
||||
#endif
|
||||
};
|
||||
|
||||
consteval Class MakeClass(String name, uint8 kind, size_t size, size_t align, serialize_fct_type fct)
|
||||
{
|
||||
Class cls = {};
|
||||
cls.CRC = crc32(name.Str, name.Size);
|
||||
cls.kind = kind;
|
||||
cls.size_of = size;
|
||||
cls.alignment = align;
|
||||
cls.serialize_fct = fct;
|
||||
|
||||
#if JULIET_DEBUG
|
||||
// TODO: string struct may be
|
||||
const char* Name;
|
||||
size_t Name_Length;
|
||||
cls.Name = name;
|
||||
#endif
|
||||
|
||||
consteval Class(const char* className, size_t name_length)
|
||||
{
|
||||
CRC = crc32(className, name_length);
|
||||
#if JULIET_DEBUG
|
||||
// TODO: string struct may be
|
||||
Name = className;
|
||||
Name_Length = name_length;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
return cls;
|
||||
}
|
||||
|
||||
template <typename type>
|
||||
bool IsA(Class& cls)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#define JULIET_DEBUG_ONLY(...) __VA_ARGS__
|
||||
#define JULIET_DEBUG_PARAM_FIRST(...) __VA_ARGS__
|
||||
#define JULIET_DEBUG_PARAM(...) , __VA_ARGS__
|
||||
#define JULIET_EDITOR 1
|
||||
#else
|
||||
#define JULIET_DEBUG 0
|
||||
#define JULIET_DEBUG_ONLY(...)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <Core/Common/serialization.h>
|
||||
|
||||
#include <Core/Common/CoreUtils.h>
|
||||
#include <Core/Memory/MemoryArena.h>
|
||||
#include <Core/Memory/Utils.h>
|
||||
|
||||
void serialize(archive* ar, void* data, size_t size)
|
||||
{
|
||||
if (ar->loading)
|
||||
{
|
||||
auto* ptr = ArenaPushSize(ar->arena, size, 8, false JULIET_DEBUG_PARAM("serialized load field"));
|
||||
MemCopy(data, ptr, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* ptr = ArenaPushSize(ar->arena, size, 8, false JULIET_DEBUG_PARAM("serialized save field"));
|
||||
MemCopy(ptr, data, size);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ String GetAssetBasePath()
|
||||
|
||||
[[nodiscard]] String GetAssetPath(NonNullPtr<Arena> arena, String filename)
|
||||
{
|
||||
// TODO: Path is Assets/Compiled, we need to fix that one day
|
||||
Assert(IsValid(CachedAssetBasePath));
|
||||
Assert(IsValid(filename));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user