diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index b3742322..7ed6fd1f 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -564,6 +564,7 @@ set(src_framework framework/DeclPDA.cpp framework/DeclSkin.cpp framework/DeclTable.cpp + framework/Dhewm3SettingsMenu.cpp framework/EditField.cpp framework/EventLoop.cpp framework/File.cpp diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 07fe92c8..7ba9ef1b 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -60,6 +60,9 @@ If you have questions concerning this license or the applicable additional terms #define MAX_PRINT_MSG_SIZE 4096 #define MAX_WARNING_LIST 256 +// DG: implemented in Dhewm3SettingsMenu.cpp (the only Com_*_f() function not implemented in this file) +extern void Com_Dhewm3Settings_f( const idCmdArgs &args ); + typedef enum { ERP_NONE, ERP_FATAL, // exit the entire game with a popup window @@ -2302,6 +2305,8 @@ void idCommonLocal::InitCommands( void ) { cmdSystem->AddCommand( "setMachineSpec", Com_SetMachineSpec_f, CMD_FL_SYSTEM, "detects system capabilities and sets com_machineSpec to appropriate value" ); cmdSystem->AddCommand( "execMachineSpec", Com_ExecMachineSpec_f, CMD_FL_SYSTEM, "execs the appropriate config files and sets cvars based on com_machineSpec" ); + cmdSystem->AddCommand( "dhewm3Settings", Com_Dhewm3Settings_f, CMD_FL_SYSTEM, "Toggles (opens/closes) the (advanced) dhewm3 settings menu" ); + #if !defined( ID_DEDICATED ) // compilers cmdSystem->AddCommand( "dmap", Dmap_f, CMD_FL_TOOL, "compiles a map", idCmdSystem::ArgCompletion_MapName ); diff --git a/neo/framework/Dhewm3SettingsMenu.cpp b/neo/framework/Dhewm3SettingsMenu.cpp new file mode 100644 index 00000000..cf1b7050 --- /dev/null +++ b/neo/framework/Dhewm3SettingsMenu.cpp @@ -0,0 +1,72 @@ + +#include "Common.h" + +#include "sys/sys_imgui.h" + +#ifndef IMGUI_DISABLE + +static bool dhewm3MenuOpen = false; + +// called from D3::ImGuiHooks::NewFrame() (if this window is enabled) +void Com_DrawDhewm3SettingsMenu() +{ + bool showSettingsWindow = true; + ImGui::Begin("dhewm3 Settings", &showSettingsWindow); + + if (ImGui::Button("Show ImGui Demo")) { + D3::ImGuiHooks::OpenWindow( D3::ImGuiHooks::D3_ImGuiWin_Demo ); + } + + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; + if (ImGui::BeginTabBar("SettingsTabBar", tab_bar_flags)) + { + if (ImGui::BeginTabItem("Avocado")) + { + ImGui::Text("This is the Avocado tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Broccoli")) + { + ImGui::Text("This is the Broccoli tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Cucumber")) + { + ImGui::Text("This is the Cucumber tab!\nblah blah blah blah blah"); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + + + + + ImGui::End(); + if(!showSettingsWindow) { + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings ); + dhewm3MenuOpen = false; + } +} + +void Com_Dhewm3Settings_f( const idCmdArgs &args ) +{ + if ( !dhewm3MenuOpen ) { + // TODO: if in SP game, pause + + D3::ImGuiHooks::OpenWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings ); + + dhewm3MenuOpen = true; + + } else { + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_Settings ); + + // TODO: if in SP game, unpause + dhewm3MenuOpen = false; + } +} + +#else // IMGUI_DISABLE - just a stub function + +void Com_Dhewm3Settings_f( const idCmdArgs &args ) {} + +#endif diff --git a/neo/sys/sys_imgui.cpp b/neo/sys/sys_imgui.cpp index ad058d59..ee0cbe99 100644 --- a/neo/sys/sys_imgui.cpp +++ b/neo/sys/sys_imgui.cpp @@ -12,6 +12,7 @@ #include "renderer/qgl.h" #include "renderer/tr_local.h" // glconfig +extern void Com_DrawDhewm3SettingsMenu(); // in framework/dhewm3SettingsMenu.cpp namespace D3 { namespace ImGuiHooks { @@ -19,6 +20,8 @@ namespace ImGuiHooks { ImGuiContext* imguiCtx = NULL; static bool haveNewFrame = false; +static int openImguiWindows = 0; // or-ed enum D3ImGuiWindow values + // using void* instead of SDL_Window and SDL_GLContext to avoid dragging SDL headers into sys_imgui.h bool Init(void* sdlWindow, void* sdlGlContext) { @@ -51,6 +54,17 @@ bool Init(void* sdlWindow, void* sdlGlContext) ImVec4* colors = style.Colors; colors[ImGuiCol_TitleBg] = ImVec4(0.28f, 0.36f, 0.48f, 0.88f); + colors[ImGuiCol_TabHovered] = ImVec4(0.42f, 0.69f, 1.00f, 0.80f); + colors[ImGuiCol_TabActive] = ImVec4(0.24f, 0.51f, 0.83f, 1.00f); + + int winIdx = SDL_GetWindowDisplayIndex( (SDL_Window*)sdlWindow ); + if (winIdx >= 0) { + float ddpi = 0, hdpi = 0, vdpi = 0; + SDL_GetDisplayDPI(winIdx, &ddpi, &hdpi, &vdpi); + printf("XXX ddpi = %f, hdpi = %f, vdpi = %f\n", ddpi, hdpi, vdpi); + // TODO: does this work good enough for high DPI support? + io.FontGlobalScale = vdpi / 96.0f; + } // Setup Platform/Renderer backends if ( ! ImGui_ImplSDL2_InitForOpenGL( (SDL_Window*)sdlWindow, sdlGlContext ) ) { @@ -96,58 +110,38 @@ void Shutdown() ImGui::DestroyContext( imguiCtx ); } +// NewFrame() is called once per D3 frame, after all events have been gotten +// => ProcessEvent() has already been called (probably multiple times) void NewFrame() { + if (openImguiWindows == 0) + return; + // Start the Dear ImGui frame ImGui_ImplOpenGL2_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); haveNewFrame = true; - // XXX hack for testing: - static bool show_another_window = false; - static bool show_demo_window = true; - - if(show_demo_window) - ImGui::ShowDemoWindow(&show_demo_window); - - - { - static float f = 0.0f; - static int counter = 0; - - ImGuiIO& io = ImGui::GetIO(); - - ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. - - ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) - ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state - ImGui::Checkbox("Another Window", &show_another_window); - - ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f - //ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color - - if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) - counter++; - ImGui::SameLine(); - ImGui::Text("counter = %d", counter); - - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); - ImGui::End(); + if (openImguiWindows & D3_ImGuiWin_Settings) { + Com_DrawDhewm3SettingsMenu(); } - if ( show_another_window ) { - ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) - ImGui::Text("Hello from another window!"); - if (ImGui::Button("Close Me")) - show_another_window = false; - ImGui::End(); + if (openImguiWindows & D3_ImGuiWin_Demo) { + bool show_demo_window = true; + ImGui::ShowDemoWindow(&show_demo_window); + if(!show_demo_window) + CloseWindow(D3_ImGuiWin_Demo); } } -// returns true if ImGui has handled the event and it shouldn't be passed to the game +// called with every SDL event by Sys_GetEvent() +// returns true if ImGui has handled the event (so it shouldn't be handled by D3) bool ProcessEvent(const void* sdlEvent) { + if (openImguiWindows == 0) + return false; + const SDL_Event* ev = (const SDL_Event*)sdlEvent; // ImGui_ImplSDL2_ProcessEvent() doc says: // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. @@ -180,6 +174,9 @@ bool ProcessEvent(const void* sdlEvent) void EndFrame() { + if (openImguiWindows == 0 && !haveNewFrame) + return; + // I think this can happen if we're not coming from idCommon::Frame() but screenshot or sth if ( !haveNewFrame ) { NewFrame(); @@ -222,6 +219,17 @@ void EndFrame() } } + +void OpenWindow( D3ImGuiWindow win ) +{ + openImguiWindows |= win; +} + +void CloseWindow( D3ImGuiWindow win ) +{ + openImguiWindows &= ~win; +} + }} //namespace D3::ImGuiHooks #endif // D3_ENABLE_IMGUI diff --git a/neo/sys/sys_imgui.h b/neo/sys/sys_imgui.h index a362ea4f..7e228386 100644 --- a/neo/sys/sys_imgui.h +++ b/neo/sys/sys_imgui.h @@ -8,6 +8,13 @@ namespace D3 { namespace ImGuiHooks { +enum D3ImGuiWindow { + D3_ImGuiWin_None = 0, + D3_ImGuiWin_Settings = 1, // advanced dhewm3 settings menu + D3_ImGuiWin_Demo = 2, // ImGui demo window + // next should be 4, then 8, etc so a bitmask can be used +}; + #ifndef IMGUI_DISABLE extern ImGuiContext* imguiCtx; // this is only here so IsImguiEnabled() can use it inline @@ -22,10 +29,20 @@ extern bool Init(void* sdlWindow, void* sdlGlContext); extern void Shutdown(); -extern void NewFrame(); +extern void OpenWindow( D3ImGuiWindow win ); +extern void CloseWindow( D3ImGuiWindow win ); + +// called with every SDL event by Sys_GetEvent() +// returns true if ImGui has handled the event (so it shouldn't be handled by D3) extern bool ProcessEvent(const void* sdlEvent); +// NewFrame() is called once per D3 frame, after all events have been gotten +// => ProcessEvent() has already been called (probably multiple times) +extern void NewFrame(); + +// called at the end of the D3 frame, when all other D3 rendering is done +// renders ImGui menus then extern void EndFrame(); #else // IMGUI_DISABLE - just stub out everything @@ -43,12 +60,16 @@ inline bool Init(void* sdlWindow, void* sdlGlContext) inline void Shutdown() {} -inline void NewFrame() {} - inline bool ProcessEvent(const void* sdlEvent) { return false; } +inline void NewFrame() {} + inline void EndFrame() {} +inline void OpenWindow( D3ImGuiWindow win ) {} + +inline void CloseWindow( D3ImGuiWindow win ) {} + #endif }} //namespace D3::ImGuiHooks