#include "Application.h" #include #include #include #include extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); namespace UI { Application& Application::Get() { static Application instance; return instance; } bool Application::Initialize(HWND hwnd) { m_hwnd = hwnd; if (!CreateDevice()) { MessageBoxW(hwnd, L"Failed to create D3D12 device", L"Error", MB_OK | MB_ICONERROR); return false; } if (!CreateRenderTarget()) { MessageBoxW(hwnd, L"Failed to create render target", L"Error", MB_OK | MB_ICONERROR); return false; } IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.Fonts->AddFontFromFileTTF("C:/Windows/Fonts/msyh.ttc", 16.0f); io.Fonts->AddFontDefault(); unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); ApplyUnityDarkTheme(); ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX12_Init(m_device, 3, DXGI_FORMAT_R8G8B8A8_UNORM, m_srvHeap, m_srvHeap->GetCPUDescriptorHandleForHeapStart(), m_srvHeap->GetGPUDescriptorHandleForHeapStart()); m_menuBar = std::make_unique(); m_hierarchyPanel = std::make_unique(); m_sceneViewPanel = std::make_unique(); m_gameViewPanel = std::make_unique(); m_inspectorPanel = std::make_unique(); m_consolePanel = std::make_unique(); m_projectPanel = std::make_unique(); wchar_t exePath[MAX_PATH]; GetModuleFileNameW(nullptr, exePath, MAX_PATH); std::wstring exeDirW(exePath); size_t pos = exeDirW.find_last_of(L"\\/"); if (pos != std::wstring::npos) { exeDirW = exeDirW.substr(0, pos); } std::string exeDir; int len = WideCharToMultiByte(CP_UTF8, 0, exeDirW.c_str(), -1, nullptr, 0, nullptr, nullptr); if (len > 0) { exeDir.resize(len - 1); WideCharToMultiByte(CP_UTF8, 0, exeDirW.c_str(), -1, &exeDir[0], len, nullptr, nullptr); } m_projectPanel->Initialize(exeDir); return true; } void Application::Shutdown() { ImGui_ImplDX12_Shutdown(); ImGui_ImplWin32_Shutdown(); ImGui::DestroyContext(); CleanupRenderTarget(); if (m_fence) m_fence->Release(); if (m_commandList) m_commandList->Release(); if (m_commandAllocator) m_commandAllocator->Release(); if (m_commandQueue) m_commandQueue->Release(); if (m_rtvHeap) m_rtvHeap->Release(); if (m_srvHeap) m_srvHeap->Release(); if (m_swapChain) m_swapChain->Release(); if (m_device) m_device->Release(); } void Application::Render() { ImGui_ImplDX12_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); SetupDockspace(); RenderUI(); ImGui::Render(); m_frameIndex = m_swapChain->GetCurrentBackBufferIndex(); m_commandAllocator->Reset(); m_commandList->Reset(m_commandAllocator, nullptr); D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; barrier.Transition.pResource = m_renderTargets[m_frameIndex]; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; m_commandList->ResourceBarrier(1, &barrier); float clearColor[4] = { 0.12f, 0.12f, 0.12f, 1.0f }; D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart(); rtvHandle.ptr += m_frameIndex * m_rtvDescriptorSize; m_commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); m_commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr); ID3D12DescriptorHeap* heaps[] = { m_srvHeap }; m_commandList->SetDescriptorHeaps(1, heaps); ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), m_commandList); barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; m_commandList->ResourceBarrier(1, &barrier); m_commandList->Close(); ID3D12CommandList* cmdLists[] = { m_commandList }; m_commandQueue->ExecuteCommandLists(1, cmdLists); m_swapChain->Present(1, 0); m_fenceValue++; m_commandQueue->Signal(m_fence, m_fenceValue); if (m_fence->GetCompletedValue() < m_fenceValue) { m_fence->SetEventOnCompletion(m_fenceValue, nullptr); } } void Application::OnResize(int width, int height) { if (width <= 0 || height <= 0) return; m_width = width; m_height = height; CleanupRenderTarget(); if (m_swapChain) { DXGI_SWAP_CHAIN_DESC desc; m_swapChain->GetDesc(&desc); m_swapChain->ResizeBuffers(3, width, height, desc.BufferDesc.Format, desc.Flags); } CreateRenderTarget(); } bool Application::CreateDevice() { HRESULT hr = D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)); if (FAILED(hr)) { return false; } D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; queueDesc.Priority = 0; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.NodeMask = 0; hr = m_device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_commandQueue)); if (FAILED(hr)) return false; hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&m_commandAllocator)); if (FAILED(hr)) return false; hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator, nullptr, IID_PPV_ARGS(&m_commandList)); if (FAILED(hr)) return false; m_commandList->Close(); hr = m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)); if (FAILED(hr)) return false; IDXGIFactory4* factory = nullptr; hr = CreateDXGIFactory1(IID_PPV_ARGS(&factory)); if (FAILED(hr)) return false; DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.BufferCount = 3; swapChainDesc.Width = m_width; swapChainDesc.Height = m_height; swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; swapChainDesc.SampleDesc.Count = 1; IDXGISwapChain1* swapChain1 = nullptr; hr = factory->CreateSwapChainForHwnd(m_commandQueue, m_hwnd, &swapChainDesc, nullptr, nullptr, &swapChain1); factory->Release(); if (FAILED(hr)) return false; hr = swapChain1->QueryInterface(IID_PPV_ARGS(&m_swapChain)); swapChain1->Release(); if (FAILED(hr)) return false; D3D12_DESCRIPTOR_HEAP_DESC rtvDesc = {}; rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; rtvDesc.NumDescriptors = 3; rtvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; hr = m_device->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(&m_rtvHeap)); if (FAILED(hr)) return false; m_rtvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); D3D12_DESCRIPTOR_HEAP_DESC srvDesc = {}; srvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; srvDesc.NumDescriptors = 1; srvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; hr = m_device->CreateDescriptorHeap(&srvDesc, IID_PPV_ARGS(&m_srvHeap)); if (FAILED(hr)) return false; return true; } bool Application::CreateRenderTarget() { if (!m_swapChain || !m_device || !m_rtvHeap) return false; for (UINT i = 0; i < 3; i++) { HRESULT hr = m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_renderTargets[i])); if (FAILED(hr)) return false; D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = m_rtvHeap->GetCPUDescriptorHandleForHeapStart(); rtvHandle.ptr += i * m_rtvDescriptorSize; m_device->CreateRenderTargetView(m_renderTargets[i], nullptr, rtvHandle); } return true; } void Application::CleanupRenderTarget() { for (UINT i = 0; i < 3; i++) { if (m_renderTargets[i]) { m_renderTargets[i]->Release(); m_renderTargets[i] = nullptr; } } } void Application::SetupDockspace() { static ImGuiDockNodeFlags dockspaceFlags = ImGuiDockNodeFlags_NoWindowMenuButton; ImGuiWindowFlags windowFlags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->Pos); ImGui::SetNextWindowSize(viewport->Size); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("MainDockspace", nullptr, windowFlags); ImGui::PopStyleVar(); ImGui::PopStyleVar(2); ImGuiID dockspaceId = ImGui::GetID("MyDockspace"); ImGui::DockSpace(dockspaceId, ImVec2(0.0f, 0.0f), dockspaceFlags); static bool firstTime = true; if (firstTime) { firstTime = false; ImGui::DockBuilderRemoveNode(dockspaceId); ImGui::DockBuilderAddNode(dockspaceId, dockspaceFlags | ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockspaceId, viewport->Size); ImGuiID dockMain = dockspaceId; ImGuiID dockBottom = ImGui::DockBuilderSplitNode(dockMain, ImGuiDir_Down, 0.25f, nullptr, &dockMain); ImGuiID dockLeft = ImGui::DockBuilderSplitNode(dockMain, ImGuiDir_Left, 0.15f, nullptr, &dockMain); ImGuiID dockRight = ImGui::DockBuilderSplitNode(dockMain, ImGuiDir_Right, 0.25f, nullptr, &dockMain); ImGui::DockBuilderDockWindow("Hierarchy", dockLeft); ImGui::DockBuilderDockWindow("Scene", dockMain); ImGui::DockBuilderDockWindow("Game", dockMain); ImGui::DockBuilderDockWindow("Inspector", dockRight); ImGui::DockBuilderDockWindow("Console", dockBottom); ImGui::DockBuilderDockWindow("Project", dockBottom); ImGui::DockBuilderFinish(dockspaceId); } ImGui::End(); } void Application::RenderUI() { m_menuBar->Render(); m_hierarchyPanel->Render(); m_sceneViewPanel->Render(); m_gameViewPanel->Render(); m_inspectorPanel->Render(); m_consolePanel->Render(); m_projectPanel->Render(); } }