456 lines
18 KiB
C++
456 lines
18 KiB
C++
// Dear ImGui: standalone example application for DirectX 11
|
|
|
|
// Learn about Dear ImGui:
|
|
// - FAQ https://dearimgui.com/faq
|
|
// - Getting Started https://dearimgui.com/getting-started
|
|
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
|
|
// - Introduction, links and more at the top of imgui.cpp
|
|
|
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
|
#include "imgui.h"
|
|
#include "imgui_impl_win32.h"
|
|
#include "imgui_impl_dx11.h"
|
|
#include <d3d11.h>
|
|
#include <tchar.h>
|
|
#include <misc/freetype/imgui_freetype.h>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <cmath>
|
|
|
|
#include "imgui_internal.h"
|
|
#include "fonts/montserrat.c"
|
|
|
|
// Data
|
|
static ID3D11Device* g_pd3dDevice = nullptr;
|
|
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
|
|
static IDXGISwapChain* g_pSwapChain = nullptr;
|
|
static bool g_SwapChainOccluded = false;
|
|
static UINT g_ResizeWidth = 0, g_ResizeHeight = 0;
|
|
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
|
|
|
|
#define IM_PI 3.14159265358979323846f
|
|
|
|
// Forward declarations of helper functions
|
|
bool CreateDeviceD3D(HWND hWnd);
|
|
void CleanupDeviceD3D();
|
|
void CreateRenderTarget();
|
|
void CleanupRenderTarget();
|
|
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
void draw_star(ImVec2 center, float outerRadius, float innerRadius, int numPoints, ImU32 color, float rotation)
|
|
{
|
|
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
|
int totalPoints = numPoints * 2;
|
|
|
|
for (int i = 0; i < totalPoints; i++)
|
|
{
|
|
float angle = rotation + ((float)i / totalPoints) * 2.0f * IM_PI - (IM_PI / 2.0f);
|
|
float radius = (i % 2 == 0) ? outerRadius : innerRadius;
|
|
drawList->PathLineTo(ImVec2(
|
|
center.x + cosf(angle) * radius,
|
|
center.y + sinf(angle) * radius
|
|
));
|
|
}
|
|
|
|
drawList->PathFillConcave(color);
|
|
}
|
|
|
|
// Main code
|
|
int main(int, char**)
|
|
{
|
|
// Create application window
|
|
//ImGui_ImplWin32_EnableDpiAwareness();
|
|
WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };
|
|
::RegisterClassExW(&wc);
|
|
HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, nullptr, nullptr, wc.hInstance, nullptr);
|
|
|
|
// Initialize Direct3D
|
|
if (!CreateDeviceD3D(hwnd))
|
|
{
|
|
CleanupDeviceD3D();
|
|
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
|
return 1;
|
|
}
|
|
|
|
// Show the window
|
|
::ShowWindow(hwnd, SW_SHOWDEFAULT);
|
|
::UpdateWindow(hwnd);
|
|
|
|
// Setup Dear ImGui context
|
|
IMGUI_CHECKVERSION();
|
|
ImGui::CreateContext();
|
|
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
|
|
|
// Setup Dear ImGui style
|
|
ImGui::StyleColorsDark();
|
|
//ImGui::StyleColorsLight();
|
|
|
|
// Setup Platform/Renderer backends
|
|
ImGui_ImplWin32_Init(hwnd);
|
|
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
|
|
|
|
ImFontConfig cfg;
|
|
cfg.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_LoadColor;
|
|
cfg.PixelSnapH = true;
|
|
cfg.SizePixels = 18.0f;
|
|
cfg.RasterizerMultiply = 1.0f;
|
|
|
|
ImFont* font = io.Fonts->AddFontFromMemoryTTF(montserrat, sizeof(montserrat), 18.0f, &cfg, io.Fonts->GetGlyphRangesCyrillic());
|
|
|
|
// Our state
|
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
|
|
|
bool test_bool = false;
|
|
float test_float = 0.f;
|
|
int test_int = 0;
|
|
|
|
// Main loop
|
|
bool done = false;
|
|
while (!done)
|
|
{
|
|
// Poll and handle messages (inputs, window resize, etc.)
|
|
// See the WndProc() function below for our to dispatch events to the Win32 backend.
|
|
MSG msg;
|
|
while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE))
|
|
{
|
|
::TranslateMessage(&msg);
|
|
::DispatchMessage(&msg);
|
|
if (msg.message == WM_QUIT)
|
|
done = true;
|
|
}
|
|
if (done)
|
|
break;
|
|
|
|
// Handle window being minimized or screen locked
|
|
if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
|
|
{
|
|
::Sleep(10);
|
|
continue;
|
|
}
|
|
g_SwapChainOccluded = false;
|
|
|
|
// Handle window resize (we don't resize directly in the WM_SIZE handler)
|
|
if (g_ResizeWidth != 0 && g_ResizeHeight != 0)
|
|
{
|
|
CleanupRenderTarget();
|
|
g_pSwapChain->ResizeBuffers(0, g_ResizeWidth, g_ResizeHeight, DXGI_FORMAT_UNKNOWN, 0);
|
|
g_ResizeWidth = g_ResizeHeight = 0;
|
|
CreateRenderTarget();
|
|
}
|
|
|
|
// Start the Dear ImGui frame
|
|
ImGui_ImplDX11_NewFrame();
|
|
ImGui_ImplWin32_NewFrame();
|
|
ImGui::NewFrame();
|
|
|
|
ImGui::ShowDemoWindow();
|
|
|
|
ImGui::PushFontShadow(IM_COL32(0, 0, 0, 0));
|
|
ImGui::SetNextWindowBgAlpha(1.f);
|
|
ImGui::SetNextWindowSize(ImVec2(620, 620));
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 10.f);
|
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.f);
|
|
ImGui::Begin("##mocha_main", 0, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoBackground);
|
|
{
|
|
auto pos = ImGui::GetWindowPos();
|
|
auto size = ImGui::GetWindowSize();
|
|
auto draw = ImGui::GetWindowDrawList();
|
|
auto b_draw = ImGui::GetBackgroundDrawList();
|
|
|
|
/*const int shadow_layers = 10;
|
|
const float shadow_size = 5.0f;
|
|
for (int i = 0; i < shadow_layers; ++i)
|
|
{
|
|
const float t = (float)(i + 1) / (float)shadow_layers;
|
|
const float expand = t * shadow_size;
|
|
const int alpha = (int)(40.0f * (1.0f - t));
|
|
b_draw->AddRectFilled(
|
|
ImVec2(pos.x - expand, pos.y - expand),
|
|
ImVec2(pos.x + size.x + expand, pos.y + size.y + expand),
|
|
IM_COL32(40, 46, 60, alpha),
|
|
10.0f + expand
|
|
);
|
|
}*/
|
|
|
|
draw->AddRectFilled(ImGui::GetWindowPos(), ImGui::GetWindowPos() + ImGui::GetWindowSize(), IM_COL32(40, 46, 60, 255), 10.f);
|
|
// titlebar
|
|
{
|
|
const float title_h = 30.0f;
|
|
const float title_r = 8.f;
|
|
const ImU32 col_l = IM_COL32(129, 183, 255, 255);
|
|
const ImU32 col_r = IM_COL32(137, 120, 255, 255);
|
|
|
|
// i want to die
|
|
draw->AddRectFilledMultiColor(
|
|
ImVec2(pos.x, pos.y + title_r),
|
|
ImVec2(pos.x + size.x, pos.y + title_h),
|
|
col_l, col_r, col_r, col_l
|
|
);
|
|
|
|
draw->AddRectFilledMultiColor(
|
|
ImVec2(pos.x + title_r, pos.y),
|
|
ImVec2(pos.x + size.x - title_r, pos.y + title_r),
|
|
col_l, col_r, col_r, col_l
|
|
);
|
|
|
|
draw->AddCircleFilled(ImVec2(pos.x + title_r, pos.y + title_r), title_r, col_l, 24);
|
|
draw->AddCircleFilled(ImVec2(pos.x + size.x - title_r, pos.y + title_r), title_r, col_r, 24);
|
|
|
|
const float t = static_cast<float>(ImGui::GetTime());
|
|
|
|
const int segments = 80;
|
|
const float amp = 3.5f;
|
|
const float freq = 6.0f;
|
|
const float phase_step = 0.8f;
|
|
ImU32 wave_color = IM_COL32(180, 200, 255, 140);
|
|
for (int l = 0; l < 3; ++l)
|
|
{
|
|
float y_base = pos.y + title_h * 0.55f + l * 3.5f;
|
|
draw->PathClear();
|
|
for (int i = 0; i <= segments; ++i)
|
|
{
|
|
float u = static_cast<float>(i) / static_cast<float>(segments);
|
|
float x = pos.x + (size.x) * u;
|
|
float y = y_base + std::sinf(u * freq + t * 2.2f + l * phase_step) * amp;
|
|
draw->PathLineTo(ImVec2(x, y));
|
|
}
|
|
draw->PathStroke(wave_color, false, 1.6f);
|
|
}
|
|
|
|
static std::vector<ImVec2> star_offsets;
|
|
static std::vector<float> star_radii;
|
|
static std::vector<float> star_rotations;
|
|
static std::vector<float> star_opacities;
|
|
constexpr float kStarRadius = 7.5f;
|
|
constexpr float kStarMinDistance = 12.0f;
|
|
if (star_offsets.empty())
|
|
{
|
|
star_offsets.reserve(16);
|
|
star_radii.reserve(16);
|
|
star_rotations.reserve(16);
|
|
star_opacities.reserve(16);
|
|
unsigned seed = 1234567u;
|
|
const char* title_text = "Mocha";
|
|
float title_reserved = 12.0f + ImGui::CalcTextSize(title_text).x + 16.0f;
|
|
float x_min = ImClamp(title_reserved, 10.0f, size.x - 10.0f);
|
|
float x_max = size.x - 10.0f;
|
|
auto nextf = [&seed]() {
|
|
seed = seed * 1664525u + 1013904223u;
|
|
return (seed >> 8) * (1.0f / 16777216.0f);
|
|
};
|
|
|
|
const int star_count = 16;
|
|
float step = (x_max - x_min) / static_cast<float>(star_count);
|
|
for (int i = 0; i < star_count; ++i)
|
|
{
|
|
float jitter = (nextf() - 0.5f) * step * 0.35f;
|
|
float x = ImClamp(x_min + (i + 0.5f) * step + jitter, x_min, x_max);
|
|
float y = 4.0f + nextf() * (title_h - 8.0f);
|
|
ImVec2 candidate(x, y);
|
|
|
|
bool too_close = false;
|
|
for (const ImVec2& p : star_offsets)
|
|
{
|
|
if (ImLengthSqr(p - candidate) < kStarMinDistance * kStarMinDistance)
|
|
{
|
|
too_close = true;
|
|
break;
|
|
}
|
|
}
|
|
if (too_close)
|
|
continue;
|
|
|
|
star_offsets.push_back(candidate);
|
|
star_radii.push_back(kStarRadius * (0.7f + nextf() * 0.6f));
|
|
star_rotations.push_back(nextf() * (2.0f * IM_PI));
|
|
star_opacities.push_back(0.55f + nextf() * 0.45f);
|
|
}
|
|
}
|
|
draw->PushClipRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + size.x, pos.y + title_h), true);
|
|
for (size_t i = 0; i < star_offsets.size(); ++i)
|
|
{
|
|
float offset = std::sinf(t * 1.5f + static_cast<float>(i) * 0.6f) * 2.2f;
|
|
ImVec2 center = pos + star_offsets[i] + ImVec2(0.0f, offset);
|
|
float fade = star_opacities[i] * (0.85f + 0.15f * (0.5f + 0.5f * std::sinf(t * 1.5f + static_cast<float>(i) * 0.6f)));
|
|
ImU32 star_color = ImGui::ColorConvertFloat4ToU32(ImVec4(1.0f, 1.0f, 1.0f, fade));
|
|
draw_star(center, star_radii[i], star_radii[i] * 0.55f, 5, star_color, star_rotations[i]);
|
|
}
|
|
draw->PopClipRect();
|
|
|
|
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + 2.f, ImGui::GetCursorPosY() - 2.f));
|
|
ImGui::Text("Mocha");
|
|
}
|
|
|
|
// content
|
|
{
|
|
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + 5.f, ImGui::GetCursorPosY() + 15.f));
|
|
ImGui::BeginChild("##mocha_test_child", ImVec2(300, 330));
|
|
{
|
|
pos = ImGui::GetWindowPos();
|
|
size = ImGui::GetWindowSize();
|
|
draw = ImGui::GetWindowDrawList();
|
|
|
|
draw->AddRectFilled(pos, pos + size, IM_COL32(45, 50, 65, 255), 10.f);
|
|
|
|
// titlebar
|
|
{
|
|
const float title_h = 30.0f;
|
|
const float title_r = 10.0f;
|
|
const ImU32 col_top = IM_COL32(45, 50, 65, 255);
|
|
const ImU32 col_bottom = IM_COL32(40, 45, 60, 255);
|
|
|
|
draw->AddRectFilled(
|
|
ImVec2(pos.x, pos.y),
|
|
ImVec2(pos.x + size.x, pos.y + title_h),
|
|
col_top,
|
|
title_r
|
|
);
|
|
|
|
// Reverse gradient under the titlebar to mimic a drop shadow
|
|
draw->AddRectFilledMultiColor(
|
|
ImVec2(pos.x, pos.y + title_h),
|
|
ImVec2(pos.x + size.x, pos.y + title_h + title_r),
|
|
col_bottom, col_bottom, col_top, col_top
|
|
);
|
|
|
|
draw->AddCircleFilled(ImVec2(pos.x + title_r, pos.y + title_r), title_r, col_top, 24);
|
|
draw->AddCircleFilled(ImVec2(pos.x + size.x - title_r, pos.y + title_r), title_r, col_top, 24);
|
|
const char* text = "Test";
|
|
ImVec2 text_size = ImGui::CalcTextSize(text);
|
|
ImGui::SetCursorPos(ImVec2(((size.x - text_size.x) * 0.5f), 5.f));
|
|
ImGui::Text(text);
|
|
}
|
|
|
|
// content
|
|
{
|
|
ImGui::SetCursorPos(ImVec2(10, 40));
|
|
ImGui::Checkbox("Test Toggle", &test_bool);
|
|
|
|
ImGui::SetCursorPos(ImVec2(10, ImGui::GetCursorPosY() + 25.f));; ImGui::SliderFloat("Test Slider (float)", &test_float, 0.f, 100.f);
|
|
ImGui::SetCursorPosX(10); ImGui::SliderInt("Test Slider (int)", &test_int, 0, 100);
|
|
}
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
}
|
|
ImGui::End();
|
|
ImGui::PopStyleVar(4);
|
|
ImGui::PopFontShadow();
|
|
|
|
// Rendering
|
|
ImGui::Render();
|
|
const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
|
|
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);
|
|
g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);
|
|
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
|
|
|
|
// Present
|
|
HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync
|
|
//HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
|
|
g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
|
|
}
|
|
|
|
// Cleanup
|
|
ImGui_ImplDX11_Shutdown();
|
|
ImGui_ImplWin32_Shutdown();
|
|
ImGui::DestroyContext();
|
|
|
|
CleanupDeviceD3D();
|
|
::DestroyWindow(hwnd);
|
|
::UnregisterClassW(wc.lpszClassName, wc.hInstance);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
bool CreateDeviceD3D(HWND hWnd)
|
|
{
|
|
// Setup swap chain
|
|
DXGI_SWAP_CHAIN_DESC sd;
|
|
ZeroMemory(&sd, sizeof(sd));
|
|
sd.BufferCount = 2;
|
|
sd.BufferDesc.Width = 0;
|
|
sd.BufferDesc.Height = 0;
|
|
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
sd.BufferDesc.RefreshRate.Numerator = 60;
|
|
sd.BufferDesc.RefreshRate.Denominator = 1;
|
|
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
|
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
|
sd.OutputWindow = hWnd;
|
|
sd.SampleDesc.Count = 1;
|
|
sd.SampleDesc.Quality = 0;
|
|
sd.Windowed = TRUE;
|
|
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
|
|
|
UINT createDeviceFlags = 0;
|
|
//createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
D3D_FEATURE_LEVEL featureLevel;
|
|
const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
|
|
HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
|
|
if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available.
|
|
res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
|
|
if (res != S_OK)
|
|
return false;
|
|
|
|
CreateRenderTarget();
|
|
return true;
|
|
}
|
|
|
|
void CleanupDeviceD3D()
|
|
{
|
|
CleanupRenderTarget();
|
|
if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; }
|
|
if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; }
|
|
if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
|
|
}
|
|
|
|
void CreateRenderTarget()
|
|
{
|
|
ID3D11Texture2D* pBackBuffer;
|
|
g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
|
|
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
|
|
pBackBuffer->Release();
|
|
}
|
|
|
|
void CleanupRenderTarget()
|
|
{
|
|
if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; }
|
|
}
|
|
|
|
// Forward declare message handler from imgui_impl_win32.cpp
|
|
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
// Win32 message handler
|
|
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
|
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
|
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
|
|
return true;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_SIZE:
|
|
if (wParam == SIZE_MINIMIZED)
|
|
return 0;
|
|
g_ResizeWidth = (UINT)LOWORD(lParam); // Queue resize
|
|
g_ResizeHeight = (UINT)HIWORD(lParam);
|
|
return 0;
|
|
case WM_SYSCOMMAND:
|
|
if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
|
|
return 0;
|
|
break;
|
|
case WM_DESTROY:
|
|
::PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
return ::DefWindowProcW(hWnd, msg, wParam, lParam);
|
|
}
|