conversion to c by gemini 4 argon

This commit is contained in:
Patedam committed 2026-10-16 23:40:45 -04:00
1 parent b70e2d21d2
commit f9356f9ba7
369 files changed
+18074 -107634

No files matched your search

+4 -2
View File
@@ -1,4 +1,6 @@
#pragma once
constexpr index_t kPlayCamera = 0;
constexpr index_t kDebugCamera = 1;
#include <Juliet.h>
#define kPlayCamera ((index_t)0)
#define kDebugCamera ((index_t)1)
+163
View File
@@ -0,0 +1,163 @@
#include <Controller/DebugCameraController.h>
#include <Controller/ControllerUtils.h>
#include <Core/Common/CoreUtils.h>
#include <Core/HAL/Keyboard/Keyboard.h>
#include <Core/HAL/Mouse/Mouse.h>
#include <Core/Memory/MemoryArena.h>
#include <Graphics/Camera.h>
#include <dcimgui.h>
#include <math.h>
global bool8 gIsDebugCameraActive = false;
global index_t gPreviousCameraIndex = 0;
global float gPitch = 0.0f;
global float gYaw = 0.0f;
global bool8 gFirstUpdate = true;
global bool8 gIsFpsModeActive = false;
global bool8 gWasRightMouseButtonDown = false;
void ActivateDebugController(void)
{
Assert(gIsDebugCameraActive == false);
Camera* currentCam = GetCurrentCamera();
gPreviousCameraIndex = currentCam->Index;
SetCurrentCamera(kDebugCamera);
gIsDebugCameraActive = true;
gFirstUpdate = true;
gIsFpsModeActive = false;
gWasRightMouseButtonDown = false;
}
void DeactivateDebugController(void)
{
Assert(gIsDebugCameraActive);
gIsDebugCameraActive = false;
SetCurrentCamera(gPreviousCameraIndex);
}
bool8 IsDebugControllerActive(void)
{
return gIsDebugCameraActive;
}
void UpdateDebugController(float dt)
{
Camera* currentCam = GetCurrentCamera();
if (gFirstUpdate)
{
Vector3 origin = { 0.0f, 0.0f, 0.0f };
Vector3 dir = Vector3Sub(origin, currentCam->Position);
dir = Normalize(dir);
gPitch = asinf(dir.z);
gYaw = atan2f(dir.y, dir.x);
gFirstUpdate = false;
Vector3 forward = { cosf(gPitch) * cosf(gYaw), cosf(gPitch) * sinf(gYaw), sinf(gPitch) };
Vector3 worldUp = { 0.0f, 0.0f, 1.0f };
Vector3 right = Normalize(Cross(worldUp, forward));
currentCam->Target = Vector3Add(currentCam->Position, forward);
currentCam->Up = Cross(forward, right);
}
bool8 isRightMouseButtonDown = IsMouseButtonDown(MouseButton_Right);
if (isRightMouseButtonDown && !gWasRightMouseButtonDown)
{
gIsFpsModeActive = !gIsFpsModeActive;
}
gWasRightMouseButtonDown = isRightMouseButtonDown;
if (gIsFpsModeActive)
{
MousePosition mouseDelta = GetMouseDelta();
float sensitivity = 0.005f;
gYaw += (float)mouseDelta.X * sensitivity;
gPitch -= (float)mouseDelta.Y * sensitivity;
gPitch = Min(gPitch, 1.5f);
gPitch = Max(gPitch, -1.5f);
if (IsKeyDown(ScanCode_Q))
{
gYaw -= 2.0f * dt;
}
if (IsKeyDown(ScanCode_E))
{
gYaw += 2.0f * dt;
}
Vector3 forward = { cosf(gPitch) * cosf(gYaw), cosf(gPitch) * sinf(gYaw), sinf(gPitch) };
Vector3 worldUp = { 0.0f, 0.0f, 1.0f };
Vector3 right = Normalize(Cross(worldUp, forward));
Vector3 defaultUp = Cross(forward, right);
const float kMovementPerFrame = 10.f; // 10m/s
float speedPerFrame = kMovementPerFrame;
if (IsKeyDown(ScanCode_LeftShift))
{
speedPerFrame *= 10.f; // 100m/s
}
if (IsKeyDown(ScanCode_W))
{
currentCam->Position = Vector3Add(currentCam->Position, Vector3Scale(forward, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_S))
{
currentCam->Position = Vector3Sub(currentCam->Position, Vector3Scale(forward, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_D))
{
currentCam->Position = Vector3Add(currentCam->Position, Vector3Scale(right, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_A))
{
currentCam->Position = Vector3Sub(currentCam->Position, Vector3Scale(right, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_Space))
{
currentCam->Position = Vector3Add(currentCam->Position, Vector3Scale(defaultUp, speedPerFrame * dt));
}
if (IsKeyDown(ScanCode_LeftControl))
{
currentCam->Position = Vector3Sub(currentCam->Position, Vector3Scale(defaultUp, speedPerFrame * dt));
}
currentCam->Target = Vector3Add(currentCam->Position, forward);
currentCam->Up = defaultUp;
}
}
#if JULIET_DEBUG
void RenderImGuiDebugController(float dt)
{
Camera* currentCam = GetCurrentCamera();
ImGui_Text("Delta time: %f", (double)dt);
ImGui_Text("Camera:");
ImGui_Indent();
ImGui_BulletText("Index: %zu", (size_t)currentCam->Index);
ImGui_BulletText("Position: (%.3f, %.3f, %.3f)", (double)currentCam->Position.x, (double)currentCam->Position.y,
(double)currentCam->Position.z);
ImGui_BulletText("Target: (%.3f, %.3f, %.3f)", (double)currentCam->Target.x, (double)currentCam->Target.y,
(double)currentCam->Target.z);
ImGui_BulletText("Up: (%.3f, %.3f, %.3f)", (double)currentCam->Up.x, (double)currentCam->Up.y,
(double)currentCam->Up.z);
ImGui_BulletText("FOV: %.3f rad", (double)currentCam->FOV);
ImGui_BulletText("AspectRatio: %.3f", (double)currentCam->AspectRatio);
ImGui_BulletText("NearPlane: %.3f", (double)currentCam->NearPlane);
ImGui_BulletText("FarPlane: %.3f", (double)currentCam->FarPlane);
ImGui_BulletText("Pitch: %.3f, Yaw: %.3f", (double)gPitch, (double)gYaw);
ImGui_BulletText("FPS Mode: %s", gIsFpsModeActive ? "Active" : "Inactive");
ImGui_Unindent();
}
#endif
-170
View File
@@ -1,170 +0,0 @@
#include <Controller/DebugCameraController.h>
#include <Controller/ControllerUtils.h>
#include <Core/Common/CoreUtils.h>
#include <Core/HAL/Keyboard/Keyboard.h>
#include <Core/HAL/Mouse/Mouse.h>
#include <Graphics/Camera.h>
#include <imgui.h>
namespace
{
bool gIsDebugCameraActive = false;
index_t gPreviousCameraIndex = 0;
float gPitch = 0.0f;
float gYaw = 0.0f;
bool gFirstUpdate = true;
bool gIsFpsModeActive = false;
bool gWasRightMouseButtonDown = false;
} // namespace
void ActivateDebugController()
{
Assert(gIsDebugCameraActive == false);
Camera* currentCam = GetCurrentCamera();
gPreviousCameraIndex = currentCam->Index;
SetCurrentCamera(kDebugCamera);
gIsDebugCameraActive = true;
gFirstUpdate = true;
gIsFpsModeActive = false;
gWasRightMouseButtonDown = false;
}
void DeactivateDebugController()
{
Assert(gIsDebugCameraActive);
gIsDebugCameraActive = false;
SetCurrentCamera(gPreviousCameraIndex);
}
bool IsDebugControllerActive()
{
return gIsDebugCameraActive;
}
void UpdateDebugController(float dt)
{
Camera* currentCam = GetCurrentCamera();
if (gFirstUpdate)
{
Vector3 dir = Vector3{ 0.0f, 0.0f, 0.0f } - currentCam->Position;
dir = Normalize(dir);
gPitch = asinf(dir.z);
gYaw = atan2f(dir.y, dir.x);
gFirstUpdate = false;
Vector3 forward;
forward.x = cosf(gPitch) * cosf(gYaw);
forward.y = cosf(gPitch) * sinf(gYaw);
forward.z = sinf(gPitch);
Vector3 right = Normalize(Cross(Vector3{ 0.0f, 0.0f, 1.0f }, forward));
currentCam->Target = currentCam->Position + forward;
currentCam->Up = Cross(forward, right);
}
bool isRightMouseButtonDown = IsMouseButtonDown(MouseButton::Right);
if (isRightMouseButtonDown && !gWasRightMouseButtonDown)
{
gIsFpsModeActive = !gIsFpsModeActive;
}
gWasRightMouseButtonDown = isRightMouseButtonDown;
if (!gIsFpsModeActive)
{
return;
}
MousePosition mouseDelta = GetMouseDelta();
float sensitivity = 0.005f;
gYaw += mouseDelta.X * sensitivity;
gPitch -= mouseDelta.Y * sensitivity;
gPitch = std::min(gPitch, 1.5f);
gPitch = std::max(gPitch, -1.5f);
if (IsKeyDown(ScanCode::Q))
{
gYaw -= 2.0f * dt;
}
if (IsKeyDown(ScanCode::E))
{
gYaw += 2.0f * dt;
}
Vector3 forward;
forward.x = cosf(gPitch) * cosf(gYaw);
forward.y = cosf(gPitch) * sinf(gYaw);
forward.z = sinf(gPitch);
Vector3 right = Normalize(Cross(Vector3{ 0.0f, 0.0f, 1.0f }, forward));
Vector3 defaultUp = Cross(forward, right);
static const float kMovementPerFrame = 10.f; // 10m/s
float speedPerFrame = kMovementPerFrame;
if (IsKeyDown(ScanCode::LeftShift))
{
speedPerFrame *= 10.f; // 100m/s
}
if (IsKeyDown(ScanCode::W))
{
currentCam->Position = currentCam->Position + forward * (speedPerFrame * dt);
}
if (IsKeyDown(ScanCode::S))
{
currentCam->Position = currentCam->Position - forward * (speedPerFrame * dt);
}
if (IsKeyDown(ScanCode::D))
{
currentCam->Position = currentCam->Position + right * (speedPerFrame * dt);
}
if (IsKeyDown(ScanCode::A))
{
currentCam->Position = currentCam->Position - right * (speedPerFrame * dt);
}
if (IsKeyDown(ScanCode::Space))
{
currentCam->Position = currentCam->Position + defaultUp * (speedPerFrame * dt);
}
if (IsKeyDown(ScanCode::LeftControl))
{
currentCam->Position = currentCam->Position - defaultUp * (speedPerFrame * dt);
}
currentCam->Target = currentCam->Position + forward;
currentCam->Up = defaultUp;
}
#if JULIET_DEBUG
void RenderImGuiDebugController(float dt)
{
Camera* currentCam = GetCurrentCamera();
ImGui::Text("Delta time: %f", dt);
ImGui::Text("Camera:");
ImGui::Indent();
ImGui::BulletText("Index: %zu", (size_t)currentCam->Index);
ImGui::BulletText("Position: (%.3f, %.3f, %.3f)", currentCam->Position.x, currentCam->Position.y,
currentCam->Position.z);
ImGui::BulletText("Target: (%.3f, %.3f, %.3f)", currentCam->Target.x, currentCam->Target.y, currentCam->Target.z);
ImGui::BulletText("Up: (%.3f, %.3f, %.3f)", currentCam->Up.x, currentCam->Up.y, currentCam->Up.z);
ImGui::BulletText("FOV: %.3f rad", currentCam->FOV);
ImGui::BulletText("AspectRatio: %.3f", currentCam->AspectRatio);
ImGui::BulletText("NearPlane: %.3f", currentCam->NearPlane);
ImGui::BulletText("FarPlane: %.3f", currentCam->FarPlane);
ImGui::BulletText("Pitch: %.3f, Yaw: %.3f", gPitch, gYaw);
ImGui::BulletText("FPS Mode: %s", gIsFpsModeActive ? "Active" : "Inactive");
ImGui::Unindent();
}
#endif
+6 -4
View File
@@ -1,11 +1,13 @@
#pragma once
#include <Juliet.h>
// TODO : Should support Debug/Profile/Release properly.
// I think debug profile should have this controller but not release
void ActivateDebugController();
void DeactivateDebugController();
bool IsDebugControllerActive();
void UpdateDebugController(float dt);
void ActivateDebugController(void);
void DeactivateDebugController(void);
bool8 IsDebugControllerActive(void);
void UpdateDebugController(float dt);
#if JULIET_DEBUG
void RenderImGuiDebugController(float dt);
+140
View File
@@ -0,0 +1,140 @@
#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 <Entity/EntityManager.h>
#include <Graphics/MeshRenderer.h>
#ifdef JULIET_ENABLE_IMGUI
#include <dcimgui.h>
#endif
void InitWorld(World* world, Arena* arena)
{
Assert(world != NULL);
Assert(arena != NULL);
world->WorldArena = arena;
}
void ShutdownWorld(World* world)
{
Assert(world != NULL);
world->WorldArena = NULL;
}
void AddToWorld(World* world, Entity* entity)
{
(void)world;
(void)entity;
}
void RemoveWorldEntity(World* world, size_t index)
{
(void)world;
(void)index;
}
void SerializeWorld(Archive* ar, World* world, String filename)
{
(void)world;
Assert(ar != NULL);
Assert(IsValidString(filename));
EntityManager* entityManager = GetEntityManager();
if (ar->loading)
{
// Load
ByteBuffer fileBuffer = LoadFile(ar->arena, filename);
// TEST SERIALIZATION
ParsedArchive archive = tokenize_archive(ar->arena, fileBuffer);
#if JULIET_DEBUG
audit_unconsumed_properties(&archive, ConstString("World"));
#else
(void)archive;
#endif
}
else
{
// Save
IOStream* stream = IOFromFile(ar->arena, filename, WrapString("wb"));
index_t beginPos = ArenaPos(ar->arena);
// Headers
WorldFileHeader* header = ArenaPushStruct(ar->arena, WorldFileHeader);
header->Magic = kWorldMagic;
// TODO : Move to a one file per entity model
for (uint8 i = 0; i < ENTITY(Count); ++i)
{
typed_entity_array* type = &entityManager->by_type[i];
serialize_elem(ar, type->count);
if (type->count > 0 && type->array != NULL)
{
EntityTemplate* element = type->array;
uint8* rawElement = (uint8*)element;
size_t stride = element->base->derived_kind->size_of;
for (index_t idx = 0; idx < type->count; ++idx)
{
Entity* entity = ((EntityTemplate*)(rawElement + (idx * stride)))->base;
serialize_entity(ar, entity);
}
}
}
// Write
index_t endPos = ArenaPos(ar->arena);
ByteBuffer writeBuffer = { .Data = (Byte*)header, .Size = endPos - beginPos };
size_t written = IOWrite(stream, writeBuffer);
Assert(writeBuffer.Size == written);
IOClose(stream);
}
}
bool8 LoadWorld(World* world, String filename)
{
(void)world;
Assert(IsValidString(filename));
return true;
}
#ifdef JULIET_EDITOR
void RenderWorldEditorUI(World* world)
{
Assert(world != NULL);
if (ImGui_Begin("World Editor", NULL, ImGuiWindowFlags_None))
{
TempArena temp = scratch_begin(NULL, 0);
static char worldFilePath[256] = "/world.bin";
ImGui_InputText("World File", worldFilePath, sizeof(worldFilePath));
Path path = GetAssetPath(temp.Arena, AssetType_JAsset, WrapString(worldFilePath));
if (ImGui_Button("Save World"))
{
Archive data = { .arena = temp.Arena, .loading = false };
SerializeWorld(&data, world, path.FullPath);
}
ImGui_SameLine(0.0f, -1.0f);
if (ImGui_Button("Load World"))
{
Archive data = { .arena = temp.Arena, .loading = true };
SerializeWorld(&data, world, path.FullPath);
}
scratch_end(temp);
}
ImGui_End();
}
#endif
-322
View File
@@ -1,322 +0,0 @@
#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 <Entity/EntityManager.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& ar, World& /*world*/, String filename)
{
Assert(IsValid(filename));
auto& entityManager = GetEntityManager();
if (ar.loading)
{
// Load
ByteBuffer fileBuffer = LoadFile(ar.arena, filename);
// TEST SERIALIZATION
ParsedArchive archive = tokenize_archive(ar.arena, fileBuffer);
// ArchivePropertyNode* property = find_property(&archive, "Position"_crc32);
audit_unconsumed_properties(&archive, ConstString("World"));
// if (fileBuffer.Size >= sizeof(WorldFileHeader))
// {
// ar.base_ptr = fileBuffer.Data;
//
// WorldFileHeader header;
// serialize_elem(ar, header);
// Assert(header.Magic == kWorldMagic);
//
// 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(ar.arena, filename, WrapString("wb"));
index_t beginPos = ArenaPos(ar.arena);
// Headers
auto* header = ArenaPushStruct<WorldFileHeader>(ar.arena);
header->Magic = kWorldMagic;
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->derived_kind->size_of;
for (index_t idx = 0; idx < type.count; ++idx)
{
// Todo : utils
// Get base entity from type
Entity* entity = reinterpret_cast<EntityTemplate*>(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);
Assert(writeBuffer.Size == written);
IOClose(stream);
}
//
// 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));
Path path = GetAssetPath(temp.Arena, AssetType::JAsset, ConstString(worldFilePath));
if (ImGui::Button("Save World"))
{
Archive data = { .arena = temp.Arena, .loading = false };
Serialize(data, world, path.FullPath);
}
ImGui::SameLine();
if (ImGui::Button("Load World"))
{
Archive data{ .arena = temp.Arena, .loading = true };
Serialize(data, world, path.FullPath);
}
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
+15 -14
View File
@@ -1,38 +1,39 @@
#pragma once
#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;
typedef struct Archive Archive;
typedef struct EntityManager EntityManager;
#pragma pack(push, 1)
typedef struct WorldFileHeader WorldFileHeader;
struct WorldFileHeader
{
uint32 Magic = 0x444C574A; // 'JWLD' in little-endian
uint32 Magic; // 0x444C574A ('JWLD' in little-endian)
};
#pragma pack(pop)
constexpr uint32 kWorldMagic = 0x444C574A; // 'JWLD'
constexpr uint32 kWorldVersion = 1;
#define kWorldMagic ((uint32)0x444C574A) // 'JWLD'
#define kWorldVersion ((uint32)1)
typedef struct World World;
struct World
{
Arena* WorldArena = nullptr;
EntityManager* EntityManager = nullptr;
Arena* WorldArena;
EntityManager* EntityManager;
};
void InitWorld(NonNullPtr<World> world, NonNullPtr<Arena> arena);
void ShutdownWorld(NonNullPtr<World> world);
void InitWorld(World* world, Arena* arena);
void ShutdownWorld(World* world);
void AddToWorld(NonNullPtr<World> world, NonNullPtr<Entity> entity);
void RemoveWorldEntity(World& world, size_t index);
void AddToWorld(World* world, Entity* entity);
void RemoveWorldEntity(World* world, size_t index);
void Serialize(Archive& data, World& world, String filename);
void SerializeWorld(Archive* data, World* world, String filename);
#if JULIET_EDITOR
void RenderWorldEditorUI(World& world);
void RenderWorldEditorUI(World* world);
#endif
+63
View File
@@ -0,0 +1,63 @@
#include <Debug/DebugTopBar.h>
#if JULIET_DEBUG
#include <game.h>
#include <dcimgui.h>
#include <stdio.h>
internal String GetGameModeName(GameMode gameMode)
{
String result = WrapString("Unknown ERROR");
if (gameMode == GameMode_Editor)
{
result = WrapString("Editor");
}
else if (gameMode == GameMode_Play)
{
result = WrapString("Play");
}
else if (gameMode == GameMode_Debug)
{
result = WrapString("Debug");
}
return result;
}
void DrawTopBar(void)
{
GameState* gameState = GetGameState();
ImVec2_c viewportPos = ImGui_GetMainViewportPos();
ImVec2_c viewportSize = ImGui_GetMainViewportSize();
ImGui_SetNextWindowPos(viewportPos);
ImVec2_c barSize = { viewportSize.x, 10.0f };
ImGui_SetNextWindowSize(barSize);
ImGuiWindowFlags_c flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoNav;
ImVec4_c bgCol = { 0.0f, 0.0f, 0.0f, 0.6f };
ImVec2_c padding = { 10.0f, 5.0f };
ImGui_PushStyleColor(ImGuiCol_WindowBg, bgCol);
ImGui_PushStyleVarVec2(ImGuiStyleVar_WindowPadding, padding);
if (ImGui_Begin("TopBarOverlay", NULL, flags))
{
ImGui_Text("Game Mode: %s", CStr(GetGameModeName(gameState->Mode)));
float framerate = ImGuiIO_GetFramerate();
char fps_text[32];
snprintf(fps_text, sizeof(fps_text), "FPS: %.1f (%.2f ms)", (double)framerate, (double)(1000.0f / framerate));
float text_width = ImGui_CalcTextSize(fps_text, NULL).x;
ImGui_SameLine(viewportSize.x - text_width - 15.0f, -1.0f); // 15px right padding
// Render FPS text
ImGui_Text("%s", fps_text);
}
ImGui_End();
ImGui_PopStyleVar(1);
ImGui_PopStyleColor(1);
}
#endif
-49
View File
@@ -1,49 +0,0 @@
#include <Debug/DebugTopBar.h>
#include <game.h>
#include <imgui.h>
namespace
{
String GetGameModeName(GameMode gameMode)
{
switch (gameMode)
{
case GameMode::Editor: return WrapString("Editor");
case GameMode::Play: return WrapString("Play");
case GameMode::Debug: return WrapString("Debug");
default: return WrapString("Unknown ERROR");
}
}
} // namespace
void DrawTopBar()
{
auto* gameState = GetGameState();
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(ImVec2(viewport->Size.x, 10.0f));
ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.6f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0f, 5.0f));
if (ImGui::Begin("TopBarOverlay", nullptr, flags))
{
ImGui::Text("Game Mode: %s", CStr(GetGameModeName(gameState->Mode)));
char fps_text[32];
snprintf(fps_text, sizeof(fps_text), "FPS: %.1f (%.2f ms)", ImGui::GetIO().Framerate, 1000.0f / ImGui::GetIO().Framerate);
float text_width = ImGui::CalcTextSize(fps_text).x;
ImGui::SameLine(viewport->Size.x - text_width - 15.0f); // 15px right padding
// Render FPS text
ImGui::Text("%s", fps_text);
}
ImGui::End();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
+3 -1
View File
@@ -1,5 +1,7 @@
#pragma once
#include <Juliet.h>
#if JULIET_DEBUG
void DrawTopBar();
void DrawTopBar(void);
#endif
+115
View File
@@ -0,0 +1,115 @@
#include <Entity/Entity.h>
#include <Core/Common/serialization.h>
#include <Core/Memory/Utils.h>
#include <stdlib.h>
void serialize_entity(Archive* ar, Entity* entity)
{
Assert(ar != NULL);
Assert(entity != NULL);
// Entity fields
serialize_class(ar, ClassKind(Entity), entity);
// Derived fields
if (entity->derived_kind != NULL && entity->derived != NULL)
{
serialize_class(ar, entity->derived_kind, entity->derived);
}
}
internal void init_Entity(void* payload)
{
Assert(payload != NULL);
Entity* entity = (Entity*)payload;
MemZeroStruct(entity);
}
internal void serialize_Entity(Archive* ar, uint16 version, void* payload)
{
(void)version;
Assert(ar != NULL);
Assert(payload != NULL);
Entity* entity = (Entity*)payload;
SERIALIZE_U64(ar, id, &entity->ID);
SERIALIZE_VEC4(ar, position, &entity->position);
}
DEFINE_CLASS_VERSIONED(Entity, 1, NULL)
String format_entity_id(Arena* arena, EntityID entity_id)
{
Assert(arena != NULL);
// Format:
// U = UserID
// LC = Local Entity Counter
// UU_LCLCLCLCLCLCLC
uint8 user_id = get_user_id(entity_id);
uint64 counter = get_local_counter(entity_id);
const size_t kBufferSize = 18;
char* buffer = ArenaPushArray(arena, char, kBufferSize);
Assert(buffer != NULL);
juliet_snprintf(buffer, kBufferSize, "%02X_%014llX", (unsigned int)user_id, (unsigned long long)counter);
String result = { .Str = buffer, .Size = kBufferSize - 1 };
return result;
}
bool8 parse_entity_id(String str, EntityID* out_id)
{
Assert(out_id != NULL);
bool8 result = false;
*out_id = kInvalid_entity_id;
if (!(str.Size != 17 || str.Str == NULL || str.Str[2] != '_'))
{
char user_hex[3] = { str.Str[0], str.Str[1], '\0' };
char* end = user_hex;
uint8 user_id = (uint8)strtoul(user_hex, &end, 16);
if (end != user_hex && user_id != kInvalid_user_id)
{
char* counter_hex = str.Str + 3;
end = counter_hex;
uint64 counter = strtoull(counter_hex, &end, 16);
if (end != counter_hex && counter < kEntityID_user_id_mask)
{
*out_id = make_entity_id(user_id, counter);
result = true;
}
}
}
return result;
}
const Class* resolve_entity_class(uint8 kind, uint32 crc)
{
Assert(kind <= ENTITY(Count));
const Class* class_ptr = kEntity_type_class_ptr[kind];
Assert(class_ptr != NULL);
Assert(class_get_crc(class_ptr) == crc);
return class_ptr;
}
const Class* find_class_by_name(String name)
{
Assert(IsValidString(name));
const Class* result = NULL;
const uint32 name_crc = crc32_bytes(name.Str, name.Size);
for (uint8 kind = 0; kind < ENTITY(Count); ++kind)
{
const Class* class_ptr = resolve_entity_class(kind, name_crc);
if (class_ptr != NULL)
{
result = class_ptr;
break;
}
}
return result;
}
-92
View File
@@ -1,92 +0,0 @@
#include <Entity/Entity.h>
#include <Core/Common/serialization.h>
void serialize(Archive& ar, NonNullPtr<Entity> entity)
{
// Entity fields
serialize(ar, Entity::kind, entity.Get());
// Derived fields
if (entity->derived_kind != nullptr && entity->derived != nullptr)
{
serialize(ar, entity->derived_kind, entity->derived);
}
}
DEFINE_CLASS_VERSIONED(Entity, 1, nullptr)
internal void serialize(Archive& ar, uint16 /*version*/, Entity& entity)
{
SERIALIZE(ar, id, entity.ID);
SERIALIZE(ar, position, entity.position);
}
String format_entity_id(NonNullPtr<Arena> arena, EntityID entity_id)
{
// Format:
// U = UserID
// LC = Local Entity Counter
// UU_LCLCLCLCLCLCLC
uint8 user_id = get_user_id(entity_id);
uint64 counter = get_local_counter(entity_id);
constexpr size_t kBufferSize = 18;
char* buffer = ArenaPushArray<char>(arena, kBufferSize);
Assert(buffer);
juliet_snprintf(buffer, kBufferSize, "%02X_%014llX", (unsigned int)user_id, counter);
return { buffer, kBufferSize - 1 };
}
bool parse_entity_id(String str, EntityID& out_id)
{
bool result = false;
out_id = kInvalid_entity_id;
if (!(str.Size != 17 || str.Str == nullptr || str.Str[2] != '_'))
{
char user_hex[3]{ str.Str[0], str.Str[1], '\0' };
char* end = user_hex;
uint8 user_id = strtoul(user_hex, &end, 16);
if (end != user_hex && user_id != kInvalid_user_id)
{
char* counter_hex = str.Str + 3;
end = counter_hex;
uint64 counter = strtoull(counter_hex, &end, 16);
if (end != counter_hex && counter < kEntityID_user_id_mask)
{
out_id = make_entity_id(user_id, counter);
result = true;
}
}
}
return result;
}
const Class* resolve_entity_class(uint8 kind, uint32 crc)
{
Assert(kind <= ENTITY(Count));
NonNullPtr<const Class> class_ptr = kEntity_type_class_ptr[kind];
Assert(class_ptr->CRC == crc);
return class_ptr.Get();
}
const Class* find_class_by_name(String name)
{
Assert(IsValid(name));
const Class* result = nullptr;
const uint32 name_crc = crc32(name.Str, name.Size);
for (uint8 kind = 0; kind < ENTITY(Count); ++kind)
{
const Class* class_ptr = resolve_entity_class(kind, name_crc);
if (class_ptr != nullptr)
{
result = class_ptr;
break;
}
}
return result;
}
+48 -51
View File
@@ -6,98 +6,95 @@
#include <Entity/entity_common.h>
#include <Entity/EntityManager.h>
struct Entity final
typedef struct Entity Entity;
struct Entity
{
DECLARE_CLASS()
EntityID ID = 0;
const Class* derived_kind = nullptr;
DerivedType derived = nullptr;
Vector4 position = {};
bool is_dirty = false;
EntityID ID;
const Class* derived_kind;
DerivedType derived;
Vector4 position;
bool8 is_dirty;
};
DECLARE_CLASS(Entity)
// Can reinterpret cast to this to have the offset of Base and Kind for any entity
// Can cast to this to have the offset of base for any entity
typedef struct EntityTemplate EntityTemplate;
struct EntityTemplate
{
DECLARE_ENTITY();
DECLARE_ENTITY()
};
//
template <typename EntityType>
concept EntityConcept = requires(EntityType entity) {
{ EntityType::kind } -> std::convertible_to<const Class*>;
requires std::same_as<decltype(entity.base), Entity*>;
};
inline constexpr EntityID make_entity_id(uint8 user_id, uint64 local_counter)
static inline EntityID make_entity_id(uint8 user_id, uint64 local_counter)
{
Assert(user_id != kInvalid_user_id);
Assert((local_counter & kEntityID_user_id_mask) == kInvalid_entity_id);
EntityID result = kInvalid_user_id;
result = ((EntityID)user_id << kEntityID_user_id_shift) | (local_counter & kEntityID_user_id_mask);
EntityID result = ((EntityID)user_id << kEntityID_user_id_shift) | (local_counter & kEntityID_user_id_mask);
return result;
}
inline constexpr uint8 get_user_id(EntityID entity_id)
static inline uint8 get_user_id(EntityID entity_id)
{
return (((uint8)entity_id & kEntityID_user_id_mask) << kEntityID_user_id_shift);
return (uint8)((entity_id & kEntityID_user_id_mask) >> kEntityID_user_id_shift);
}
inline constexpr uint64 get_local_counter(EntityID entity_id)
static inline uint64 get_local_counter(EntityID entity_id)
{
return (uint64)entity_id & kEntityID_user_id_mask;
return (uint64)entity_id & ~kEntityID_user_id_mask;
}
inline constexpr bool is_entity_id_valid(EntityID entity_id)
static inline bool8 is_entity_id_valid(EntityID entity_id)
{
bool result = entity_id != kInvalid_entity_id;
bool8 result = (entity_id != kInvalid_entity_id);
return result;
}
template <typename EntityType>
requires EntityConcept<EntityType>
[[nodiscard]] bool IsA(const Entity* entity)
static inline bool8 IsEntityA(const Entity* entity, const Class* kind)
{
Assert(entity != nullptr);
return entity->derived_kind == EntityType::kind;
Assert(entity != NULL);
return entity->derived_kind == kind;
}
template <typename EntityType>
requires EntityConcept<EntityType>
[[nodiscard]] EntityType* MakeEntity(EntityManager& manager, float x, float y, float z)
{
Entity* base_ptr = allocate_entity(manager, EntityType::kind);
Assert(base_ptr);
#define IsEntityOfType(entity_ptr, EntityType) IsEntityA((entity_ptr), ClassKind(EntityType))
base_ptr->ID = EntityManager::ID++;
static inline void* make_entity_by_class(EntityManager* manager, const Class* kind, float x, float y, float z)
{
Assert(manager != NULL);
Assert(kind != NULL);
Entity* base_ptr = allocate_entity(manager, kind);
Assert(base_ptr != NULL);
base_ptr->ID = g_entity_manager_id++;
base_ptr->position.x = x;
base_ptr->position.y = y;
base_ptr->position.z = z;
base_ptr->position.w = 1.0f;
return (EntityType*)base_ptr->derived;
return base_ptr->derived;
}
template <typename EntityType>
requires EntityConcept<EntityType>
[[nodiscard]] EntityType* DownCast(Entity* entity)
#define MakeEntity(EntityType, manager_ptr, x, y, z) \
((EntityType*)make_entity_by_class((manager_ptr), ClassKind(EntityType), (x), (y), (z)))
static inline void* down_cast_entity(Entity* entity, const Class* kind)
{
Assert(entity != nullptr);
Assert(IsA<EntityType>(entity));
return static_cast<EntityType*>(entity->derived);
Assert(entity != NULL);
Assert(IsEntityA(entity, kind));
return entity->derived;
}
void serialize(Archive& ar, NonNullPtr<Entity> entity);
#define DownCastEntity(EntityType, entity_ptr) ((EntityType*)down_cast_entity((entity_ptr), ClassKind(EntityType)))
JULIET_API uint8 get_local_user_id();
void serialize_entity(Archive* ar, Entity* entity);
JULIET_API String format_entity_id(NonNullPtr<Arena> arena, EntityID id);
JULIET_API uint8 get_local_user_id(void);
JULIET_API bool parse_entity_id(String str, EntityID& outId);
JULIET_API String format_entity_id(Arena* arena, EntityID id);
[[nodiscard]] const Class* find_class_by_name(String name);
JULIET_API bool8 parse_entity_id(String str, EntityID* outId);
[[nodiscard]] const Class* resolve_entity_class(uint8 kind, uint32 crc);
const Class* find_class_by_name(String name);
const Class* resolve_entity_class(uint8 kind, uint32 crc);
+190
View File
@@ -0,0 +1,190 @@
#include <Entity/EntityManager.h>
#include <Core/Common/serialization.h>
#include <Core/Memory/Utils.h>
#include <Data/World.h>
#include <Engine/user.h>
#include <Entity/Entity.h>
#include <Entity/entity_types.h>
#include <game.h>
#include <Graphics/MeshRenderer.h>
EntityID g_entity_manager_id = 0;
void InitEntityManager(World* world)
{
Assert(world != NULL);
EntityManager* newManager = ArenaPushStruct(world->WorldArena, EntityManager);
newManager->next_local_counter = 1ULL;
world->EntityManager = newManager;
VectorArenaCreate(&newManager->Entities, Entity, world->WorldArena, 100000 JULIET_DEBUG_PARAM("Entities"));
ArenaParams by_type_params = { .ReserveSize = Kilobytes(1),
.CommitSize = Kilobytes(1),
.Name = "" JULIET_DEBUG_ONLY(, .DoNotReserveMore = true) };
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 = NULL;
newManager->by_type[i].count = 0;
}
}
void ShutdownEntityManager(void)
{
EntityManager* manager = GetEntityManager();
for (uint8 i = 0; i < ENTITY(Count); ++i)
{
ArenaRelease(manager->by_type[i].arena);
}
vector_arena_destroy(&manager->Entities);
}
EntityManager* GetEntityManager(void)
{
EntityManager* entityManager = GetGameState()->World->EntityManager;
Assert(entityManager != NULL);
return entityManager;
}
EntityID generate_next_entity_id(EntityManager* manager)
{
Assert(manager != NULL);
UserID user = get_local_user_id();
Assert(user != kInvalid_user_id);
Assert(manager->next_local_counter != kEntityID_user_id_mask);
EntityID new_id = make_entity_id(user, manager->next_local_counter);
manager->next_local_counter += 1ULL;
return new_id;
}
EntityTemplate* RegisterEntity(EntityManager* manager, Entity* base, DerivedType entity)
{
Assert(manager != NULL);
Assert(base != NULL);
base->ID = g_entity_manager_id++;
base->derived = entity;
VectorArenaPushPtr(Entity, &manager->Entities, base);
EntityTemplate* ptr = (EntityTemplate*)ArenaPushSize(
manager->by_type[base->derived_kind->kind].arena, base->derived_kind->size_of, base->derived_kind->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[base->derived_kind->kind]));
MemCopy(ptr, entity, base->derived_kind->size_of);
manager->by_type[base->derived_kind->kind].count += 1;
ptr->base = VectorArenaBack(Entity, &manager->Entities);
if (manager->by_type[base->derived_kind->kind].array == NULL)
{
manager->by_type[base->derived_kind->kind].array = ptr;
}
return ptr;
}
Entity* allocate_entity(EntityManager* manager, const Class* derived_type_class)
{
Assert(manager != NULL);
Assert(derived_type_class != NULL);
Assert(derived_type_class->kind < ENTITY(Count));
Assert(derived_type_class->size_of >= sizeof(EntityTemplate));
Assert(derived_type_class->alignment > 0);
Assert(derived_type_class->initialize_fct != NULL);
Entity base_template = { 0 };
base_template.derived_kind = derived_type_class;
base_template.is_dirty = true;
VectorArenaPushPtr(Entity, &manager->Entities, &base_template);
Entity* base_ptr = VectorArenaBack(Entity, &manager->Entities);
Assert(base_ptr != NULL);
typed_entity_array* typed_array = &manager->by_type[derived_type_class->kind];
Assert(typed_array->arena != NULL);
void* derived = ArenaPushSize(typed_array->arena, derived_type_class->size_of, derived_type_class->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[derived_type_class->kind]));
Assert(derived != NULL);
derived_type_class->initialize_fct(derived);
EntityTemplate* derived_template = (EntityTemplate*)derived;
base_ptr->derived = derived;
derived_template->base = base_ptr;
if (typed_array->array == NULL)
{
typed_array->array = derived_template;
}
typed_array->count += 1;
return base_ptr;
}
void UpdateEntityManager(EntityManager* manager)
{
Assert(manager != NULL);
// Todo : inert by definition dont move, but this is for test
typed_entity_array* by_type = &manager->by_type[ENTITY(Inert)];
for (index_t i = 0; i < by_type->count; ++i)
{
Inert* inert = ((Inert*)by_type->array) + i;
if (inert->MeshInstance != indexMax)
{
Matrix transform =
MatrixTranslation(inert->base->position.x, inert->base->position.y, inert->base->position.z);
SetMeshInstanceTransform(inert->MeshInstance, &transform);
}
}
}
Entity* deserialize_entity(Archive* ar, EntityManager* manager)
{
Assert(ar != NULL);
Assert(manager != NULL);
Assert(ar->loading);
String class_name = { 0 };
SERIALIZE_STR(ar, Class, &class_name);
const Class* class_ptr = find_class_by_name(class_name);
Assert(class_ptr != NULL);
Entity* entity_base = allocate_entity(manager, class_ptr);
Assert(entity_base != NULL);
serialize_entity(ar, entity_base);
if (entity_base->ID >= g_entity_manager_id)
{
g_entity_manager_id = entity_base->ID + 1;
}
entity_base->is_dirty = false;
return entity_base;
}
void update_local_counter(EntityManager* manager, EntityID entity_id)
{
Assert(manager != NULL);
uint8 user_id = get_local_user_id();
if (get_user_id(entity_id) == user_id)
{
uint64 counter = get_local_counter(entity_id);
if (counter >= manager->next_local_counter)
{
manager->next_local_counter = counter + 1ULL;
}
}
}
-170
View File
@@ -1,170 +0,0 @@
#include <Entity/EntityManager.h>
#include <Core/Common/EnumUtils.h>
#include <Core/Common/serialization.h>
#include <Data/World.h>
#include <Entity/Entity.h>
#include <Entity/entity_types.h>
#include <game.h>
#include <Graphics/MeshRenderer.h>
#include "Engine/user.h"
EntityID EntityManager::ID = 0;
void InitEntityManager(NonNullPtr<World> world)
{
EntityManager* newManager = ArenaPushStruct<EntityManager>(world->WorldArena);
world->EntityManager = newManager;
newManager->Entities.Create(world->WorldArena JULIET_DEBUG_PARAM("Entities"));
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()
{
auto& manager = GetEntityManager();
for (typed_entity_array& type : manager.by_type)
{
ArenaRelease(type.arena);
}
manager.Entities.Destroy();
}
EntityManager& GetEntityManager()
{
NonNullPtr entityManager = GetGameState()->World->EntityManager;
return *entityManager;
}
EntityID generate_next_entity_id(EntityManager& manager)
{
UserID user = get_local_user_id();
Assert(user != kInvalid_user_id);
Assert(manager.next_local_counter != kEntityID_user_id_mask);
EntityID new_id = make_entity_id(user, manager.next_local_counter);
manager.next_local_counter += 1ULL;
return new_id;
}
EntityTemplate* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity)
{
base->ID = EntityManager::ID++;
base->derived = entity;
manager.Entities.PushBack(*base);
auto* ptr = (EntityTemplate*)ArenaPushSize(manager.by_type[base->derived_kind->kind].arena,
base->derived_kind->size_of, base->derived_kind->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[base->derived_kind->kind]));
MemCopy(ptr, entity, base->derived_kind->size_of);
manager.by_type[base->derived_kind->kind].count += 1;
ptr->base = manager.Entities.Back();
if (manager.by_type[base->derived_kind->kind].array == nullptr)
{
manager.by_type[base->derived_kind->kind].array = ptr;
}
return ptr;
}
Entity* allocate_entity(EntityManager& manager, NonNullPtr<const Class> derived_type_class)
{
Assert(derived_type_class->kind < ENTITY(Count));
Assert(derived_type_class->size_of >= sizeof(EntityTemplate));
Assert(derived_type_class->alignment > 0);
Assert(derived_type_class->initialize_fct);
Entity base_template = {};
base_template.derived_kind = derived_type_class;
base_template.is_dirty = true;
manager.Entities.PushBack(base_template);
Entity* base_ptr = manager.Entities.Back();
Assert(base_ptr);
typed_entity_array& typed_array = manager.by_type[derived_type_class->kind];
Assert(typed_array.arena);
void* derived = ArenaPushSize(typed_array.arena, derived_type_class->size_of, derived_type_class->alignment,
false JULIET_DEBUG_PARAM(kEntity_type_names[derived_type_class->kind]));
Assert(derived);
derived_type_class->initialize_fct(derived);
auto* derived_template = (EntityTemplate*)derived;
base_ptr->derived = derived;
derived_template->base = base_ptr;
if (typed_array.array == nullptr)
{
typed_array.array = derived_template;
}
typed_array.count += 1;
return base_ptr;
}
void UpdateEntityManager(EntityManager& manager)
{
// 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)
{
Inert* inert = reinterpret_cast<Inert*>(by_type.array) + i;
if (inert->MeshInstance != indexMax)
{
SetMeshInstanceTransform(inert->MeshInstance,
MatrixTranslation(inert->base->position.x, inert->base->position.y,
inert->base->position.z));
}
}
}
Entity* deserialize_entity(Archive& ar, EntityManager& manager)
{
Assert(ar.loading);
String class_name;
SERIALIZE(ar, Class, class_name);
NonNullPtr<const Class> class_ptr = find_class_by_name(class_name);
NonNullPtr<Entity> entity_base = allocate_entity(manager, class_ptr);
serialize(ar, entity_base);
if (entity_base->ID >= EntityManager::ID)
{
EntityManager::ID = entity_base->ID + 1;
}
entity_base->is_dirty = false;
return entity_base;
}
void update_local_counter(EntityManager& manager, EntityID entity_id)
{
uint8 user_id = get_local_user_id();
if (get_user_id(entity_id) == user_id)
{
uint64 counter = get_local_counter(entity_id);
if (counter >= manager.next_local_counter)
{
manager.next_local_counter = counter + 1ULL;
}
}
}
+20 -16
View File
@@ -3,11 +3,13 @@
#include <Core/Container/Vector.h>
#include <Entity/entity_common.h>
struct World;
struct EntityTemplate;
struct Entity;
struct Class;
typedef struct World World;
typedef struct EntityTemplate EntityTemplate;
typedef struct Entity Entity;
typedef struct Class Class;
typedef struct Archive Archive;
typedef struct typed_entity_array typed_entity_array;
struct typed_entity_array
{
Arena* arena;
@@ -15,22 +17,24 @@ struct typed_entity_array
size_t count;
};
typedef struct EntityManager EntityManager;
struct EntityManager
{
static EntityID ID;
uint64 next_local_counter = 1ULL;
uint64 next_local_counter;
// TODO: Should be a pool
VectorArena<Entity, 100'000> Entities;
VectorArena Entities;
typed_entity_array by_type[ENTITY(Count)];
typed_entity_array by_type[Entity_Type_Count];
};
void InitEntityManager(NonNullPtr<World> world);
void ShutdownEntityManager();
EntityManager& GetEntityManager();
EntityTemplate* RegisterEntity(EntityManager& manager, Entity* base, DerivedType entity);
[[nodiscard]] Entity* allocate_entity(EntityManager& manager, NonNullPtr<const Class> derived_type_class);
void UpdateEntityManager(EntityManager& manager);
[[nodiscard]] Entity* deserialize_entity(Archive& archive, EntityManager& manager);
void update_local_counter(EntityManager& manager, EntityID entity_id);
extern EntityID g_entity_manager_id;
void InitEntityManager(World* world);
void ShutdownEntityManager(void);
EntityManager* GetEntityManager(void);
EntityTemplate* RegisterEntity(EntityManager* manager, Entity* base, DerivedType entity);
Entity* allocate_entity(EntityManager* manager, const Class* derived_type_class);
void UpdateEntityManager(EntityManager* manager);
Entity* deserialize_entity(Archive* archive, EntityManager* manager);
void update_local_counter(EntityManager* manager, EntityID entity_id);
@@ -5,17 +5,16 @@
// clang-format off
#define AS_STR(name) #name
const char* kEntity_type_names[] = {
const char* kEntity_type_names[] = {
ENTITY_TYPE_LIST(AS_STR)
"Count"
};
#undef AS_STR
#define AS_CLASS(name) name::kind
const Class* kEntity_type_class_ptr[]
{
#define AS_CLASS(name) &class_kind_##name
const Class* kEntity_type_class_ptr[] = {
ENTITY_TYPE_LIST(AS_CLASS)
nullptr
NULL
};
#undef AS_CLASS
// clang-format on
+26 -24
View File
@@ -1,46 +1,48 @@
#pragma once
#include <Core/Common/EnumUtils.h>
#include <Engine/Class.h>
struct Class;
using DerivedType = void*;
using EntityID = uint64_t;
typedef void* DerivedType;
typedef uint64 EntityID;
constexpr uint8 kInvalid_user_id = 0U;
constexpr EntityID kInvalid_entity_id = 0ULL;
constexpr EntityID kEntityID_user_id_shift = 56ULL;
constexpr EntityID kEntityID_user_id_mask = 0xFF00'0000'0000'0000ULL;
#define kInvalid_user_id ((uint8)0U)
#define kInvalid_entity_id ((EntityID)0ULL)
#define kEntityID_user_id_shift ((EntityID)56ULL)
#define kEntityID_user_id_mask ((EntityID)0xFF00000000000000ULL)
// clang-format off
#define ENTITY_TYPE_LIST(X) X(Inert),
#define AS_ENUM(name) name
enum class Entity_Type : uint8
typedef uint8 Entity_Type;
#define AS_ENUM(name) Entity_Type_##name
enum
{
ENTITY_TYPE_LIST(AS_ENUM)
Count
Entity_Type_Count
};
#undef AS_ENUM
extern const char* kEntity_type_names[];
extern const char* kEntity_type_names[];
extern const Class* kEntity_type_class_ptr[];
#define ENTITY(kind) ToUnderlying(Entity_Type::kind)
#define ENTITY(kind) ((uint8)Entity_Type_##kind)
// clang-format on
struct Entity;
typedef struct Entity Entity;
#define DECLARE_ENTITY() \
Entity* base; \
DECLARE_CLASS()
#define DECLARE_ENTITY() Entity* base;
#define DEFINE_ENTITY_VERSIONED(entity, version) \
constexpr Class entityKind##entity = \
MakeClass(ConstString(#entity), (uint8)Entity_Type::entity, (version), &class_kind_Entity, sizeof(entity), \
alignof(entity), (&initialize_thunk<entity>), (&serialize_thunk<entity>)); \
Class* entity::kind = const_cast<Class*>(&entityKind##entity);
#define DEFINE_ENTITY_VERSIONED(entity, version_num) \
const Class class_kind_##entity = { \
.Name = ConstString(#entity), \
.kind = (uint8)Entity_Type_##entity, \
.version = (version_num), \
.base_class = &class_kind_Entity, \
.initialize_fct = init_##entity, \
.serialize_fct = serialize_##entity, \
.size_of = sizeof(entity), \
.alignment = AlignOf(entity) \
};
// Note: Needed by every classes see define above
// Note: Needed by every entity class (see define above)
extern const Class class_kind_Entity;
+22
View File
@@ -0,0 +1,22 @@
#include <Entity/entity_types.h>
#include <Core/Common/serialization.h>
internal void init_Inert(void* payload)
{
Assert(payload != NULL);
Inert* inert = (Inert*)payload;
inert->base = NULL;
inert->MeshInstance = indexMax;
}
internal void serialize_Inert(Archive* ar, uint16 version, void* payload)
{
(void)version;
Assert(ar != NULL);
Assert(payload != NULL);
Inert* inert = (Inert*)payload;
SERIALIZE_U64(ar, MeshInstance, &inert->MeshInstance);
}
DEFINE_ENTITY_VERSIONED(Inert, 1)
-9
View File
@@ -1,9 +0,0 @@
#include <Entity/entity_types.h>
#include <Core/Common/serialization.h>
DEFINE_ENTITY_VERSIONED(Inert, 1)
internal void serialize(Archive& ar, uint16 /*version*/, Inert& inert)
{
SERIALIZE(ar, MeshInstance, inert.MeshInstance);
}
+3 -1
View File
@@ -2,9 +2,11 @@
#include <Entity/entity_common.h>
typedef struct Inert Inert;
struct Inert
{
DECLARE_ENTITY()
index_t MeshInstance = indexMax;
index_t MeshInstance;
};
DECLARE_CLASS(Inert)
+171
View File
@@ -0,0 +1,171 @@
#include <UnitTest/WorldUnitTest.h>
#if JULIET_DEBUG
#include <Core/Common/CoreUtils.h>
#include <Core/Common/serialization.h>
#include <Core/HAL/IO/IOStream.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Thread/ThreadContext.h>
#include <Data/World.h>
#include <Entity/Entity.h>
#include <Entity/entity_types.h>
#include <game.h>
internal void TestEntityAllocationAndWiring(void)
{
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] Running TestEntityAllocationAndWiring...");
TempArena tempArena = scratch_begin(NULL, 0);
GameState* gameState = GetGameState();
World testWorld = { 0 };
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager* manager = testWorld.EntityManager;
// 1. Allocate Inert Entity via allocate_entity
Entity* base_entity = allocate_entity(manager, ClassKind(Inert));
Assert(base_entity != NULL);
Assert(base_entity->ID == 0); // Pure memory allocator leaves ID unassigned (0) until MakeEntity or deserialization
base_entity->ID = ++g_entity_manager_id;
Assert(base_entity->ID > 0);
Assert(base_entity->derived_kind == ClassKind(Inert));
Assert(base_entity->derived != NULL);
Assert(base_entity->is_dirty == true);
// 2. Validate mutual back-pointer wiring
EntityTemplate* derived = (EntityTemplate*)base_entity->derived;
Assert(derived->base == base_entity);
// 3. DownCast verification
Inert* inert = DownCastEntity(Inert, base_entity);
Assert(inert != NULL);
Assert(inert->base == base_entity);
// 4. Validate typed array tracking
typed_entity_array* inert_array = &manager->by_type[ENTITY(Inert)];
Assert(inert_array->count == 1);
Assert(inert_array->array == derived);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = NULL;
scratch_end(tempArena);
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] PASSED: TestEntityAllocationAndWiring");
}
internal void TestInPlaceDeserialization(void)
{
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] Running TestInPlaceDeserialization...");
TempArena tempArena = scratch_begin(NULL, 0);
GameState* gameState = GetGameState();
World testWorld = { 0 };
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager* manager = testWorld.EntityManager;
// 1. In-memory .jasset text fixture (no streams or files needed)
String asset_content = ConstString("; Class\n"
"Inert\n"
"; version\n"
"1\n"
"; class_version\n"
"1\n"
"; id\n"
"42\n"
"; position\n"
"12.5 -44.0 108.2 1.0\n"
"; MeshInstance\n"
"42\n");
ByteBuffer buffer = { .Data = (Byte*)asset_content.Str, .Size = asset_content.Size };
// 2. Tokenize into Archive
Archive load_ar = { 0 };
load_ar.arena = tempArena.Arena;
load_ar.loading = true;
load_ar.base = tokenize_archive(tempArena.Arena, buffer);
// 3. Deserialize in-place
Entity* loaded_base = deserialize_entity(&load_ar, manager);
// 4. Verify in-place allocations and values
Assert(loaded_base != NULL);
Assert(loaded_base->ID == 42);
Assert(g_entity_manager_id > 42); // Generator counter advanced past loaded ID
Assert(loaded_base->position.x == 12.5f);
Assert(loaded_base->position.y == -44.0f);
Assert(loaded_base->position.z == 108.2f);
Assert(loaded_base->is_dirty == false);
Inert* loaded_inert = DownCastEntity(Inert, loaded_base);
Assert(loaded_inert != NULL);
Assert(loaded_inert->base == loaded_base);
Assert(loaded_inert->MeshInstance == 42);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = NULL;
scratch_end(tempArena);
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] PASSED: TestInPlaceDeserialization");
}
internal void TestDirtyTrackingLifecycle(void)
{
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] Running TestDirtyTrackingLifecycle...");
TempArena tempArena = scratch_begin(NULL, 0);
GameState* gameState = GetGameState();
World testWorld = { 0 };
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager* manager = testWorld.EntityManager;
Entity* entity = allocate_entity(manager, ClassKind(Inert));
Assert(entity->is_dirty == true);
// TODO : proper save and mutation
// Simulate save
entity->is_dirty = false;
Assert(entity->is_dirty == false);
// Simulate mutation
entity->position.x += 1.0f;
entity->is_dirty = true;
Assert(entity->is_dirty == true);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = NULL;
scratch_end(tempArena);
Log(LogLevel_Message, LogCategory_Game, "[UnitTest] PASSED: TestDirtyTrackingLifecycle");
}
void UnitTest_WorldUnitTest(void)
{
Log(LogLevel_Message, LogCategory_Game, "==================================================");
Log(LogLevel_Message, LogCategory_Game, "Starting Entity Allocation & Lifecycle Unit Tests");
Log(LogLevel_Message, LogCategory_Game, "==================================================");
TestEntityAllocationAndWiring();
TestInPlaceDeserialization();
// TestSwapAndPopPointerFixup(); // Deferred to Entity_Removal_Brainstorm.md
TestDirtyTrackingLifecycle();
Log(LogLevel_Message, LogCategory_Game, "==================================================");
Log(LogLevel_Message, LogCategory_Game, "All Entity Lifecycle Unit Tests PASSED Successfully");
Log(LogLevel_Message, LogCategory_Game, "==================================================");
}
#endif
-173
View File
@@ -1,173 +0,0 @@
#include <UnitTest/WorldUnitTest.h>
#if JULIET_DEBUG
#include <Core/Common/CoreUtils.h>
#include <Core/Common/serialization.h>
#include <Core/HAL/IO/IOStream.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Thread/ThreadContext.h>
#include <Data/World.h>
#include <Entity/entity_types.h>
#include <game.h>
namespace UnitTest
{
internal void TestEntityAllocationAndWiring()
{
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] Running TestEntityAllocationAndWiring...");
TempArena tempArena = scratch_begin(nullptr, 0);
GameState* gameState = GetGameState();
World testWorld{};
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager& manager = *testWorld.EntityManager;
// 1. Allocate Inert Entity via AllocateEntity
Entity* base_entity = allocate_entity(manager, Inert::kind);
Assert(base_entity != nullptr);
Assert(base_entity->ID == 0); // Pure memory allocator leaves ID unassigned (0) until MakeEntity or deserialization
base_entity->ID = ++EntityManager::ID;
Assert(base_entity->ID > 0);
Assert(base_entity->derived_kind == Inert::kind);
Assert(base_entity->derived != nullptr);
Assert(base_entity->is_dirty == true);
// 2. Validate mutual back-pointer wiring
auto* derived = reinterpret_cast<EntityTemplate*>(base_entity->derived);
Assert(derived->base == base_entity);
// 3. DownCast verification
Inert* inert = DownCast<Inert>(base_entity);
Assert(inert != nullptr);
Assert(inert->base == base_entity);
// 4. Validate typed array tracking
typed_entity_array& inert_array = manager.by_type[ENTITY(Inert)];
Assert(inert_array.count == 1);
Assert(inert_array.array == derived);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = nullptr;
scratch_end(tempArena);
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] PASSED: TestEntityAllocationAndWiring");
}
internal void TestInPlaceDeserialization()
{
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] Running TestInPlaceDeserialization...");
TempArena tempArena = scratch_begin(nullptr, 0);
GameState* gameState = GetGameState();
World testWorld{};
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager& manager = *testWorld.EntityManager;
// 1. In-memory .jasset text fixture (no streams or files needed)
String asset_content = ConstString("; Class\n"
"Inert\n"
"; version\n"
"1\n"
"; class_version\n"
"1\n"
"; id\n"
"42\n"
"; position\n"
"12.5 -44.0 108.2 1.0\n"
"; MeshInstance\n"
"42\n");
ByteBuffer buffer = { .Data = reinterpret_cast<Byte*>(asset_content.Str), .Size = asset_content.Size };
// 2. Tokenize into Archive
Archive load_ar = {};
load_ar.arena = tempArena.Arena;
load_ar.loading = true;
load_ar.base = tokenize_archive(tempArena.Arena, buffer);
// 3. Deserialize in-place
Entity* loaded_base = deserialize_entity(load_ar, manager);
// 4. Verify in-place allocations and values
Assert(loaded_base != nullptr);
Assert(loaded_base->ID == 42);
Assert(EntityManager::ID > 42); // Generator counter advanced past loaded ID
Assert(loaded_base->position.x == 12.5f);
Assert(loaded_base->position.y == -44.0f);
Assert(loaded_base->position.z == 108.2f);
Assert(loaded_base->is_dirty == false);
Inert* loaded_inert = DownCast<Inert>(loaded_base);
Assert(loaded_inert != nullptr);
Assert(loaded_inert->base == loaded_base);
Assert(loaded_inert->MeshInstance == 42);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = nullptr;
scratch_end(tempArena);
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] PASSED: TestInPlaceDeserialization");
}
internal void TestDirtyTrackingLifecycle()
{
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] Running TestDirtyTrackingLifecycle...");
TempArena tempArena = scratch_begin(nullptr, 0);
GameState* gameState = GetGameState();
World testWorld{};
gameState->World = &testWorld;
InitWorld(&testWorld, tempArena.Arena);
InitEntityManager(&testWorld);
EntityManager& manager = *testWorld.EntityManager;
Entity* entity = allocate_entity(manager, Inert::kind);
Assert(entity->is_dirty == true);
// TODO : proper save and mutation
// Simulate save
entity->is_dirty = false;
Assert(entity->is_dirty == false);
// Simulate mutation
entity->position.x += 1.0f;
entity->is_dirty = true;
Assert(entity->is_dirty == true);
ShutdownEntityManager();
ShutdownWorld(&testWorld);
gameState->World = nullptr;
scratch_end(tempArena);
Log(LogLevel::Message, LogCategory::Game, "[UnitTest] PASSED: TestDirtyTrackingLifecycle");
}
void WorldUnitTest()
{
Log(LogLevel::Message, LogCategory::Game, "==================================================");
Log(LogLevel::Message, LogCategory::Game, "Starting Entity Allocation & Lifecycle Unit Tests");
Log(LogLevel::Message, LogCategory::Game, "==================================================");
TestEntityAllocationAndWiring();
TestInPlaceDeserialization();
// TestSwapAndPopPointerFixup(); // Deferred to Entity_Removal_Brainstorm.md
TestDirtyTrackingLifecycle();
Log(LogLevel::Message, LogCategory::Game, "==================================================");
Log(LogLevel::Message, LogCategory::Game, "All Entity Lifecycle Unit Tests PASSED Successfully");
Log(LogLevel::Message, LogCategory::Game, "==================================================");
}
} // namespace UnitTest
#endif
+1 -4
View File
@@ -4,9 +4,6 @@
#if JULIET_DEBUG
namespace UnitTest
{
void WorldUnitTest();
} // namespace UnitTest
void UnitTest_WorldUnitTest(void);
#endif
+48 -42
View File
@@ -13,28 +13,26 @@
#include <Entity/EntityManager.h>
#include <Graphics/Camera.h>
#include <Graphics/MeshRenderer.h>
#include <imgui.h>
#include <dcimgui.h>
#include <stdio.h>
#if JULIET_DEBUG
#include <Debug/DebugTopBar.h>
#include <UnitTest/WorldUnitTest.h>
#endif
namespace
{
GameState* gGameState = nullptr;
}
global GameState* gGameState = NULL;
GameState* GetGameState()
GameState* GetGameState(void)
{
return gGameState;
}
extern "C" JULIET_API void __cdecl GameShutdown()
JULIET_API void __cdecl GameShutdown(void)
{
printf("Shutting down game...\n");
if (gGameState && gGameState->World)
if (gGameState != NULL && gGameState->World != NULL)
{
ShutdownWorld(gGameState->World);
}
@@ -42,47 +40,55 @@ extern "C" JULIET_API void __cdecl GameShutdown()
ShutdownEntityManager();
}
extern "C" JULIET_API void __cdecl GameUpdate(GameData* params, [[maybe_unused]] float deltaTime)
JULIET_API void __cdecl GameUpdate(GameData* params, float deltaTime)
{
Assert(params != NULL);
gGameState = params->GameState;
if (!gGameState)
if (gGameState == NULL)
{
Arena* gameStateArena = ArenaAllocate({ .ReserveSize = Megabytes(1), .Name = "Game Total Arena" });
auto* gameState = ArenaPushStruct<GameState>(gameStateArena);
ArenaParams gameStateParams = { .ReserveSize = Megabytes(1), .Name = "Game Total Arena" };
Arena* gameStateArena = ArenaAllocate(gameStateParams);
GameState* gameState = ArenaPushStruct(gameStateArena, GameState);
gGameState = params->GameState = gameState;
gameState->TotalArena = gameStateArena;
gameState->Mode = GameMode_Editor;
gameState->TotalTime = 0.0f;
gameState->Score = 0;
printf("Game Arena Allocated: %p\n", static_cast<void*>(gameState));
printf("Game Arena Allocated: %p\n", (void*)gameState);
// Reserve cameras. Lets go with 4 for now
ReserveCamera(4);
#if JULIET_DEBUG
UnitTest::WorldUnitTest();
UnitTest_WorldUnitTest();
#endif
// Bootstrap world
auto* worldArena = ArenaAllocate({ .ReserveSize = Megabytes(1), .Name = "World Arena" });
World* world = ArenaPushStruct<World>(worldArena JULIET_DEBUG_PARAM("World"));
gameState->World = world;
ArenaParams worldParams = { .ReserveSize = Megabytes(1), .Name = "World Arena" };
Arena* worldArena = ArenaAllocate(worldParams);
World* world = ArenaPushStructTag(worldArena, World, "World");
gameState->World = world;
InitWorld(gameState->World, worldArena);
// Entity Use case
InitEntityManager(gameState->World);
auto& manager = GetEntityManager();
EntityManager* manager = GetEntityManager();
NonNullPtr mesh = MakeEntity<Inert>(manager, 1.f, 2.f, 0.f);
Inert* mesh = MakeEntity(Inert, manager, 1.f, 2.f, 0.f);
Assert(mesh != NULL);
MeshAssetID cubeAsset = GetCubePrimitiveMeshAssetID();
MeshInstanceID meshInst = CreateMeshInstance(cubeAsset, 0, MatrixIdentity());
Matrix identity = MatrixIdentity();
MeshInstanceID meshInst = CreateMeshInstance(cubeAsset, 0, &identity);
mesh->MeshInstance = meshInst;
NonNullPtr mesh2 = MakeEntity<Inert>(manager, 4.f, 0.f, 2.f);
Inert* mesh2 = MakeEntity(Inert, manager, 4.f, 0.f, 2.f);
Assert(mesh2 != NULL);
MeshInstanceID meshInst2 = CreateMeshInstance(cubeAsset, 0, MatrixIdentity());
MeshInstanceID meshInst2 = CreateMeshInstance(cubeAsset, 0, &identity);
mesh2->MeshInstance = meshInst2;
// Summer at 2pm lighting
@@ -97,64 +103,64 @@ extern "C" JULIET_API void __cdecl GameUpdate(GameData* params, [[maybe_unused]]
#endif
GameMode previousFrameMode = gGameState->Mode;
if (IsKeyPressed(ScanCode::F1))
if (IsKeyPressed(ScanCode_F1))
{
if (previousFrameMode == GameMode::Play)
if (previousFrameMode == GameMode_Play)
{
gGameState->Mode = GameMode::Debug;
gGameState->Mode = GameMode_Debug;
}
else if (previousFrameMode == GameMode::Debug)
else if (previousFrameMode == GameMode_Debug)
{
gGameState->Mode = GameMode::Play;
gGameState->Mode = GameMode_Play;
}
}
if (gGameState->Mode == GameMode::Editor)
if (gGameState->Mode == GameMode_Editor)
{
if (IsKeyPressed(ScanCode::F5))
if (IsKeyPressed(ScanCode_F5))
{
gGameState->Mode = GameMode::Play;
gGameState->Mode = GameMode_Play;
}
#if JULIET_EDITOR
RenderWorldEditorUI(*gGameState->World);
RenderWorldEditorUI(gGameState->World);
#endif
}
if (gGameState->Mode == GameMode::Play)
if (gGameState->Mode == GameMode_Play)
{
if (IsKeyPressed(ScanCode::Escape))
if (IsKeyPressed(ScanCode_Escape))
{
gGameState->Mode = GameMode::Editor;
gGameState->Mode = GameMode_Editor;
}
// update game
}
auto& manager = GetEntityManager();
EntityManager* manager = GetEntityManager();
UpdateEntityManager(manager);
if (gGameState->Mode == GameMode::Debug)
if (gGameState->Mode == GameMode_Debug)
{
if (previousFrameMode == GameMode::Play)
if (previousFrameMode == GameMode_Play)
{
ActivateDebugController();
}
if (IsKeyPressed(ScanCode::Escape))
if (IsKeyPressed(ScanCode_Escape))
{
gGameState->Mode = GameMode::Editor;
gGameState->Mode = GameMode_Editor;
}
UpdateDebugController(deltaTime);
#if JULIET_DEBUG
ImGui::Begin("Debug");
ImGui_Begin("Debug", NULL, ImGuiWindowFlags_None);
RenderImGuiDebugController(deltaTime);
ImGui::End();
ImGui_End();
#endif
}
if (gGameState->Mode != GameMode::Debug && previousFrameMode == GameMode::Debug)
if (gGameState->Mode != GameMode_Debug && previousFrameMode == GameMode_Debug)
{
DeactivateDebugController();
}
+13 -11
View File
@@ -3,25 +3,27 @@
#include <Core/Memory/MemoryArena.h>
#include <Data/World.h>
struct EntityManager;
typedef struct EntityManager EntityManager;
enum class GameMode
typedef uint32 GameMode;
enum
{
Editor,
Play,
Debug
GameMode_Editor = 0,
GameMode_Play,
GameMode_Debug
};
typedef struct GameState GameState;
struct GameState
{
Arena* TotalArena = nullptr;
Arena* TotalArena;
World* World = nullptr;
World* World;
GameMode Mode = GameMode::Editor;
GameMode Mode;
float TotalTime = 0.0f;
int Score = 0;
float TotalTime;
int Score;
};
[[nodiscard]] extern GameState* GetGameState();
extern GameState* GetGameState(void);