Examples: DX11 + Win32: Initial attempt at implementing the viewport/platform api. (WIP/test API) (#1542)

docking
omar 7 years ago
parent 735267d27c
commit 25349b31d7

@ -117,10 +117,12 @@ int main(int, char**)
// Setup ImGui binding
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
ImGuiIO& io = ImGui::GetIO();
io.NavFlags |= ImGuiNavFlags_EnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_MultiViewports;
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
//io.NavFlags |= ImGuiNavFlags_EnableKeyboard; // Enable Keyboard Controls
// Setup style
ImGui::StyleColorsDark();
@ -206,6 +208,9 @@ int main(int, char**)
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindows();
g_pSwapChain->Present(1, 0); // Present with vsync
//g_pSwapChain->Present(0, 0); // Present without vsync
}

@ -11,6 +11,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiRendererInterface
// 2018-XX-XX: DirectX11: Offset projection matrix and clipping rectangle by io.DisplayPos (which will be non-zero for multi-viewport applications).
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
@ -26,6 +27,7 @@
// DirectX data
static ID3D11Device* g_pd3dDevice = NULL;
static ID3D11DeviceContext* g_pd3dDeviceContext = NULL;
static IDXGIFactory1* g_pFactory = NULL;
static ID3D11Buffer* g_pVB = NULL;
static ID3D11Buffer* g_pIB = NULL;
static ID3D10Blob * g_pVertexShaderBlob = NULL;
@ -46,6 +48,10 @@ struct VERTEX_CONSTANT_BUFFER
float mvp[4][4];
};
// Forward Declarations
static void ImGui_ImplDX11_InitPlatformInterface();
static void ImGui_ImplDX11_ShutdownPlatformInterface();
// Render function
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
@ -466,13 +472,29 @@ void ImGui_ImplDX11_InvalidateDeviceObjects()
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
{
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory1* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) != S_OK)
return false;
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) != S_OK)
return false;
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) != S_OK)
return false;
ImGuiIO& io = ImGui::GetIO();
g_pd3dDevice = device;
g_pd3dDeviceContext = device_context;
g_pFactory = pFactory;
if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports)
ImGui_ImplDX11_InitPlatformInterface();
return true;
}
void ImGui_ImplDX11_Shutdown()
{
ImGui_ImplDX11_ShutdownPlatformInterface();
ImGui_ImplDX11_InvalidateDeviceObjects();
g_pd3dDevice = NULL;
g_pd3dDeviceContext = NULL;
@ -483,3 +505,121 @@ void ImGui_ImplDX11_NewFrame()
if (!g_pFontSampler)
ImGui_ImplDX11_CreateDeviceObjects();
}
// --------------------------------------------------------------------------------------------------------
// Platform Windows
// --------------------------------------------------------------------------------------------------------
#include "imgui_internal.h" // ImGuiViewport
struct ImGuiPlatformDataDx11
{
IDXGISwapChain* SwapChain;
ID3D11RenderTargetView* RTView;
ImGuiPlatformDataDx11() { SwapChain = NULL; RTView = NULL; }
~ImGuiPlatformDataDx11() { IM_ASSERT(SwapChain == NULL && RTView == NULL); }
};
static void ImGui_ImplDX11_CreateViewport(ImGuiViewport* viewport)
{
ImGuiPlatformDataDx11* data = IM_NEW(ImGuiPlatformDataDx11)();
viewport->RendererUserData = data;
// FIXME-PLATFORM
HWND hwnd = (HWND)viewport->PlatformHandle;
IM_ASSERT(hwnd != 0);
// Create swap chain
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferDesc.Width = (UINT)viewport->Size.x;
sd.BufferDesc.Height = (UINT)viewport->Size.y;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = hwnd;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags = 0;
IM_ASSERT(data->SwapChain == NULL && data->RTView == NULL);
g_pFactory->CreateSwapChain(g_pd3dDevice, &sd, &data->SwapChain);
// Create the render target
if (data->SwapChain)
{
ID3D11Texture2D* pBackBuffer;
data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView);
pBackBuffer->Release();
}
}
static void ImGui_ImplDX11_DestroyViewport(ImGuiViewport* viewport)
{
if (ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData)
{
if (data->SwapChain)
data->SwapChain->Release();
data->SwapChain = NULL;
if (data->RTView)
data->RTView->Release();
data->RTView = NULL;
IM_DELETE(data);
}
viewport->RendererUserData = NULL;
}
static void ImGui_ImplDX11_ResizeViewport(ImGuiViewport* viewport, int w, int h)
{
ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData;
if (data->RTView)
{
data->RTView->Release();
data->RTView = NULL;
}
if (data->SwapChain)
{
ID3D11Texture2D* pBackBuffer = NULL;
data->SwapChain->ResizeBuffers(0, w, h, DXGI_FORMAT_UNKNOWN, 0);
data->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &data->RTView);
pBackBuffer->Release();
}
}
static void ImGui_ImplDX11_RenderViewport(ImGuiViewport* viewport)
{
ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData;
ImVec4 clear_color = ImGui::GetStyle().Colors[ImGuiCol_WindowBg]; // FIXME-PLATFORM
clear_color.w = 1.0f;
g_pd3dDeviceContext->OMSetRenderTargets(1, &data->RTView, NULL);
g_pd3dDeviceContext->ClearRenderTargetView(data->RTView, (float*)&clear_color);
ImGui_ImplDX11_RenderDrawData(&viewport->DrawData);
}
static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport)
{
ImGuiPlatformDataDx11* data = (ImGuiPlatformDataDx11*)viewport->RendererUserData;
data->SwapChain->Present(0, 0); // Present without vsync
}
void ImGui_ImplDX11_InitPlatformInterface()
{
ImGuiIO& io = ImGui::GetIO();
io.RendererInterface.CreateViewport = ImGui_ImplDX11_CreateViewport;
io.RendererInterface.DestroyViewport = ImGui_ImplDX11_DestroyViewport;
io.RendererInterface.ResizeViewport = ImGui_ImplDX11_ResizeViewport;
io.RendererInterface.RenderViewport = ImGui_ImplDX11_RenderViewport;
io.RendererInterface.SwapBuffers = ImGui_ImplDX11_SwapBuffers;
}
void ImGui_ImplDX11_ShutdownPlatformInterface()
{
ImGuiIO& io = ImGui::GetIO();
memset(&io.RendererInterface, 0, sizeof(io.RendererInterface));
}

@ -5,8 +5,12 @@
#include "imgui_impl_win32.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
#include "imgui_internal.h" // FIXME-PLATFORM
// CHANGELOG
// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformInterface
// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling).
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-06: Inputs: Honoring the io.WantMoveMouse by repositioning the mouse by using navigation and ImGuiNavFlags_MoveMouse is set.
@ -24,6 +28,10 @@ static INT64 g_Time = 0;
static INT64 g_TicksPerSecond = 0;
static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_Count_;
// Forward Declarations
static void ImGui_ImplWin32_InitPlatformInterface();
static void ImGui_ImplWin32_ShutdownPlatformInterface();
// Functions
bool ImGui_ImplWin32_Init(void* hwnd)
{
@ -57,11 +65,21 @@ bool ImGui_ImplWin32_Init(void* hwnd)
io.KeyMap[ImGuiKey_Y] = 'Y';
io.KeyMap[ImGuiKey_Z] = 'Z';
io.ImeWindowHandle = g_hWnd; return true;
io.ImeWindowHandle = g_hWnd;
// Our mouse update function expect PlatformHandle to be filled for the main viewport
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->PlatformHandle = (void*)g_hWnd;
if (io.ConfigFlags & ImGuiConfigFlags_MultiViewports)
ImGui_ImplWin32_InitPlatformInterface();
return true;
}
void ImGui_ImplWin32_Shutdown()
{
ImGui_ImplWin32_ShutdownPlatformInterface();
g_hWnd = (HWND)0;
}
@ -92,6 +110,42 @@ static void ImGui_ImplWin32_UpdateMouseCursor()
}
}
// This code supports multiple OS Windows mapped into different ImGui viewports,
// So it is a little more complicated than your typical binding code (which only needs to set io.MousePos in your WM_MOUSEMOVE handler)
// This is what imgui needs from the back-end to support multiple windows:
// - io.MousePos = mouse position (e.g. io.MousePos == viewport->Pos when we are on the upper-left of our viewport)
// - io.MousePosViewport = viewport which mouse position is based from (generally the focused/active/capturing viewport)
// - io.MouseHoveredWindow = viewport which mouse is hovering, **regardless of it being the active/focused window**, **regardless of another window holding mouse captured**. [Optional]
// This function overwrite the value of io.MousePos normally updated by the WM_MOUSEMOVE handler.
// We keep the WM_MOUSEMOVE handling code so that WndProc function can be copied as-in in applications which do not need multiple OS windows support.
static void ImGui_ImplWin32_UpdateMousePos()
{
ImGuiIO& io = ImGui::GetIO();
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
io.MousePosViewport = 0;
io.MouseHoveredViewport = 0;
POINT pos;
if (!::GetCursorPos(&pos))
return;
// Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui
io.ConfigFlags |= ImGuiConfigFlags_PlatformHasMouseHoveredViewport;
HWND hovered_hwnd = ::WindowFromPoint(pos);
if (hovered_hwnd)
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
io.MouseHoveredViewport = viewport->ID;
// Convert mouse from screen position to window client position
HWND focused_hwnd = ::GetActiveWindow();
if (focused_hwnd != 0 && ::ScreenToClient(focused_hwnd, &pos))
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_hwnd))
{
io.MousePos = ImVec2(viewport->Pos.x + (float)pos.x, viewport->Pos.y + (float)pos.y);
io.MousePosViewport = viewport->ID;
}
}
void ImGui_ImplWin32_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
@ -133,6 +187,8 @@ void ImGui_ImplWin32_NewFrame()
ImGui_ImplWin32_UpdateMouseCursor();
}
ImGui_ImplWin32_UpdateMousePos();
// Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application.
ImGui::NewFrame();
}
@ -213,3 +269,199 @@ IMGUI_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wPa
}
return 0;
}
// --------------------------------------------------------------------------------------------------------
// Platform Windows
// --------------------------------------------------------------------------------------------------------
struct ImGuiPlatformDataWin32
{
HWND Hwnd;
bool ExternalResize;
DWORD DwStyle;
DWORD DwExStyle;
ImGuiPlatformDataWin32() { Hwnd = NULL; ExternalResize = false; DwStyle = DwExStyle = 0; }
~ImGuiPlatformDataWin32() { IM_ASSERT(Hwnd == NULL); }
};
static void ImGui_ImplWin32_CreateViewport(ImGuiViewport* viewport)
{
ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)();
viewport->PlatformUserData = data;
if (viewport->Flags & ImGuiViewportFlags_NoDecoration)
{
data->DwStyle = WS_POPUP;
data->DwExStyle = 0;
}
else
{
data->DwStyle = WS_OVERLAPPEDWINDOW;
data->DwExStyle = WS_EX_TOOLWINDOW;
}
// Create window
RECT rect = { (LONG)viewport->PlatformOsDesktopPos.x, (LONG)viewport->PlatformOsDesktopPos.y, (LONG)(viewport->PlatformOsDesktopPos.x + viewport->Size.x), (LONG)(viewport->PlatformOsDesktopPos.y + viewport->Size.y) };
::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
data->ExternalResize = true;
data->Hwnd = ::CreateWindowExA(
data->DwExStyle, "ImGui Platform", "No Title Yet", data->DwStyle, // Style, class name, window name
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area
g_hWnd, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param
data->ExternalResize = false;
viewport->PlatformHandle = data->Hwnd;
}
static void ImGui_ImplWin32_DestroyViewport(ImGuiViewport* viewport)
{
if (ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData)
{
if (::GetCapture() == data->Hwnd)
{
// Transfer capture so if we started dragging from a window that later disappears, we'll still release the MOUSEUP event.
::ReleaseCapture();
::SetCapture(g_hWnd);
}
if (data->Hwnd)
::DestroyWindow(data->Hwnd);
data->Hwnd = NULL;
IM_DELETE(data);
}
viewport->PlatformUserData = viewport->PlatformHandle = NULL;
}
static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport)
{
ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
IM_ASSERT(data->Hwnd != 0);
data->ExternalResize = true;
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
::ShowWindow(data->Hwnd, SW_SHOWNA);
else
::ShowWindow(data->Hwnd, SW_SHOW);
data->ExternalResize = false;
}
static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport)
{
ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
IM_ASSERT(data->Hwnd != 0);
POINT pos = { 0, 0 };
::ClientToScreen(data->Hwnd, &pos);
return ImVec2((float)pos.x, (float)pos.y);
}
static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
{
ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
IM_ASSERT(data->Hwnd != 0);
RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y };
::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle);
::SetWindowPos(data->Hwnd, NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport)
{
ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
IM_ASSERT(data->Hwnd != 0);
RECT rect;
::GetClientRect(data->Hwnd, &rect);
return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top));
}
static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
IM_ASSERT(data->Hwnd != 0);
data->ExternalResize = true;
RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y };
::AdjustWindowRectEx(&rect, data->DwStyle, FALSE, data->DwExStyle); // Client to Screen
::SetWindowPos(data->Hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
data->ExternalResize = false;
}
static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title)
{
ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
IM_ASSERT(data->Hwnd != 0);
::SetWindowTextA(data->Hwnd, title);
}
static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true;
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
{
ImGuiIO& io = ImGui::GetIO();
ImGuiPlatformDataWin32* data = (ImGuiPlatformDataWin32*)viewport->PlatformUserData;
switch (msg)
{
case WM_CLOSE:
viewport->PlatformRequestClose = true;
return 0;
case WM_MOVE:
viewport->PlatformOsDesktopPos = ImVec2((float)(short)LOWORD(lParam), (float)(short)HIWORD(lParam));
break;
case WM_NCHITTEST:
// Let mouse pass-through the window, this is used while e.g. dragging a window, we creates a temporary overlay but want the cursor to aim behind our overlay.
if (viewport->Flags & ImGuiViewportFlags_NoInputs)
return HTTRANSPARENT;
break;
case WM_SIZE:
if (!data->ExternalResize)
viewport->PlatformRequestResize = true;
if (io.RendererInterface.ResizeViewport)
io.RendererInterface.ResizeViewport(viewport, (int)LOWORD(lParam), (int)HIWORD(lParam));
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
static void ImGui_ImplWin32_InitPlatformInterface()
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = ::GetModuleHandle(NULL);
wcex.hIcon = NULL;
wcex.hCursor = NULL;
wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = _T("ImGui Platform");
wcex.hIconSm = NULL;
::RegisterClassEx(&wcex);
// Register platform interface (will be coupled with a renderer interface)
ImGuiIO& io = ImGui::GetIO();
io.PlatformInterface.CreateViewport = ImGui_ImplWin32_CreateViewport;
io.PlatformInterface.DestroyViewport = ImGui_ImplWin32_DestroyViewport;
io.PlatformInterface.ShowWindow = ImGui_ImplWin32_ShowWindow;
io.PlatformInterface.SetWindowPos = ImGui_ImplWin32_SetWindowPos;
io.PlatformInterface.GetWindowPos = ImGui_ImplWin32_GetWindowPos;
io.PlatformInterface.SetWindowSize = ImGui_ImplWin32_SetWindowSize;
io.PlatformInterface.GetWindowSize = ImGui_ImplWin32_GetWindowSize;
io.PlatformInterface.SetWindowTitle = ImGui_ImplWin32_SetWindowTitle;
// Register main window handle
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
ImGuiPlatformDataWin32* data = IM_NEW(ImGuiPlatformDataWin32)();
data->Hwnd = g_hWnd;
main_viewport->PlatformUserData = data;
main_viewport->PlatformHandle = (void*)data->Hwnd;
}
static void ImGui_ImplWin32_ShutdownPlatformInterface()
{
ImGuiIO& io = ImGui::GetIO();
memset(&io.PlatformInterface, 0, sizeof(io.PlatformInterface));
::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(NULL));
}

Loading…
Cancel
Save