349 lines
9.8 KiB
C
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;
|
|
}
|