#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // 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; namespace { using GameInit_t = void (*)(void); 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" }; } // namespace void JulietApplication::Init() { Log(LogLevel::Message, LogCategory::Tool, "Initializing Juliet Application..."); Log(LogLevel::Message, LogCategory::Tool, "%s", CStr(GetBasePath())); 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); { // Create graphics pipeline String entryPoint = WrapString("main"); ShaderCreateInfo shaderCI = {}; shaderCI.EntryPoint = entryPoint; // TODO: Assets management that handles path to assets or something. String shaderPath = WrapString("../../../Assets/compiled/Triangle.vert.dxil"); shaderCI.Stage = ShaderStage::Vertex; Shader* vertexShader = CreateShader(GraphicsDevice, shaderPath, shaderCI); shaderPath = WrapString("../../../Assets/compiled/SolidColor.frag.dxil"); shaderCI.Stage = ShaderStage::Fragment; Shader* fragmentShader = CreateShader(GraphicsDevice, shaderPath, shaderCI); ColorTargetDescription colorTargetDescription = {}; colorTargetDescription.Format = GetSwapChainTextureFormat(GraphicsDevice, MainWindow); GraphicsPipelineCreateInfo pipelineCI = {}; pipelineCI.VertexShader = vertexShader; pipelineCI.FragmentShader = fragmentShader; pipelineCI.PrimitiveType = PrimitiveType::TriangleList; pipelineCI.TargetInfo = { .ColorTargetDescriptions = &colorTargetDescription, .NumColorTargets = 1, .DepthStencilFormat = {}, .HasDepthStencilTarget = false }; pipelineCI.RasterizerState.FillMode = FillMode::Solid; GraphicsPipeline = CreateGraphicsPipeline(GraphicsDevice, pipelineCI); if (GraphicsPipeline == nullptr) { LogError(LogCategory::Game, "Failed to create graphics pipeline!"); Running = false; } if (vertexShader) { DestroyShader(GraphicsDevice, vertexShader); } if (fragmentShader) { DestroyShader(GraphicsDevice, fragmentShader); } 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)) { Game.Init(); } } } 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 (MainWindow && GraphicsDevice) { DetachFromWindow(GraphicsDevice, MainWindow); } if (MainWindow) { DestroyPlatformWindow(MainWindow); } if (GraphicsDevice) { DestroyGraphicsDevice(GraphicsDevice); } 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; } } if (evt.Type == EventType::Key_Down) { } // Shader hot reload using keyboard. // TODO: Add Input debounce in the library. (Just pressed vs pressed) if (!reloadShadersDebounce && ((GetKeyModState() & KeyMod::Alt) != KeyMod::None) && IsKeyDown(ScanCode::R)) { reloadShaders = true; reloadShadersDebounce = true; } if (reloadShadersDebounce && !IsKeyDown(ScanCode::R)) { reloadShadersDebounce = false; } } Game.Update(0.0f); if (ShouldReloadCode(GameCode)) { ReloadCode(GameCode); } if (reloadShaders) { // We need to wait for the gpu to be idle to recreate our graphics pipelines WaitUntilGPUIsIdle(GraphicsDevice); #if ALLOW_SHADER_HOT_RELOAD String entryPoint = WrapString("main"); ShaderCreateInfo shaderCI = {}; shaderCI.EntryPoint = entryPoint; String shaderPath = WrapString("../../../Assets/compiled/Triangle.vert.dxil"); shaderCI.Stage = ShaderStage::Vertex; Shader* vertexShader = CreateShader(GraphicsDevice, shaderPath, shaderCI); shaderPath = WrapString("../../../Assets/compiled/SolidColor.frag.dxil"); shaderCI.Stage = ShaderStage::Fragment; Shader* fragmentShader = CreateShader(GraphicsDevice, shaderPath, shaderCI); UpdateGraphicsPipelineShaders(GraphicsDevice, GraphicsPipeline, vertexShader, fragmentShader); if (vertexShader) { DestroyShader(GraphicsDevice, vertexShader); } if (fragmentShader) { DestroyShader(GraphicsDevice, fragmentShader); } #endif } // Draw here for now // 1) Acquire a Command Buffer CommandList* cmdList = AcquireCommandList(GraphicsDevice, QueueType::Graphics); if (cmdList == nullptr) { Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire command list."); Running = false; return; } Texture* swapChainTexture = nullptr; if (!WaitAndAcquireSwapChainTexture(cmdList, MainWindow, &swapChainTexture)) { Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire swapchain texture."); Running = false; return; } if (swapChainTexture) { ColorTargetInfo colorTargetInfo = {}; colorTargetInfo.TargetTexture = swapChainTexture; colorTargetInfo.ClearColor = { .R = .5f, .G = .8f, .B = .0f, .A = 1.f }; colorTargetInfo.LoadOperation = LoadOperation::Clear; colorTargetInfo.StoreOperation = StoreOperation::Store; RenderPass* renderPass = BeginRenderPass(cmdList, colorTargetInfo); BindGraphicsPipeline(renderPass, GraphicsPipeline); DrawPrimitives(renderPass, 3, 1, 0, 0); EndRenderPass(renderPass); } // Submit Commands SubmitCommandLists(cmdList); } bool JulietApplication::IsRunning() { return Running; } namespace { JulietApplication EditorApplication; } JulietApplication& GetEditorApplication() { return EditorApplication; } int main(int argc, char** argv) { // Allow only one instance to be launched. // Need to create Mutex code in the lib because i dont want to include windows.h in this file anymore // CreateMutex(0, false, L"Local\\Juliet.App"); // if (GetLastError() == ERROR_ALREADY_EXISTS) // { // MessageBox(nullptr, L"An instance of Juliet is already running.", L"Juliet", MB_OK | MB_ICONEXCLAMATION); // return EXIT_FAILURE; // } StartApplication(EditorApplication, JulietInit_Flags::Display); // Pause here to not close the console window immediatly on stop system("PAUSE"); return EXIT_SUCCESS; }