From beb1b3c55309b5650cbe569737c3a3c173329208 Mon Sep 17 00:00:00 2001 From: morgoth90 Date: Mon, 13 Jul 2020 21:37:39 +0200 Subject: [PATCH] Multiple main windows support --- .travis.yml | 34 +++ imconfig.h | 5 + imgui.cpp | 66 +++-- imgui.h | 2 + imgui_impl_morgoth.cpp | 591 +++++++++++++++++++++++++++++++++++++++++ imgui_impl_morgoth.h | 25 ++ imgui_impl_opengl3.cpp | 584 ++++++++++++++++++++++++++++++++++++++++ imgui_impl_opengl3.h | 40 +++ imgui_internal.h | 1 + imgui_widgets.cpp | 2 +- 10 files changed, 1331 insertions(+), 19 deletions(-) create mode 100644 .travis.yml create mode 100644 imgui_impl_morgoth.cpp create mode 100644 imgui_impl_morgoth.h create mode 100644 imgui_impl_opengl3.cpp create mode 100644 imgui_impl_opengl3.h diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000000..7a554baf6709 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +language: cpp +sudo: required +dist: trusty + +os: + - linux + - osx + +compiler: + - gcc + - clang + +before_install: + - if [ $TRAVIS_OS_NAME == linux ]; then + sudo apt-get update -qq; + sudo apt-get install -y --no-install-recommends libxrandr-dev libxi-dev libxxf86vm-dev libsdl2-dev; + wget https://github.com/glfw/glfw/releases/download/3.2.1/glfw-3.2.1.zip; + unzip glfw-3.2.1.zip && cd glfw-3.2.1; + cmake -DBUILD_SHARED_LIBS=true -DGLFW_BUILD_EXAMPLES=false -DGLFW_BUILD_TESTS=false -DGLFW_BUILD_DOCS=false .; + sudo make -j $CPU_NUM install && cd ..; + fi + - if [ $TRAVIS_OS_NAME == osx ]; then + brew update; + brew install glfw3; + brew install sdl2; + fi + +script: + - make -C examples/example_glfw_opengl2 + - make -C examples/example_glfw_opengl3 + - make -C examples/example_sdl_opengl3 + - if [ $TRAVIS_OS_NAME == osx ]; then + xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_macos; + fi diff --git a/imconfig.h b/imconfig.h index c6817de7cc47..d3a4fa8ed733 100644 --- a/imconfig.h +++ b/imconfig.h @@ -106,3 +106,8 @@ namespace ImGui void MyFunction(const char* name, const MyMatrix44& v); } */ + +#define ImTextureID GLuint + +#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM "../ogl.h" +#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM diff --git a/imgui.cpp b/imgui.cpp index 36d2714596da..52a4b476a787 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -872,7 +872,7 @@ static void EndFrameDrawDimmedBackgrounds(); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. -static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); +ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); static void UpdateViewportsNewFrame(); static void UpdateViewportsEndFrame(); static void UpdateSelectWindowViewport(ImGuiWindow* window); @@ -5134,8 +5134,8 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->ViewportPos = main_viewport->Pos; + ImGuiViewport* default_viewport = ImGui::GetMainViewport(); + window->ViewportPos = default_viewport->Pos; if (settings->ViewportId) { window->ViewportId = settings->ViewportId; @@ -5160,9 +5160,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) g.WindowsById.SetVoidPtr(window->ID, window); // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->Pos = main_viewport->Pos + ImVec2(60, 60); - window->ViewportPos = main_viewport->Pos; + ImGuiViewport* default_viewport = ImGui::GetDefaultViewport(); + window->Pos = default_viewport->Pos + ImVec2(60, 60); + window->ViewportPos = default_viewport->Pos; // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. if (!(flags & ImGuiWindowFlags_NoSavedSettings)) @@ -5199,6 +5199,9 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window) { + if (window == NULL) + return window; + return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window; } @@ -6128,7 +6131,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->Pos = FindBestWindowPosForPopup(window); // Late create viewport if we don't fit within our current host viewport. - if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized)) + if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized) && !(window->Flags & ImGuiWindowFlags_NoDocking)) if (!window->Viewport->GetMainRect().Contains(window->Rect())) { // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) @@ -10792,6 +10795,18 @@ ImGuiViewport* ImGui::GetMainViewport() return g.Viewports[0]; } +ImGuiViewport* ImGui::GetDefaultViewport() +{ + ImGuiContext& g = *GImGui; + return g.default_viewport; +} + +void ImGui::SetDefaultViewport(ImGuiViewport* viewport) +{ + ImGuiContext& g = *GImGui; + g.default_viewport = viewport; +} + ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) { ImGuiContext& g = *GImGui; @@ -10888,7 +10903,15 @@ static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImG static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); + + for (ImGuiViewportP *viewport : g.Viewports) + { + if(viewport->PlatformUserData != NULL) + if (UpdateTryMergeWindowIntoHostViewport(window, viewport)) + return true; + } + + return false; } // Translate imgui windows when a Host Viewport has been moved @@ -10959,7 +10982,7 @@ static void ImGui::UpdateViewportsNewFrame() { ImGuiViewportP* viewport = g.Viewports[n]; const bool platform_funcs_available = viewport->PlatformWindowCreated; - if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) + if (g.PlatformIO.Platform_GetWindowMinimized)// && platform_funcs_available) { bool minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); if (minimized) @@ -11069,6 +11092,8 @@ static void ImGui::UpdateViewportsNewFrame() // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor); } viewport->DpiScale = new_dpi_scale; + + viewport->ClearRequestFlags(); } if (!viewports_enabled) @@ -11138,8 +11163,8 @@ static void ImGui::UpdateViewportsEndFrame() continue; if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) continue; - if (i > 0) - IM_ASSERT(viewport->Window != NULL); + //if (i > 0) + // IM_ASSERT(viewport->Window != NULL); g.PlatformIO.Viewports.push_back(viewport); } g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called @@ -11168,6 +11193,7 @@ ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const viewport->Pos = pos; if (!viewport->PlatformRequestResize) viewport->Size = size; + viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags } else @@ -11214,10 +11240,10 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) window->ViewportAllowPlatformMonitorExtend = -1; // Restore main viewport if multi-viewport is not supported by the back-end - ImGuiViewportP* main_viewport = g.Viewports[0]; + ImGuiViewportP* default_viewport = (ImGuiViewportP*)g.default_viewport; if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { - SetWindowViewport(window, main_viewport); + SetWindowViewport(window, default_viewport); return; } window->ViewportOwned = false; @@ -11281,7 +11307,7 @@ static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) // Fallback to default viewport if (window->Viewport == NULL) - window->Viewport = main_viewport; + window->Viewport = default_viewport; // Mark window as allowed to protrude outside of its viewport and into the current monitor if (!lock_viewport) @@ -11350,6 +11376,11 @@ void ImGui::UpdatePlatformWindows() { ImGuiViewportP* viewport = g.Viewports[i]; + bool is_new_platform_window = (viewport->PlatformWindowCreated == false); + + if (is_new_platform_window && viewport->PlatformHandle != NULL) //Morgoth engine window + continue; + // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame) bool destroy_platform_window = false; @@ -11366,7 +11397,6 @@ void ImGui::UpdatePlatformWindows() continue; // Create window - bool is_new_platform_window = (viewport->PlatformWindowCreated == false); if (is_new_platform_window) { IMGUI_DEBUG_LOG_VIEWPORT("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); @@ -11441,7 +11471,7 @@ void ImGui::UpdatePlatformWindows() for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++) { ImGuiViewportP* viewport = g.Viewports[n]; - if (viewport->PlatformWindowCreated) + //if (viewport->PlatformWindowCreated) if (g.PlatformIO.Platform_GetWindowFocus(viewport)) focused_viewport = viewport; } @@ -11558,7 +11588,7 @@ void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) } else { - IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + // IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); } viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL; viewport->ClearRequestFlags(); @@ -14170,7 +14200,7 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags fla ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) { if (viewport == NULL) - viewport = GetMainViewport(); + viewport = GetDefaultViewport(); SetNextWindowPos(viewport->GetWorkPos()); SetNextWindowSize(viewport->GetWorkSize()); diff --git a/imgui.h b/imgui.h index 233d2841f8c1..87e9a74ac65a 100644 --- a/imgui.h +++ b/imgui.h @@ -811,6 +811,8 @@ namespace ImGui // Note: You may use GetWindowViewport() to get the current viewport of the current window. IMGUI_API ImGuiPlatformIO& GetPlatformIO(); // platform/renderer functions, for back-end to setup + viewports list. IMGUI_API ImGuiViewport* GetMainViewport(); // main viewport. same as GetPlatformIO().MainViewport == GetPlatformIO().Viewports[0]. + IMGUI_API ImGuiViewport* GetDefaultViewport(); + IMGUI_API void SetDefaultViewport(ImGuiViewport* viewport); IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from back-end Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). diff --git a/imgui_impl_morgoth.cpp b/imgui_impl_morgoth.cpp new file mode 100644 index 000000000000..843abf9e3370 --- /dev/null +++ b/imgui_impl_morgoth.cpp @@ -0,0 +1,591 @@ +// dear imgui: Platform Binding for SDL2 +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Clipboard support. +// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE). +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Missing features: +// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. +// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. +// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples. +// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter. +// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText). +// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. +// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value. +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. +// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS). +// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes. +// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. +// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS. +// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. +// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). +// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. + +#include "imgui.h" +#include "imgui_impl_morgoth.h" +#include "../Core/Engine.h" + +using namespace Morgoth; + +// SDL +// (the multi-viewports feature requires SDL features supported from SDL 2.0.5+) +#include +#include +#define SDL_HAS_WARP_MOUSE_GLOBAL SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#define SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH SDL_VERSION_ATLEAST(2,0,5) +#if !SDL_HAS_VULKAN +static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; +#endif + +// Data +static Uint64 g_Time = 0; +static bool g_MousePressed[3] = { false, false, false }; +static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; +static char* g_ClipboardTextData = NULL; + +// Forward Declarations +static void ImGui_ImplMorgoth_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context); +static void ImGui_ImplMorgoth_ShutdownPlatformInterface(); + +static const char* ImGui_ImplMorgoth_GetClipboardText(void*) +{ + if (g_ClipboardTextData) + SDL_free(g_ClipboardTextData); + g_ClipboardTextData = SDL_GetClipboardText(); + return g_ClipboardTextData; +} + +static void ImGui_ImplMorgoth_SetClipboardText(void*, const char* text) +{ + SDL_SetClipboardText(text); +} + +// 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. +// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. +// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. +bool ImGui_ImplMorgoth_ProcessEvent(SDL_Event* event) +{ + ImGuiIO& io = ImGui::GetIO(); + switch (event->type) + { + case SDL_MOUSEWHEEL: + { + if (event->wheel.x > 0) io.MouseWheelH += 1; + if (event->wheel.x < 0) io.MouseWheelH -= 1; + if (event->wheel.y > 0) io.MouseWheel += 1; + if (event->wheel.y < 0) io.MouseWheel -= 1; + return true; + } + case SDL_MOUSEBUTTONDOWN: + { + if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; + if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; + if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; + return true; + } + case SDL_TEXTINPUT: + { + io.AddInputCharactersUTF8(event->text.text); + return true; + } + case SDL_KEYDOWN: + case SDL_KEYUP: + { + int key = event->key.keysym.scancode; + IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)); + io.KeysDown[key] = (event->type == SDL_KEYDOWN); + io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0); + io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0); + io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0); + io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0); + return true; + } + // Multi-viewport support + case SDL_WINDOWEVENT: + Uint8 window_event = event->window.event; + if (window_event == SDL_WINDOWEVENT_CLOSE || window_event == SDL_WINDOWEVENT_MOVED || window_event == SDL_WINDOWEVENT_RESIZED) + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)SDL_GetWindowFromID(event->window.windowID))) + { + if (window_event == SDL_WINDOWEVENT_CLOSE) + viewport->PlatformRequestClose = true; + if (window_event == SDL_WINDOWEVENT_MOVED) + viewport->PlatformRequestMove = true; + if (window_event == SDL_WINDOWEVENT_RESIZED) + viewport->PlatformRequestResize = true; + return true; + } + break; + } + return false; +} + +void ImGui_ImplMorgoth_Init() +{ + g_MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + g_MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); + g_MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); + g_MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); + g_MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); + g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW); + g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE); + g_MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); +} + +static bool ImGui_ImplMorgoth_Init(SDL_Window* window, void* sdl_gl_context) +{ + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) +#if SDL_HAS_WARP_MOUSE_GLOBAL + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) +#endif +#if SDL_HAS_CAPTURE_MOUSE + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) +#endif + + // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. + io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; + io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT; + io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT; + io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP; + io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN; + io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP; + io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN; + io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME; + io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END; + io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT; + io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE; + io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE; + io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE; + io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN; + io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE; + io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A; + io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C; + io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V; + io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X; + io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y; + io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z; + + io.SetClipboardTextFn = ImGui_ImplMorgoth_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplMorgoth_GetClipboardText; + io.ClipboardUserData = NULL; + + + + // Our mouse update function expect PlatformHandle to be filled for the main viewport + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->PlatformHandle = (void*)window; + + // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. + // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)) + ImGui_ImplMorgoth_InitPlatformInterface(window, sdl_gl_context); + + return true; +} + +bool ImGui_ImplMorgoth_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) +{ + (void)sdl_gl_context; // Viewport branch will need this. + return ImGui_ImplMorgoth_Init(window, sdl_gl_context); +} + +void ImGui_ImplMorgoth_Shutdown() +{ + ImGui_ImplMorgoth_ShutdownPlatformInterface(); + + // Destroy last known clipboard data + if (g_ClipboardTextData) + SDL_free(g_ClipboardTextData); + g_ClipboardTextData = NULL; + + // Destroy SDL mouse cursors + for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) + SDL_FreeCursor(g_MouseCursors[cursor_n]); + memset(g_MouseCursors, 0, sizeof(g_MouseCursors)); +} + +static void ImGui_ImplMorgoth_UpdateMousePosAndButtons() +{ + ImGuiIO& io = ImGui::GetIO(); + io.MouseHoveredViewport = 0; + + // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + // (When multi-viewports are enabled, all imgui positions are same as OS positions.) +#if SDL_HAS_WARP_MOUSE_GLOBAL + if (!io.WantSetMousePos) + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + else if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) + { + //SDL_WarpMouseInWindow(g_Window, (int)io.MousePos.x, (int)io.MousePos.y); + } + else + SDL_WarpMouseGlobal((int)io.MousePos.x, (int)io.MousePos.y); +#else + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); +#endif + + int mx, my; + Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); + io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; + io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; + g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; + +#if SDL_HAS_CAPTURE_MOUSE && !defined(__EMSCRIPTEN__) + SDL_Window* focused_window = SDL_GetKeyboardFocus(); + if (focused_window) + { + // SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?) + // The creation of new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally. + int wx, wy; + SDL_GetWindowPosition(focused_window, &wx, &wy); + SDL_GetGlobalMouseState(&mx, &my); + mx -= wx; + my -= wy; + } + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_window)) + io.MousePos = ImVec2(viewport->Pos.x + (float)mx, viewport->Pos.y + (float)my); + + // We already retrieve global mouse position, SDL_CaptureMouse() also let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't trigger the OS window resize cursor + // The function is only supported from SDL 2.0.4 (released Jan 2016) + bool any_mouse_button_down = ImGui::IsAnyMouseDown(); + SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); +#else + if (SDL_GetWindowFlags(g_Window) & SDL_WINDOW_INPUT_FOCUS) + io.MousePos = ImVec2((float)mx, (float)my); +#endif +} + +static void ImGui_ImplMorgoth_UpdateMouseCursor() +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) + return; + + ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); + if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None) + { + // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor + SDL_ShowCursor(SDL_FALSE); + } + else + { + // Show OS mouse cursor + SDL_SetCursor(g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); + SDL_ShowCursor(SDL_TRUE); + } +} + +void ImGui_ImplMorgoth_NewFrame(SDL_Window* window) +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + SDL_GetWindowSize(window, &w, &h); + SDL_GL_GetDrawableSize(window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); + + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) + static Uint64 frequency = SDL_GetPerformanceFrequency(); + Uint64 current_time = SDL_GetPerformanceCounter(); + io.DeltaTime = g_Time > 0 ? (float)((double)(current_time - g_Time) / frequency) : (float)(1.0f / 60.0f); + g_Time = current_time; + + ImGui_ImplMorgoth_UpdateMousePosAndButtons(); + ImGui_ImplMorgoth_UpdateMouseCursor(); +} + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +struct ImGuiViewportDataSDL2 +{ + SDL_Window* Window; + Uint32 WindowID; + bool WindowOwned; + SDL_GLContext GLContext; + + ImGuiViewportDataSDL2() { Window = NULL; WindowID = 0; WindowOwned = false; GLContext = NULL; } + ~ImGuiViewportDataSDL2() { IM_ASSERT(Window == NULL && GLContext == NULL); } +}; + +static void ImGui_ImplMorgoth_CreateWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); + viewport->PlatformUserData = data; + + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiViewportDataSDL2* main_viewport_data = (ImGuiViewportDataSDL2*)main_viewport->PlatformUserData; + + // Share GL resources with main context + bool use_opengl = (main_viewport_data->GLContext != NULL); + //SDL_GLContext backup_context = NULL; + if (use_opengl) + { + // backup_context = SDL_GL_GetCurrentContext(); + // SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + // SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); + } + + // We don't enable SDL_WINDOW_RESIZABLE because it enforce windows decorations + Uint32 sdl_flags = 0; + sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : SDL_WINDOW_VULKAN; + sdl_flags |= SDL_WINDOW_HIDDEN; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; +#if SDL_HAS_ALWAYS_ON_TOP + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; +#endif + data->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + data->WindowOwned = true; + if (use_opengl) + { + data->GLContext = SDL_GL_CreateContext(data->Window); + SDL_GL_SetSwapInterval(0); + } + + if(data->WindowOwned) + Morgoth::Engine::RegisterImGuiWindow(*data->Window, *ImGui::GetCurrentContext()); + + + //if (use_opengl && backup_context) + // SDL_GL_MakeCurrent(data->Window, backup_context); + viewport->PlatformHandle = (void*)data->Window; +} + +static void ImGui_ImplMorgoth_DestroyWindow(ImGuiViewport* viewport) +{ + if (ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData) + { + if (data->GLContext && data->WindowOwned) + SDL_GL_DeleteContext(data->GLContext); + if (data->Window && data->WindowOwned) + { + Morgoth::Engine::UnregisterImGuiWindow(*data->Window); + SDL_DestroyWindow(data->Window); + } + + data->GLContext = NULL; + data->Window = NULL; + IM_DELETE(data); + } + viewport->PlatformUserData = viewport->PlatformHandle = NULL; +} + +static void ImGui_ImplMorgoth_ShowWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; +#if defined(_WIN32) + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(data->Window, &info)) + { + HWND hwnd = info.info.win.window; + + // SDL hack: Hide icon from task bar + // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } + + // SDL hack: SDL always activate/focus windows :/ + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + ::SetWindowPos(hwnd, // handle to window + HWND_TOPMOST, // placement-order handle + 0, // horizontal position + 0, // vertical position + 0, // width + 0, // height + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE// window-positioning options + ); + return; + } + } +#endif + + SDL_ShowWindow(data->Window); + SDL_RaiseWindow(data->Window); +} + +static ImVec2 ImGui_ImplMorgoth_GetWindowPos(ImGuiViewport* viewport) +{ + int x = 0, y = 0; + SDL_GetWindowPosition((SDL_Window*)viewport->PlatformHandle, &x, &y); + return ImVec2((float)x, (float)y); +} + +static void ImGui_ImplMorgoth_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + SDL_SetWindowPosition(data->Window, (int)pos.x, (int)pos.y); +} + +static ImVec2 ImGui_ImplMorgoth_GetWindowSize(ImGuiViewport* viewport) +{ + int w = 0, h = 0; + SDL_GetWindowSize((SDL_Window*)viewport->PlatformHandle, &w, &h); + return ImVec2((float)w, (float)h); +} + +static void ImGui_ImplMorgoth_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + if(data!=NULL) + SDL_SetWindowSize(data->Window, (int)size.x, (int)size.y); +} + +static void ImGui_ImplMorgoth_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + SDL_SetWindowTitle(data->Window, title); +} + +static void ImGui_ImplMorgoth_SetWindowFocus(ImGuiViewport* viewport) +{ + SDL_Window *window = (SDL_Window*)viewport->PlatformHandle; + + SDL_SetWindowInputFocus(window); + SDL_RaiseWindow(window); +} + +static bool ImGui_ImplMorgoth_GetWindowFocus(ImGuiViewport* viewport) +{ + return (SDL_GetWindowFlags((SDL_Window*)viewport->PlatformHandle) & SDL_WINDOW_INPUT_FOCUS) != 0; +} + +static bool ImGui_ImplMorgoth_GetWindowMinimized(ImGuiViewport* viewport) +{ + return (SDL_GetWindowFlags((SDL_Window*)viewport->PlatformHandle) & SDL_WINDOW_MINIMIZED) != 0; +} + +static void ImGui_ImplMorgoth_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + + if (data != NULL) + { + if (data->GLContext) + SDL_GL_MakeCurrent(data->Window, data->GLContext); + } + else //ENGINE WINDOW + { + } +} + +static void ImGui_ImplMorgoth_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataSDL2* data = (ImGuiViewportDataSDL2*)viewport->PlatformUserData; + if (data != NULL) + { + if (data->GLContext) + { + SDL_GL_MakeCurrent(data->Window, data->GLContext); + SDL_GL_SwapWindow(data->Window); + } + } + else //ENGINE WINDOW + { + } +} + +// FIXME-PLATFORM: SDL doesn't have an event to notify the application of display/monitor changes +static void ImGui_ImplMorgoth_UpdateMonitors() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Monitors.resize(0); + int display_count = SDL_GetNumVideoDisplays(); + for (int n = 0; n < display_count; n++) + { + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + ImGuiPlatformMonitor monitor; + SDL_Rect r; + SDL_GetDisplayBounds(n, &r); + monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h); +#if SDL_HAS_USABLE_DISPLAY_BOUNDS + SDL_GetDisplayUsableBounds(n, &r); + monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.WorkSize = ImVec2((float)r.w, (float)r.h); +#endif +#if SDL_HAS_PER_MONITOR_DPI + float dpi = 0.0f; + if (SDL_GetDisplayDPI(n, &dpi, NULL, NULL)) + monitor.DpiScale = dpi / 96.0f; +#endif + platform_io.Monitors.push_back(monitor); + } +} + +static void ImGui_ImplMorgoth_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context) +{ + // Register platform interface (will be coupled with a renderer interface) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplMorgoth_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplMorgoth_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplMorgoth_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplMorgoth_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplMorgoth_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplMorgoth_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplMorgoth_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplMorgoth_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplMorgoth_GetWindowFocus; + platform_io.Platform_SetWindowTitle = ImGui_ImplMorgoth_SetWindowTitle; + platform_io.Platform_RenderWindow = ImGui_ImplMorgoth_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplMorgoth_SwapBuffers; + platform_io.Platform_GetWindowMinimized = ImGui_ImplMorgoth_GetWindowMinimized; + + // SDL2 by default doesn't pass mouse clicks to the application when the click focused a window. This is getting in the way of our interactions and we disable that behavior. +#if SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); +#endif + + ImGui_ImplMorgoth_UpdateMonitors(); + + // Register main window handle (which is owned by the main application, not by us) + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiViewportDataSDL2* data = IM_NEW(ImGuiViewportDataSDL2)(); + data->Window = window; + data->WindowID = SDL_GetWindowID(window); + data->WindowOwned = false; + data->GLContext = sdl_gl_context; + main_viewport->PlatformUserData = data; + main_viewport->PlatformHandle = data->Window; +} + +static void ImGui_ImplMorgoth_ShutdownPlatformInterface() +{ +} diff --git a/imgui_impl_morgoth.h b/imgui_impl_morgoth.h new file mode 100644 index 000000000000..4200aa371ea2 --- /dev/null +++ b/imgui_impl_morgoth.h @@ -0,0 +1,25 @@ +// dear imgui: Platform Binding for SDL2 +// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) + +// Implemented features: +// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Clipboard support. +// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE). +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Missing features: +// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +struct SDL_Window; +typedef union SDL_Event SDL_Event; + + +void ImGui_ImplMorgoth_Init(); +IMGUI_IMPL_API bool ImGui_ImplMorgoth_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); +IMGUI_IMPL_API void ImGui_ImplMorgoth_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplMorgoth_NewFrame(SDL_Window* window); +IMGUI_IMPL_API bool ImGui_ImplMorgoth_ProcessEvent(SDL_Event* event); diff --git a/imgui_impl_opengl3.cpp b/imgui_impl_opengl3.cpp new file mode 100644 index 000000000000..30aa4d34e985 --- /dev/null +++ b/imgui_impl_opengl3.cpp @@ -0,0 +1,584 @@ +// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) +// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. +// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". +// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. +// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link. +// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. +// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. +// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". +// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. +// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. +// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. +// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. +// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. +// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. +// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) + +//---------------------------------------- +// OpenGL GLSL GLSL +// version version string +//---------------------------------------- +// 2.0 110 "#version 110" +// 2.1 120 +// 3.0 130 +// 3.1 140 +// 3.2 150 "#version 150" +// 3.3 330 +// 4.0 400 +// 4.1 410 "#version 410 core" +// 4.2 420 +// 4.3 430 +// ES 2.0 100 "#version 100" +// ES 3.0 300 "#version 300 es" +//---------------------------------------- + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#include "imgui_impl_opengl3.h" +#include +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif + +// iOS, Android and Emscripten can use GL ES 3 +// Call ImGui_ImplOpenGL3_Init() with "#version 300 es" +#if (defined(__APPLE__) && TARGET_OS_IOS) || (defined(__ANDROID__)) || (defined(__EMSCRIPTEN__)) +#define USE_GL_ES3 +#endif + +#ifdef USE_GL_ES3 +// OpenGL ES 3 +#include // Use GL ES 3 +#else +// Regular OpenGL +// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. +#if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) +#include +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) +#include +#elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) +#include +#else +#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM +#endif +#endif + +// OpenGL Data +static char g_GlslVersionString[32] = ""; +static GLuint g_FontTexture = 0; +static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; +static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; +static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; +static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; + +// Forward Declarations +static void ImGui_ImplOpenGL3_InitPlatformInterface(); +static void ImGui_ImplOpenGL3_ShutdownPlatformInterface(); + +// Functions +bool ImGui_ImplOpenGL3_Init(const char* glsl_version) +{ + // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. +#ifdef USE_GL_ES3 + if (glsl_version == NULL) + glsl_version = "#version 300 es"; +#else + if (glsl_version == NULL) + glsl_version = "#version 130"; +#endif + IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); + strcpy(g_GlslVersionString, glsl_version); + strcat(g_GlslVersionString, "\n"); + + // Setup back-end capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplOpenGL3_InitPlatformInterface(); + + return true; +} + +void ImGui_ImplOpenGL3_Shutdown() +{ + ImGui_ImplOpenGL3_ShutdownPlatformInterface(); + ImGui_ImplOpenGL3_DestroyDeviceObjects(); +} + +void ImGui_ImplOpenGL3_NewFrame() +{ + if (!g_FontTexture) + ImGui_ImplOpenGL3_CreateDeviceObjects(); + + ImGuiIO &io = ImGui::GetIO(); + if (!io.Fonts->IsBuilt()) + ImGui_ImplOpenGL3_CreateFontsTexture(); +} + +// OpenGL3 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) +// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. +void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + ImGuiIO& io = ImGui::GetIO(); + int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + draw_data->ScaleClipRects(io.DisplayFramebufferScale); + + // Backup GL state + GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); +#ifdef GL_SAMPLER_BINDING + GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); +#endif + GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#ifdef GL_POLYGON_MODE + GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); +#endif + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); + GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); + GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); + GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); + GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); + GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); + GLboolean last_enable_blend = glIsEnabled(GL_BLEND); + GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); +#ifdef GL_POLYGON_MODE + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); +#endif + + // Setup viewport, orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is (0,0) for single viewport apps. + glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + const float ortho_projection[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, -1.0f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, + }; + glUseProgram(g_ShaderHandle); + glUniform1i(g_AttribLocationTex, 0); + glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); +#ifdef GL_SAMPLER_BINDING + glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. +#endif + // Recreate the VAO every time + // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) + GLuint vao_handle = 0; + glGenVertexArrays(1, &vao_handle); + glBindVertexArray(vao_handle); + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glEnableVertexAttribArray(g_AttribLocationPosition); + glEnableVertexAttribArray(g_AttribLocationUV); + glEnableVertexAttribArray(g_AttribLocationColor); + glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); + glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); + glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); + + // Draw + ImVec2 pos = draw_data->DisplayPos; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawIdx* idx_buffer_offset = 0; + + glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + // User callback (registered via ImDrawList::AddCallback) + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); + if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) + { + // Apply scissor/clipping rectangle + glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); + + // Bind texture, Draw + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); + } + } + idx_buffer_offset += pcmd->ElemCount; + } + } + glDeleteVertexArrays(1, &vao_handle); + + // Restore modified GL state + glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); +#ifdef GL_SAMPLER_BINDING + glBindSampler(0, last_sampler); +#endif + glActiveTexture(last_active_texture); + glBindVertexArray(last_vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); +#ifdef GL_POLYGON_MODE + glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); +#endif + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); +} + +bool ImGui_ImplOpenGL3_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +void ImGui_ImplOpenGL3_DestroyFontsTexture() +{ + if (g_FontTexture) + { + ImGuiIO& io = ImGui::GetIO(); + glDeleteTextures(1, &g_FontTexture); + io.Fonts->TexID = 0; + g_FontTexture = 0; + } +} + +// If you get an error please report on github. You may try different GL context version or GLSL version. +static bool CheckShader(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetShaderiv(handle, GL_COMPILE_STATUS, &status); + glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if (status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); + if (log_length > 0) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return status == GL_TRUE; +} + +// If you get an error please report on github. You may try different GL context version or GLSL version. +static bool CheckProgram(GLuint handle, const char* desc) +{ + GLint status = 0, log_length = 0; + glGetProgramiv(handle, GL_LINK_STATUS, &status); + glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); + if (status == GL_FALSE) + fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s!\n", desc); + if (log_length > 0) + { + ImVector buf; + buf.resize((int)(log_length + 1)); + glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + fprintf(stderr, "%s\n", buf.begin()); + } + return status == GL_TRUE; +} + +bool ImGui_ImplOpenGL3_CreateDeviceObjects() +{ + // Backup GL state + GLint last_texture, last_array_buffer, last_vertex_array; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); + + // Parse GLSL version string + int glsl_version = 130; + sscanf(g_GlslVersionString, "#version %d", &glsl_version); + + const GLchar* vertex_shader_glsl_120 = + "uniform mat4 ProjMtx;\n" + "attribute vec2 Position;\n" + "attribute vec2 UV;\n" + "attribute vec4 Color;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_130 = + "uniform mat4 ProjMtx;\n" + "in vec2 Position;\n" + "in vec2 UV;\n" + "in vec4 Color;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_300_es = + "precision mediump float;\n" + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* vertex_shader_glsl_410_core = + "layout (location = 0) in vec2 Position;\n" + "layout (location = 1) in vec2 UV;\n" + "layout (location = 2) in vec4 Color;\n" + "uniform mat4 ProjMtx;\n" + "out vec2 Frag_UV;\n" + "out vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " Frag_UV = UV;\n" + " Frag_Color = Color;\n" + " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_120 = + "#ifdef GL_ES\n" + " precision mediump float;\n" + "#endif\n" + "uniform sampler2D Texture;\n" + "varying vec2 Frag_UV;\n" + "varying vec4 Frag_Color;\n" + "void main()\n" + "{\n" + " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_130 = + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_300_es = + "precision mediump float;\n" + "uniform sampler2D Texture;\n" + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + const GLchar* fragment_shader_glsl_410_core = + "in vec2 Frag_UV;\n" + "in vec4 Frag_Color;\n" + "uniform sampler2D Texture;\n" + "layout (location = 0) out vec4 Out_Color;\n" + "void main()\n" + "{\n" + " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" + "}\n"; + + // Select shaders matching our GLSL versions + const GLchar* vertex_shader = NULL; + const GLchar* fragment_shader = NULL; + if (glsl_version < 130) + { + vertex_shader = vertex_shader_glsl_120; + fragment_shader = fragment_shader_glsl_120; + } + else if (glsl_version == 410) + { + vertex_shader = vertex_shader_glsl_410_core; + fragment_shader = fragment_shader_glsl_410_core; + } + else if (glsl_version == 300) + { + vertex_shader = vertex_shader_glsl_300_es; + fragment_shader = fragment_shader_glsl_300_es; + } + else + { + vertex_shader = vertex_shader_glsl_130; + fragment_shader = fragment_shader_glsl_130; + } + + // Create shaders + const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; + g_VertHandle = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL); + glCompileShader(g_VertHandle); + CheckShader(g_VertHandle, "vertex shader"); + + const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; + g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL); + glCompileShader(g_FragHandle); + CheckShader(g_FragHandle, "fragment shader"); + + g_ShaderHandle = glCreateProgram(); + glAttachShader(g_ShaderHandle, g_VertHandle); + glAttachShader(g_ShaderHandle, g_FragHandle); + glLinkProgram(g_ShaderHandle); + CheckProgram(g_ShaderHandle, "shader program"); + + g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); + g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); + g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); + g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); + g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); + + // Create buffers + glGenBuffers(1, &g_VboHandle); + glGenBuffers(1, &g_ElementsHandle); + + ImGui_ImplOpenGL3_CreateFontsTexture(); + + // Restore modified GL state + glBindTexture(GL_TEXTURE_2D, last_texture); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBindVertexArray(last_vertex_array); + + return true; +} + +void ImGui_ImplOpenGL3_DestroyDeviceObjects() +{ + if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); + if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); + g_VboHandle = g_ElementsHandle = 0; + + if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); + if (g_VertHandle) glDeleteShader(g_VertHandle); + g_VertHandle = 0; + + if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); + if (g_FragHandle) glDeleteShader(g_FragHandle); + g_FragHandle = 0; + + if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); + g_ShaderHandle = 0; + + ImGui_ImplOpenGL3_DestroyFontsTexture(); +} + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) +{ + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + } + ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData); +} + +static void ImGui_ImplOpenGL3_InitPlatformInterface() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow; +} + +static void ImGui_ImplOpenGL3_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} diff --git a/imgui_impl_opengl3.h b/imgui_impl_opengl3.h new file mode 100644 index 000000000000..96ae9e58cb23 --- /dev/null +++ b/imgui_impl_opengl3.h @@ -0,0 +1,40 @@ +// dear imgui: Renderer for OpenGL3 / OpenGL ES2 / OpenGL ES3 (modern OpenGL with shaders / programmatic pipeline) +// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) +// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +// About OpenGL function loaders: +// About OpenGL function loaders: modern OpenGL doesn't have a standard header file and requires individual function pointers to be loaded manually. +// Helper libraries are often used for this purpose! Here we are supporting a few common ones: gl3w, glew, glad. +// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. + +// About GLSL version: +// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. +// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" +// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. + +// Set default OpenGL loader to be gl3w +#if !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ + && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) +#define IMGUI_IMPL_OPENGL_LOADER_GL3W +#endif + +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); diff --git a/imgui_internal.h b/imgui_internal.h index 3934034b32a2..3e1108fda5d7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1347,6 +1347,7 @@ struct ImGuiContext // Viewports ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. + ImGuiViewport* default_viewport = nullptr; float CurrentDpiScale; // == CurrentViewport->DpiScale ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() ImGuiViewportP* MouseViewport; diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index bac3898e415d..5470f84862a7 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6258,7 +6258,7 @@ void ImGui::EndMenuBar() bool ImGui::BeginMainMenuBar() { ImGuiContext& g = *GImGui; - ImGuiViewportP* viewport = g.Viewports[0]; + ImGuiViewportP* viewport = (ImGuiViewportP*)GetDefaultViewport(); ImGuiWindow* menu_bar_window = FindWindowByName("##MainMenuBar"); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.