#include "main.h" #include #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 static bool ShowMemoryDebugger = false; // TODO : Replace with message box from framework + call main and not winmain + subsystem // TODO : Think how to do the draw pipeline. // Ex: Expose a Draw method ? // Store a graphics context ? // For now. Put everything in Update down below. // Should split update from draw, update should have a != timestep than graphics (60fps or more) // TODO : Remove main.h. Useless // May be remove that Application class, useless too. using namespace Juliet; extern "C" { __declspec(dllexport) extern const unsigned int D3D12SDKVersion = 615; } extern "C" { __declspec(dllexport) extern const char* D3D12SDKPath = ".\\"; } namespace { using GameInit_t = void (*)(GameInitParams*); using GameShutdown_t = void (*)(void); using GameUpdate_t = void (*)(float deltaTime); struct GameFunctionTable { GameInit_t Init = nullptr; GameShutdown_t Shutdown = nullptr; GameUpdate_t Update = nullptr; } Game; const char* GameFunctionTable[] = { "GameInit", "GameShutdown", "GameUpdate" }; Arena* PlatformArena = nullptr; } // namespace void JulietApplication::Init() { Log(LogLevel::Message, LogCategory::Tool, "Initializing Juliet Application..."); Log(LogLevel::Message, LogCategory::Tool, "%s", CStr(GetBasePath())); PlatformArena = ArenaAllocate({ .AllowRealloc = true } JULIET_DEBUG_PARAM("Platform Arena")); GraphicsConfig config; #if JULIET_DEBUG config.EnableDebug = true; #endif GraphicsDevice = CreateGraphicsDevice(config); MainWindow = CreatePlatformWindow("Juliet Editor", 1280, 720); Running = MainWindow != nullptr && GraphicsDevice != nullptr; if (Running) { AttachToWindow(GraphicsDevice, MainWindow); { Running = InitializeMeshRenderer(PlatformArena, GraphicsDevice, MainWindow); // Create Depth Buffer TextureCreateInfo depthCI = {}; depthCI.Type = TextureType::Texture_2D; depthCI.Width = 1280; depthCI.Height = 720; depthCI.Format = TextureFormat::D32_FLOAT; depthCI.Flags = TextureUsageFlag::DepthStencilTarget; depthCI.LayerCount = 1; depthCI.MipLevelCount = 1; depthCI.SampleCount = TextureSampleCount::One; DepthBuffer = CreateTexture(GraphicsDevice, depthCI); if (DepthBuffer == nullptr) { LogError(LogCategory::Game, "Failed to create depth buffer!"); Running = false; } std::ignore = AddCube(); CommandList* loadCmd = AcquireCommandList(GraphicsDevice); LoadMeshesOnGPU(loadCmd); SubmitCommandLists(loadCmd); if (Running == false) { return; } } GameCode.Functions = reinterpret_cast(&Game); GameCode.FunctionCount = ArraySize(GameFunctionTable); GameCode.FunctionNames = GameFunctionTable; InitHotReloadCode(GameCode, ConstString("Game.dll"), ConstString("Game_Temp.dll"), ConstString("lock.tmp")); if ((Running = GameCode.IsValid)) { GameInitParams params; params.GameArena = GameArena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "Game Arena")); params.ScratchArena = GameScratchArena = ArenaAllocate({} JULIET_DEBUG_ONLY(, "Scratch Arena")); Game.Init(¶ms); } } } void JulietApplication::Shutdown() { Log(LogLevel::Message, LogCategory::Tool, "Shutting down Juliet Application..."); if (GameCode.IsValid) { Game.Shutdown(); ShutdownHotReloadCode(GameCode); } if (GraphicsPipeline) { DestroyGraphicsPipeline(GraphicsDevice, GraphicsPipeline); } if (DepthBuffer) { DestroyTexture(GraphicsDevice, DepthBuffer); } ShutdownMeshRenderer(); if (MainWindow && GraphicsDevice) { DetachFromWindow(GraphicsDevice, MainWindow); } if (MainWindow) { DestroyPlatformWindow(MainWindow); } if (GraphicsDevice) { DestroyGraphicsDevice(GraphicsDevice); } ArenaRelease(PlatformArena); Log(LogLevel::Message, LogCategory::Tool, "Juliet App shutdown Completed"); } void JulietApplication::Update() { bool reloadShaders = false; static bool reloadShadersDebounce = false; SystemEvent evt; while (GetEvent(evt)) { if (evt.Type == EventType::Window_Close_Request) { if (evt.Data.Window.AssociatedWindowID == GetWindowID(MainWindow)) { 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; } } #ifdef JULIET_ENABLE_IMGUI // ImGui::ShowDemoWindow(); #endif DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f }, false); DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f }, true); DebugDisplay_DrawLine({ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f }, true); DebugDisplay_DrawSphere({ 0.0f, 0.0f, 0.0f }, 0.5f, { 1.0f, 1.0f, 0.0f, 1.0f }, true); Game.Update(0.0f); if (ShouldReloadCode(GameCode)) { ReloadCode(GameCode); } if (reloadShaders) { WaitUntilGPUIsIdle(GraphicsDevice); #if ALLOW_SHADER_HOT_RELOAD ReloadMeshRendererShaders(); #endif } // Memory debugger toggle static bool toggleDebounce = false; if (IsKeyDown(Juliet::ScanCode::Home)) { if (!toggleDebounce) { ShowMemoryDebugger = !ShowMemoryDebugger; toggleDebounce = true; } } else { toggleDebounce = false; } // Auto-close logic if (AutoCloseFrameCount > 0) { AutoCloseFrameCount--; if (AutoCloseFrameCount == 0) { Log(LogLevel::Message, LogCategory::Tool, "Auto-closing application as requested."); Running = false; } } if (ShowMemoryDebugger) { #if JULIET_DEBUG Debug::DebugDrawMemoryArena(); #endif } ArenaClear(GameScratchArena); } void JulietApplication::OnPreRender(CommandList* /*cmd*/) {} void JulietApplication::OnRender(RenderPass* pass, CommandList* cmd) { PushData pushData = {}; pushData.ViewProjection = Camera_GetViewProjectionMatrix(GetDebugCamera()); RenderMeshes(pass, cmd, pushData); // SetPushConstants(cmd, ShaderStage::Vertex, 0, sizeof(pushData) / 4, &pushData); } ColorTargetInfo JulietApplication::GetColorTargetInfo(Texture* swapchainTexture) { ColorTargetInfo info = {}; info.TargetTexture = swapchainTexture; info.ClearColor = { .R = 0.0f, .G = 0.0f, .B = 0.0f, .A = 1.0f }; info.LoadOperation = LoadOperation::Clear; info.StoreOperation = StoreOperation::Store; return info; } DepthStencilTargetInfo* JulietApplication::GetDepthTargetInfo() { static DepthStencilTargetInfo info = {}; info.TargetTexture = DepthBuffer; info.ClearDepth = 1.0f; info.LoadOperation = LoadOperation::Clear; info.StoreOperation = StoreOperation::Store; return &info; } Camera JulietApplication::GetDebugCamera() { static float time = 0.0f; time += 0.016f; float orbitSpeed = 0.5f; float currentOrbitTime = time * orbitSpeed; // --- Adjusted for 1-Meter Scale --- float baseRadius = 2.5f; // Hover 2.5 meters away (down from 15.0f) float radius = baseRadius; /* Uncomment for active zoom float zoomAmplitude = 1.0f; // Oscillate between 1.5m and 3.5m away float zoomSpeed = 0.8f; radius = baseRadius + (sinf(time * zoomSpeed) * zoomAmplitude); */ float zHeight = radius * 0.5f; // Keep a nice downward viewing angle Camera cam = {}; cam.Position = { cosf(currentOrbitTime) * radius, sinf(currentOrbitTime) * radius, zHeight }; cam.Target = { 0.0f, 0.0f, 0.0f }; cam.Up = { 0.0f, 0.0f, 1.0f }; cam.FOV = 1.047f; cam.AspectRatio = 1200.0f / 800.0f; cam.NearPlane = 0.1f; cam.FarPlane = 1000.0f; return cam; } bool JulietApplication::IsRunning() { return Running; } namespace { JulietApplication EditorApplication; } JulietApplication& GetEditorApplication() { return EditorApplication; } 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]); EditorApplication.SetAutoCloseFrameCount(frames); } } } StartApplication(EditorApplication, JulietInit_Flags::Display); // Pause here to not close the console window immediatly on stop // Only pause if not in auto-close mode if (EditorApplication.GetAutoCloseFrameCount() == -1 && !Debug::IsDebuggerPresent()) { system("PAUSE"); } return EXIT_SUCCESS; }