Files
Juliet/JulietApp/main.c
T

349 lines
9.8 KiB
C

#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#endif
#include <Core/Common/String.h>
#include <Core/HAL/Display/Display.h>
#include <Core/HAL/Event/SystemEvent.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HotReload/HotReload.h>
#include <Core/JulietInit.h>
#include <Core/Logging/LogManager.h>
#include <Core/Logging/LogTypes.h>
#include <Core/Main.h>
#include <Core/Math/Matrix.h>
#include <Core/Memory/MemoryArena.h>
#include <Core/Memory/Utils.h>
#include <Engine/Debug/MemoryDebugger.h>
#include <Engine/Engine.h>
#include <Graphics/Camera.h>
#include <Graphics/DebugDisplay.h>
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsConfig.h>
#include <Graphics/GraphicsPipeline.h>
#include <Graphics/Mesh.h>
#include <Graphics/MeshRenderer.h>
#include <Graphics/RenderPass.h>
#include <Graphics/SkyboxRenderer.h>
#include <Graphics/VertexData.h>
#ifdef JULIET_ENABLE_IMGUI
#include <dcimgui.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
global bool8 g_show_memory_debugger = false;
typedef void (*GameShutdown_t)(void);
typedef void (*GameUpdate_t)(GameData* params, float deltaTime);
typedef struct GameFunctionTableStruct GameFunctionTableStruct;
struct GameFunctionTableStruct
{
GameShutdown_t Shutdown;
GameUpdate_t Update;
};
global GameFunctionTableStruct g_game = { 0 };
global GameData g_game_data = { 0 };
global const char* g_game_function_names[] = { "GameShutdown", "GameUpdate" };
typedef struct JulietAppState JulietAppState;
struct JulietAppState
{
Window* MainWindow;
GraphicsDevice* GraphicsDevice;
GraphicsPipeline* GraphicsPipeline;
Texture* DepthBuffer;
HotReloadCode GameCode;
bool8 Running;
float CameraTime;
int AutoCloseFrameCount;
};
global JulietAppState g_app = {
.MainWindow = NULL,
.GraphicsDevice = NULL,
.GraphicsPipeline = NULL,
.DepthBuffer = NULL,
.Running = false,
.CameraTime = 0.0f,
.AutoCloseFrameCount = -1
};
internal void App_Init(Arena* platformArena)
{
Assert(platformArena != NULL);
Log(LogLevel_Message, LogCategory_Tool, "Initializing Juliet Application...");
Log(LogLevel_Message, LogCategory_Tool, "%s", CStr(GetBasePath().Directories));
GraphicsConfig config = { 0 };
#if JULIET_DEBUG
config.EnableDebug = true;
#endif
g_app.GraphicsDevice = CreateGraphicsDevice(config);
g_app.MainWindow = CreatePlatformWindow("Juliet", 1920, 1080, 0);
g_app.Running = (g_app.MainWindow != NULL && g_app.GraphicsDevice != NULL);
if (g_app.Running)
{
AttachToWindow(g_app.GraphicsDevice, g_app.MainWindow);
{
// Create Depth Buffer
TextureCreateInfo depthCI = { 0 };
depthCI.Type = TextureType_2D;
depthCI.Width = 1920;
depthCI.Height = 1080;
depthCI.Format = TextureFormat_D32_FLOAT;
depthCI.Flags = TextureUsageFlag_DepthStencilTarget;
depthCI.LayerCount = 1;
depthCI.MipLevelCount = 1;
depthCI.SampleCount = TextureSampleCount_One;
g_app.DepthBuffer = CreateTexture(g_app.GraphicsDevice, &depthCI);
if (g_app.DepthBuffer == NULL)
{
LogError(LogCategory_Game, "Failed to create depth buffer!");
g_app.Running = false;
}
}
g_app.GameCode.Functions = (void**)&g_game;
g_app.GameCode.FunctionCount = ArraySize(g_game_function_names);
g_app.GameCode.FunctionNames = g_game_function_names;
InitHotReloadCode(platformArena, &g_app.GameCode, ConstString("Game.dll"), ConstString("Game_Temp.dll"),
ConstString("lock.tmp"));
g_app.Running = g_app.GameCode.IsValid;
}
}
internal void App_Shutdown(void)
{
Log(LogLevel_Message, LogCategory_Tool, "Shutting down Juliet Application...");
if (g_app.GameCode.IsValid)
{
g_game.Shutdown();
ShutdownHotReloadCode(&g_app.GameCode);
}
if (g_app.GraphicsPipeline != NULL)
{
DestroyGraphicsPipeline(g_app.GraphicsDevice, g_app.GraphicsPipeline);
}
if (g_app.DepthBuffer != NULL)
{
DestroyTexture(g_app.GraphicsDevice, g_app.DepthBuffer);
}
if (g_app.MainWindow != NULL && g_app.GraphicsDevice != NULL)
{
DetachFromWindow(g_app.GraphicsDevice, g_app.MainWindow);
}
if (g_app.MainWindow != NULL)
{
DestroyPlatformWindow(g_app.MainWindow);
}
if (g_app.GraphicsDevice != NULL)
{
DestroyGraphicsDevice(g_app.GraphicsDevice);
}
Log(LogLevel_Message, LogCategory_Tool, "Juliet App shutdown Completed");
}
internal void App_Update(float deltaTime)
{
g_app.CameraTime += deltaTime;
static float fpsTimer = 0.0f;
static int fpsFrames = 0;
fpsTimer += deltaTime;
fpsFrames++;
if (fpsTimer >= 0.5f)
{
float fps = (float)fpsFrames / fpsTimer;
float ms = (fpsTimer / (float)fpsFrames) * 1000.0f;
char title[64];
snprintf(title, sizeof(title), "Juliet | %.1f FPS | %.2f ms", (double)fps, (double)ms);
SetWindowTitle(g_app.MainWindow, WrapString(title));
fpsTimer = 0.0f;
fpsFrames = 0;
}
bool8 reloadShaders = false;
static bool8 reloadShadersDebounce = false;
SystemEvent evt = { 0 };
while (GetEvent(&evt))
{
if (evt.Type == EventType_Window_Close_Request)
{
if (evt.Data.Window.AssociatedWindowID == GetWindowID(g_app.MainWindow))
{
g_app.Running = false;
}
}
// Shader hot reload using keyboard.
if (!reloadShadersDebounce && ((GetKeyModState() & KeyMod_Alt) != KeyMod_None) && IsKeyDown(ScanCode_R))
{
reloadShaders = true;
reloadShadersDebounce = true;
}
if (reloadShadersDebounce && !IsKeyDown(ScanCode_R))
{
reloadShadersDebounce = false;
}
}
Vector3 origin = { 0.0f, 0.0f, 0.0f };
Vector3 xAxis = { 1.0f, 0.0f, 0.0f };
Vector3 yAxis = { 0.0f, 1.0f, 0.0f };
Vector3 zAxis = { 0.0f, 0.0f, 1.0f };
FColor redCol = { 1.0f, 0.0f, 0.0f, 1.0f };
FColor greenCol = { 0.0f, 1.0f, 0.0f, 1.0f };
FColor blueCol = { 0.0f, 0.0f, 1.0f, 1.0f };
FColor yelCol = { 1.0f, 1.0f, 0.0f, 1.0f };
DebugDisplay_DrawLine(origin, xAxis, redCol, false);
DebugDisplay_DrawLine(origin, yAxis, greenCol, true);
DebugDisplay_DrawLine(origin, zAxis, blueCol, true);
DebugDisplay_DrawSphere(origin, 0.5f, yelCol, true);
g_game.Update(&g_game_data, deltaTime);
if (ShouldReloadCode(&g_app.GameCode))
{
ReloadCode(&g_app.GameCode);
}
if (reloadShaders)
{
WaitUntilGPUIsIdle(g_app.GraphicsDevice);
#if ALLOW_SHADER_HOT_RELOAD
ReloadMeshRendererShaders();
#endif
}
// Memory debugger toggle
static bool8 toggleDebounce = false;
if (IsKeyDown(ScanCode_Home))
{
if (!toggleDebounce)
{
g_show_memory_debugger = !g_show_memory_debugger;
toggleDebounce = true;
}
}
else
{
toggleDebounce = false;
}
// Auto-close logic
if (g_app.AutoCloseFrameCount > 0)
{
g_app.AutoCloseFrameCount--;
if (g_app.AutoCloseFrameCount == 0)
{
Log(LogLevel_Message, LogCategory_Tool, "Auto-closing application as requested.");
g_app.Running = false;
}
}
if (g_show_memory_debugger)
{
#if JULIET_DEBUG
Debug_DebugDrawMemoryArena();
#endif
}
}
internal ColorTargetInfo App_GetColorTargetInfo(Texture* swapchainTexture)
{
ColorTargetInfo info = { 0 };
info.TargetTexture = swapchainTexture;
info.ClearColor = (FColor){ .R = 0.0f, .G = 0.0f, .B = 0.0f, .A = 1.0f };
info.LoadOperation = LoadOperation_Clear;
info.StoreOperation = StoreOperation_Store;
return info;
}
internal DepthStencilTargetInfo* App_GetDepthTargetInfo(void)
{
static DepthStencilTargetInfo info = { 0 };
info.TargetTexture = g_app.DepthBuffer;
info.ClearDepth = 1.0f;
info.LoadOperation = LoadOperation_Clear;
info.StoreOperation = StoreOperation_Store;
return &info;
}
internal bool8 App_IsRunning(void)
{
return g_app.Running;
}
internal Window* App_GetPlatformWindow(void)
{
return g_app.MainWindow;
}
internal GraphicsDevice* App_GetGraphicsDevice(void)
{
return g_app.GraphicsDevice;
}
int JulietMain(int argc, wchar_t** argv)
{
if (argc > 1)
{
for (int i = 1; i < argc; ++i)
{
if (wcscmp(argv[i], L"-autoclose") == 0 && (i + 1 < argc))
{
int frames = _wtoi(argv[i + 1]);
g_app.AutoCloseFrameCount = frames;
}
}
}
ApplicationCallbacks appCallbacks = {
.Init = App_Init,
.Shutdown = App_Shutdown,
.Update = App_Update,
.IsRunning = App_IsRunning,
.GetPlatformWindow = App_GetPlatformWindow,
.GetGraphicsDevice = App_GetGraphicsDevice,
.GetColorTargetInfo = App_GetColorTargetInfo,
.GetDepthTargetInfo = App_GetDepthTargetInfo,
};
StartApplication(appCallbacks, JulietInit_Flags_Display);
// Pause here to not close the console window immediately on stop
// Only pause if not in auto-close mode
if (g_app.AutoCloseFrameCount == -1 && !Debug_IsDebuggerPresent())
{
system("PAUSE");
}
return EXIT_SUCCESS;
}