#ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef JULIET_ENABLE_IMGUI #include #endif #include #include #include 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; }