ImGuiLYCommonMenu.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "ImGuiLYCommonMenu.h"
  9. #ifdef IMGUI_ENABLED
  10. #include <AzCore/std/string/conversions.h>
  11. #include <AzCore/std/sort.h>
  12. #include <AzCore/StringFunc/StringFunc.h>
  13. #include <AzCore/Console/IConsole.h>
  14. #include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
  15. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  16. #include <AzFramework/Spawnable/Spawnable.h>
  17. #include <AzFramework/Viewport/ViewportBus.h>
  18. #include <imgui/imgui_internal.h>
  19. #include <ILevelSystem.h>
  20. #include "ImGuiColorDefines.h"
  21. #include "LYImGuiUtils/ImGuiDrawHelpers.h"
  22. // individual menus
  23. #include "ImGuiLYAssetExplorer.h"
  24. #include "ImGuiLYCameraMonitor.h"
  25. #include "ImGuiLYEntityOutliner.h"
  26. namespace ImGui
  27. {
  28. // Resolution Widths to recommend for usage for both O3DE Rendering and/or ImGui Rendering
  29. static int s_renderResolutionWidths[7] = { 800, 1280, 1600, 1920, 2560, 3440, 3840 };
  30. static int s_renderAspectRatios[4][2] = { {16,9}, {16,10}, {43,18}, {4,3} };
  31. static const char* s_toggleTelemetryConsoleCmd = "radtm_ToggleEnabled 1";
  32. ImGuiLYCommonMenu::ImGuiLYCommonMenu()
  33. : m_telemetryCaptureTime(8.0f)
  34. , m_telemetryCaptureTimeRemaining(-1.0f)
  35. , m_controllerLegendWindowVisible(false)
  36. {
  37. }
  38. ImGuiLYCommonMenu::~ImGuiLYCommonMenu()
  39. {
  40. }
  41. void ImGuiLYCommonMenu::Initialize()
  42. {
  43. // Connect EBusses
  44. ImGuiUpdateListenerBus::Handler::BusConnect();
  45. // init sub menu objects
  46. m_assetExplorer.Initialize();
  47. m_cameraMonitor.Initialize();
  48. m_entityOutliner.Initialize();
  49. m_inputMonitor.Initialize();
  50. m_deltaTimeHistogram.Init("onTick Delta Time (Milliseconds)", 250, LYImGuiUtils::HistogramContainer::ViewType::Histogram, true, 0.0f, 60.0f);
  51. AZ::TickBus::Handler::BusConnect();
  52. }
  53. void ImGuiLYCommonMenu::Shutdown()
  54. {
  55. // Disconnect EBusses
  56. AZ::TickBus::Handler::BusDisconnect();
  57. ImGuiUpdateListenerBus::Handler::BusDisconnect();
  58. // shutdown sub menu objects
  59. m_inputMonitor.Shutdown();
  60. m_assetExplorer.Shutdown();
  61. m_cameraMonitor.Shutdown();
  62. m_entityOutliner.Shutdown();
  63. }
  64. static auto SystemCursorStateComboGetter = []([[maybe_unused]] void* contextPtr, int idx, const char** out_text)
  65. {
  66. AzFramework::SystemCursorState cursorState = static_cast<AzFramework::SystemCursorState>(idx);
  67. switch (cursorState)
  68. {
  69. default:
  70. break;
  71. case AzFramework::SystemCursorState::Unknown:
  72. *out_text = "*unknown*";
  73. return true;
  74. case AzFramework::SystemCursorState::ConstrainedAndHidden:
  75. *out_text = "ConstrainedAndHidden";
  76. return true;
  77. case AzFramework::SystemCursorState::ConstrainedAndVisible:
  78. *out_text = "ConstrainedAndVisible";
  79. return true;
  80. case AzFramework::SystemCursorState::UnconstrainedAndHidden:
  81. *out_text = "UnconstrainedAndHidden";
  82. return true;
  83. case AzFramework::SystemCursorState::UnconstrainedAndVisible:
  84. *out_text = "UnconstrainedAndVisible";
  85. return true;
  86. }
  87. *out_text = "*error_unimplemented*";
  88. return false;
  89. };
  90. void ImGuiLYCommonMenu::OnImGuiUpdate()
  91. {
  92. float dpiScalingFactor = 1.0f;
  93. ImGuiManagerBus::BroadcastResult(dpiScalingFactor, &ImGuiManagerBus::Events::GetDpiScalingFactor);
  94. // Utility function to calculate the size in device pixels based on the current DPI
  95. const auto dpiAwareSizeFn = [dpiScalingFactor](float size)
  96. {
  97. return dpiScalingFactor * size;
  98. };
  99. AZStd::optional<AzFramework::ViewportBorderPadding> viewportBorderPaddingOpt;
  100. AzFramework::ViewportBorderRequestBus::BroadcastResult(
  101. viewportBorderPaddingOpt, &AzFramework::ViewportBorderRequestBus::Events::GetViewportBorderPadding);
  102. AzFramework::ViewportBorderPadding viewportBorderPadding = viewportBorderPaddingOpt.value_or(AzFramework::ViewportBorderPadding{});
  103. auto ctx = ImGui::GetCurrentContext();
  104. // determine if the window is the drop down and if it is active
  105. const auto windowIt = AZStd::find_if(
  106. ctx->Windows.begin(),
  107. ctx->Windows.end(),
  108. [](const ImGuiWindow* window)
  109. {
  110. // there is a drop down if the BeginOrder is 2 (1 below main menu),
  111. // drop down items are called "##Menu_00"
  112. return window->BeginOrderWithinContext == 2 && strcmp(window->Name, "##Menu_00") == 0 && window->WasActive;
  113. });
  114. if (windowIt != ctx->Windows.end())
  115. {
  116. m_markedForHiding = false;
  117. // this conditional stops the notification from repeatedly broadcasting
  118. if (m_dropdownState != ImGuiDropdownState::Shown)
  119. {
  120. m_dropdownState = ImGuiDropdownState::Shown;
  121. AzFramework::ViewportImGuiNotificationBus::Broadcast(
  122. &AzFramework::ViewportImGuiNotificationBus::Events::OnImGuiDropDownShown);
  123. }
  124. }
  125. else
  126. {
  127. // if it has already been marked that it is hidden, notify that it has done so
  128. if (m_dropdownState != ImGuiDropdownState::Hidden && m_markedForHiding)
  129. {
  130. m_dropdownState = ImGuiDropdownState::Hidden;
  131. AzFramework::ViewportImGuiNotificationBus::Broadcast(
  132. &AzFramework::ViewportImGuiNotificationBus::Events::OnImGuiDropDownHidden);
  133. m_markedForHiding = false;
  134. }
  135. else
  136. {
  137. // when the dropdown switches between options it counts as hidden, but it isn't known if it is
  138. // actually hidden or just switching, this acts as an intermediary for the next update
  139. // in the above conditional
  140. m_markedForHiding = true;
  141. }
  142. }
  143. // Utility function to return the current offset (scaled by DPI) if a viewport border
  144. // is active (otherwise 0.0)
  145. auto dpiAwareBorderOffsetFn = [&viewportBorderPaddingOpt, &dpiAwareSizeFn](float size)
  146. {
  147. return viewportBorderPaddingOpt.has_value() ? dpiAwareSizeFn(size) : 0.0f;
  148. };
  149. // Shift the menu down if a viewport border is active
  150. ImVec2 cachedSafeArea = ImGui::GetStyle().DisplaySafeAreaPadding;
  151. ImGui::GetStyle().DisplaySafeAreaPadding = ImVec2(cachedSafeArea.x, cachedSafeArea.y + dpiAwareSizeFn(viewportBorderPadding.m_top));
  152. if (ImGui::BeginMainMenuBar())
  153. {
  154. // Constant to shift right aligned menu items by (distance to the left) when a viewport border is active
  155. const float rightAlignedBorderOffset = dpiAwareBorderOffsetFn(36.0f);
  156. // Get Discrete Input state now, we will use it both inside the ImGui SubMenu, and along the main task bar ( when it is on )
  157. bool discreteInputEnabled = false;
  158. ImGuiManagerBus::BroadcastResult(discreteInputEnabled, &IImGuiManager::GetEnableDiscreteInputMode);
  159. // Input Mode Display
  160. {
  161. const float prevCursorPos = ImGui::GetCursorPosX();
  162. ImGui::SetCursorPosX(
  163. ImGui::GetWindowWidth() - dpiAwareSizeFn(300.0f + viewportBorderPadding.m_right) - rightAlignedBorderOffset);
  164. AZStd::string inputTitle = "Input: ";
  165. if (!discreteInputEnabled)
  166. {
  167. inputTitle.append("ImGui & Game");
  168. }
  169. else
  170. {
  171. // Discrete Input - Control ImGui and Game independently.
  172. ImGui::DisplayState state;
  173. ImGui::ImGuiManagerBus::BroadcastResult(state, &ImGui::IImGuiManager::GetDisplayState);
  174. if (state == DisplayState::Visible)
  175. {
  176. inputTitle.append("ImGui");
  177. }
  178. else
  179. {
  180. inputTitle.append("Game");
  181. }
  182. }
  183. if (ImGui::BeginMenu(inputTitle.c_str()))
  184. {
  185. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Discrete Input Mode. Currently Enabled:");
  186. ImGui::SameLine();
  187. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, discreteInputEnabled ? "True" : "False");
  188. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, " * Discrete Input mode ON: All input goes to both ImGui and the Game, all the time.");
  189. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, " * Discrete Input mode OFF: ImGui has three states 1)ImGui On, Input->ImGui, 2)ImGui On, Input->Game 3) ImGui Off");
  190. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, " * Hot Tip: use the LY Common -> ImGui Menu to toggle on and off discrete input mode, or the CVAR: 'imgui_DiscreteInputMode'");
  191. ImGui::Separator();
  192. ImGui::Text("Controller Legend ");
  193. ImGui::SameLine();
  194. if (ImGui::SmallButton("Window"))
  195. {
  196. m_controllerLegendWindowVisible = true;
  197. }
  198. ImGui::SameLine();
  199. if (ImGui::BeginMenu("Peek"))
  200. {
  201. OnImGuiUpdate_DrawControllerLegend();
  202. ImGui::EndMenu();
  203. }
  204. ImGui::EndMenu(); // inputTitle
  205. }
  206. ImGui::SetCursorPosX(prevCursorPos);
  207. }
  208. // Add some space before the first menu so it won't overlap with view control buttons
  209. ImGui::SetCursorPosX(dpiAwareSizeFn(40.0f + viewportBorderPadding.m_left));
  210. // Main Open 3D Engine menu
  211. if (ImGui::BeginMenu("O3DE"))
  212. {
  213. AZ::IConsole* console = AZ::Interface<AZ::IConsole>::Get();
  214. if (console != nullptr)
  215. {
  216. // Get the current console visibility status and cache it
  217. bool showConsole = true;
  218. console->GetCvarValue("bg_showDebugConsole", showConsole);
  219. const bool originalValue = showConsole;
  220. ImGui::Checkbox("Console", &showConsole);
  221. if (originalValue != showConsole)
  222. {
  223. // If the visibility state changed then update the console variable
  224. // Guard for edges so we don't continuously spam the console with commands
  225. console->PerformCommand(showConsole ? "bg_showDebugConsole true" : "bg_showDebugConsole false");
  226. }
  227. }
  228. if (ImGui::MenuItem("Delta Time Graph"))
  229. {
  230. m_showDeltaTimeGraphs = !m_showDeltaTimeGraphs;
  231. }
  232. // Asset Explorer
  233. if (ImGui::MenuItem("Asset Explorer"))
  234. {
  235. m_assetExplorer.ToggleEnabled();
  236. }
  237. // Camera Monitor
  238. if (ImGui::MenuItem("Camera Monitor"))
  239. {
  240. m_cameraMonitor.ToggleEnabled();
  241. }
  242. if (ImGui::MenuItem("Input Monitor"))
  243. {
  244. m_inputMonitor.ToggleEnabled();
  245. }
  246. // LY Entity Outliner
  247. if (ImGui::SmallButton("Launch"))
  248. {
  249. m_entityOutliner.ToggleEnabled();
  250. }
  251. ImGui::SameLine();
  252. if (ImGui::BeginMenu("Entity Outliner"))
  253. {
  254. m_entityOutliner.ImGuiUpdate_DrawComponentViewSubMenu();
  255. ImGui::EndMenu();
  256. }
  257. // Display Options
  258. if (ImGui::BeginMenu("Display Info"))
  259. {
  260. // Display Info
  261. static ICVar* rDisplayInfoCVar = gEnv->pConsole->GetCVar("r_DisplayInfo");
  262. if (rDisplayInfoCVar)
  263. {
  264. int displayInfoVal = rDisplayInfoCVar->GetIVal();
  265. int dragIntVal = displayInfoVal;
  266. ImGui::Text("r_DisplayInfo: %d ( View Runtime LY Debug Stats)", displayInfoVal);
  267. ImGui::SliderInt("##DisplayInfo", &dragIntVal, 0, 5);
  268. if (dragIntVal != displayInfoVal)
  269. {
  270. rDisplayInfoCVar->Set(dragIntVal);
  271. }
  272. }
  273. // Texel Density
  274. static ICVar* eTexelDensityCVAR = gEnv->pConsole->GetCVar("e_texeldensity");
  275. if (eTexelDensityCVAR)
  276. {
  277. int texelDensityValue = eTexelDensityCVAR->GetIVal();
  278. int dragIntVal = texelDensityValue;
  279. ImGui::Text("e_texeldensity: %d ( Used for Misc. LOD/MipMap debugging )", texelDensityValue);
  280. ImGui::SliderInt("##texelDensity", &dragIntVal, 0, 2);
  281. if (dragIntVal != texelDensityValue)
  282. {
  283. eTexelDensityCVAR->Set(dragIntVal);
  284. }
  285. }
  286. ImGui::EndMenu();
  287. }
  288. // View Maps ( pending valid ILevelSystem )
  289. auto lvlSystem = (gEnv && gEnv->pSystem) ? gEnv->pSystem->GetILevelSystem() : nullptr;
  290. if (lvlSystem && AzFramework::LevelSystemLifecycleInterface::Get() && ImGui::BeginMenu("Levels"))
  291. {
  292. if (AzFramework::LevelSystemLifecycleInterface::Get()->IsLevelLoaded())
  293. {
  294. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Current Level: ");
  295. ImGui::SameLine();
  296. ImGui::TextColored(
  297. ImGui::Colors::s_NiceLabelColor,
  298. "%s",
  299. AzFramework::LevelSystemLifecycleInterface::Get()->GetCurrentLevelName());
  300. }
  301. // Run through all the assets in the asset catalog and gather up the list of level assets
  302. AZ::Data::AssetType levelAssetType = lvlSystem->GetLevelAssetType();
  303. AZStd::vector<AZStd::string> levelNames;
  304. AZStd::set<AZStd::string> networkedLevelNames;
  305. auto enumerateCB =
  306. [levelAssetType, &levelNames, &networkedLevelNames]([[maybe_unused]] const AZ::Data::AssetId id, const AZ::Data::AssetInfo& assetInfo)
  307. {
  308. if (assetInfo.m_assetType == levelAssetType)
  309. {
  310. // A network spawnable is serialized to file as a ".network.spawnable". (See Multiplayer Gem's MultiplayerConstants.h)
  311. // Filter out network spawnables from the level list,
  312. // but keep track of which levels require networking so they can be recognized in the level selection menu.
  313. constexpr AZStd::fixed_string<32> networkSpawnablePrefix(".network");
  314. constexpr AZStd::fixed_string<32> networkSpawnableFileExtension = networkSpawnablePrefix + AzFramework::Spawnable::DotFileExtension;
  315. if (assetInfo.m_relativePath.ends_with(networkSpawnableFileExtension))
  316. {
  317. AZStd::string spawnablePath(assetInfo.m_relativePath);
  318. AZ::StringFunc::Replace(spawnablePath, networkSpawnablePrefix.c_str(), "");
  319. networkedLevelNames.emplace(spawnablePath);
  320. }
  321. else
  322. {
  323. levelNames.emplace_back(assetInfo.m_relativePath);
  324. }
  325. }
  326. };
  327. AZ::Data::AssetCatalogRequestBus::Broadcast(
  328. &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, nullptr, enumerateCB, nullptr);
  329. AZStd::sort(levelNames.begin(), levelNames.end());
  330. // Create a menu item for each level asset, with an action to load it if selected.
  331. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Load Level: ");
  332. for (int i = 0; i < levelNames.size(); i++)
  333. {
  334. bool isNetworked = networkedLevelNames.contains(levelNames[i]);
  335. if (ImGui::MenuItem(AZStd::string::format("%d- %s%s", i, levelNames[i].c_str(), isNetworked ? " (Multiplayer)":"").c_str()))
  336. {
  337. AZ::TickBus::QueueFunction(
  338. [lvlSystem, levelNames, i]()
  339. {
  340. lvlSystem->LoadLevel(levelNames[i].c_str());
  341. });
  342. }
  343. }
  344. ImGui::EndMenu();
  345. }
  346. if (ImGui::BeginMenu("Mouse/Cursor"))
  347. {
  348. AzFramework::SystemCursorState currentCursorState;
  349. AzFramework::InputSystemCursorRequestBus::EventResult(currentCursorState, AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::GetSystemCursorState);
  350. int comboCursorState = static_cast<int>(currentCursorState);
  351. ImGui::Combo("System Cursor State", &comboCursorState, SystemCursorStateComboGetter, this, static_cast<int>(AzFramework::SystemCursorState::UnconstrainedAndVisible) + 1);
  352. AzFramework::SystemCursorState postComboCursorState = static_cast<AzFramework::SystemCursorState>(comboCursorState);
  353. if (postComboCursorState != currentCursorState)
  354. {
  355. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id, &AzFramework::InputSystemCursorRequests::SetSystemCursorState, postComboCursorState);
  356. }
  357. ImGui::EndMenu();
  358. }
  359. if (ImGui::BeginMenu("Telemetry"))
  360. {
  361. bool telemetryToggleEnabled = false;
  362. if (ImGui::MenuItem("Toggle Enabled"))
  363. {
  364. telemetryToggleEnabled = true;
  365. m_telemetryCaptureTimeRemaining = -1;
  366. }
  367. if (telemetryToggleEnabled)
  368. {
  369. gEnv->pConsole->ExecuteString(s_toggleTelemetryConsoleCmd);
  370. }
  371. if (m_telemetryCaptureTimeRemaining <= 0.0f)
  372. {
  373. if (ImGui::SmallButton(AZStd::string::format("Enable for %.01f seconds \n(ImGui will close and re-open upon completion)", m_telemetryCaptureTime).c_str()))
  374. {
  375. StartTelemetryCapture();
  376. }
  377. ImGui::DragFloat("Capture Time", &m_telemetryCaptureTime, 0.1f, 0.1f, 600.0f);
  378. }
  379. else
  380. {
  381. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Currently Auto-Capturing for % .01f / % .01f", (m_telemetryCaptureTime - m_telemetryCaptureTimeRemaining), m_telemetryCaptureTime);
  382. }
  383. ImGui::EndMenu();
  384. }
  385. if (ImGui::BeginMenu("Video Options"))
  386. {
  387. // VSync
  388. IMGUI_DRAW_CVAR_CHECKBOX("vsync_interval", "VSync");
  389. // Max Frame Rate
  390. static ICVar* maxFPSCVar = gEnv->pConsole->GetCVar("sys_MaxFPS");
  391. if (maxFPSCVar)
  392. {
  393. // Display max frame rate
  394. ImGui::Text("Max FPS: %d", maxFPSCVar->GetIVal());
  395. // Shortcut buttons
  396. int fpsToSet = 0;
  397. if (ImGui::SmallButton("30"))
  398. {
  399. fpsToSet = 30;
  400. }
  401. ImGui::SameLine();
  402. if (ImGui::SmallButton("60"))
  403. {
  404. fpsToSet = 60;
  405. }
  406. ImGui::SameLine();
  407. if (ImGui::SmallButton("unlocked"))
  408. {
  409. fpsToSet = -1;
  410. }
  411. // fpsToSet will be 0 if no one set it.
  412. if (fpsToSet && fpsToSet != maxFPSCVar->GetIVal())
  413. {
  414. maxFPSCVar->Set(fpsToSet);
  415. }
  416. }
  417. ImGui::Separator();
  418. // FullScreen options
  419. IMGUI_DRAW_CVAR_CHECKBOX("r_Fullscreen", "FullScreen");
  420. IMGUI_DRAW_CVAR_CHECKBOX("r_FullscreenWindow", "FullScreen Window");
  421. IMGUI_DRAW_CVAR_CHECKBOX("r_FullscreenNativeRes", "FullScreen Native Resolution");
  422. ImGui::Separator();
  423. // Render Resolution ( pending valid CVARS
  424. static ICVar* widthCVar = gEnv->pConsole->GetCVar("r_width");
  425. static ICVar* heightCVar = gEnv->pConsole->GetCVar("r_height");
  426. if (widthCVar && heightCVar)
  427. {
  428. if (ImGui::BeginMenu(AZStd::string::format("Render Resolution ( %d x %d )", widthCVar->GetIVal(), heightCVar->GetIVal()).c_str()))
  429. {
  430. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Current Render Resolution: ");
  431. ImGui::SameLine();
  432. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "%d x %d", widthCVar->GetIVal(), heightCVar->GetIVal());
  433. const int aspectRatioCount = sizeof(s_renderAspectRatios) / sizeof(s_renderAspectRatios[0]);
  434. const int resolutionWidthsCount = sizeof(s_renderResolutionWidths) / sizeof(s_renderResolutionWidths[0]);
  435. for (int i = 0; i < aspectRatioCount; i++)
  436. {
  437. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "%d:%d", s_renderAspectRatios[i][0], s_renderAspectRatios[i][1]);
  438. for (int j = 0; j < resolutionWidthsCount; j++)
  439. {
  440. const int renderHeight = s_renderResolutionWidths[j] * s_renderAspectRatios[i][1] / s_renderAspectRatios[i][0];
  441. if (ImGui::Button(AZStd::string::format("%d x %d", s_renderResolutionWidths[j], renderHeight).c_str(), ImVec2(400, 0)))
  442. {
  443. widthCVar->Set(s_renderResolutionWidths[j]);
  444. heightCVar->Set(renderHeight);
  445. }
  446. }
  447. }
  448. // End Render Resolution menu
  449. ImGui::EndMenu();
  450. }
  451. }
  452. // End Video Options Menu
  453. ImGui::EndMenu();
  454. }
  455. // Frame Rate Cap and VSync CVAR
  456. if (ImGui::BeginMenu("ImGui Options"))
  457. {
  458. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Input Options:");
  459. // Controller Support - Contextual
  460. {
  461. bool controllerEnabled = false;
  462. ImGuiManagerBus::BroadcastResult(controllerEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Contextual);
  463. bool controllerEnabledCheckbox = controllerEnabled;
  464. ImGui::Checkbox(AZStd::string::format("Controller Support (Contextual) %s (Click Checkbox to Toggle)", controllerEnabledCheckbox ? "On" : "Off").c_str(), &controllerEnabledCheckbox);
  465. if (controllerEnabledCheckbox != controllerEnabled)
  466. {
  467. ImGuiManagerBus::Broadcast(&IImGuiManager::EnableControllerSupportMode, ImGuiControllerModeFlags::Contextual, controllerEnabledCheckbox);
  468. }
  469. }
  470. // Controller Support - Mouse
  471. {
  472. bool controllerMouseEnabled = false;
  473. ImGuiManagerBus::BroadcastResult(controllerMouseEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Mouse);
  474. bool controllerMouseEnabledCheckbox = controllerMouseEnabled;
  475. ImGui::Checkbox(AZStd::string::format("Controller Support (Mouse) %s (Click Checkbox to Toggle)", controllerMouseEnabledCheckbox ? "On" : "Off").c_str(), &controllerMouseEnabledCheckbox);
  476. if (controllerMouseEnabledCheckbox != controllerMouseEnabled)
  477. {
  478. ImGuiManagerBus::Broadcast(&IImGuiManager::EnableControllerSupportMode, ImGuiControllerModeFlags::Mouse, controllerMouseEnabledCheckbox);
  479. }
  480. // Only draw Controller Mouse Sensitivity slider if the mouse is enabled
  481. if (controllerMouseEnabled)
  482. {
  483. float controllerMouseSensitivity = 1.0f;
  484. ImGuiManagerBus::BroadcastResult(controllerMouseSensitivity, &IImGuiManager::GetControllerMouseSensitivity);
  485. float controllerMouseSensitivitySlider = controllerMouseSensitivity;
  486. ImGui::DragFloat("Controller Mouse Sensitivity", &controllerMouseSensitivitySlider, 0.1f, 0.1f, 50.0f);
  487. if (controllerMouseSensitivitySlider != controllerMouseSensitivity)
  488. {
  489. ImGuiManagerBus::Broadcast(&IImGuiManager::SetControllerMouseSensitivity, controllerMouseSensitivitySlider);
  490. }
  491. }
  492. }
  493. // Discrete Input Mode
  494. // Not available in edit mode because inputs are discrete there anyway.
  495. if (!gEnv->IsEditor() || gEnv->IsEditorGameMode())
  496. {
  497. bool discreteInputEnabledCheckbox = discreteInputEnabled;
  498. ImGui::Checkbox(AZStd::string::format("Discrete Input %s (Click Checkbox to Toggle)", discreteInputEnabledCheckbox ? "On" : "Off").c_str(), &discreteInputEnabledCheckbox);
  499. if (discreteInputEnabledCheckbox != discreteInputEnabled)
  500. {
  501. ImGuiManagerBus::Broadcast(&IImGuiManager::SetEnableDiscreteInputMode, discreteInputEnabledCheckbox);
  502. }
  503. }
  504. // Controller Legend
  505. ImGui::Text("Controller Legend ");
  506. ImGui::SameLine();
  507. if (ImGui::SmallButton("Window"))
  508. {
  509. m_controllerLegendWindowVisible = true;
  510. }
  511. ImGui::SameLine();
  512. if (ImGui::BeginMenu("Peek"))
  513. {
  514. OnImGuiUpdate_DrawControllerLegend();
  515. ImGui::EndMenu();
  516. }
  517. ImGui::Separator();
  518. if (ImGui::BeginMenu("ImGui Resolution"))
  519. {
  520. // Resolution Mode
  521. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "ImGui Resolution Mode:");
  522. ImGuiResolutionMode resMode = ImGuiResolutionMode::MatchRenderResolution;
  523. ImGuiManagerBus::BroadcastResult(resMode, &IImGuiManager::GetResolutionMode);
  524. int resModeRadioBtn = static_cast<int>(resMode);
  525. ImGui::RadioButton("Force Resolution", &resModeRadioBtn, static_cast<int>(ImGuiResolutionMode::LockToResolution));
  526. ImGui::SameLine();
  527. ImGui::RadioButton("Match Render Resolution", &resModeRadioBtn, static_cast<int>(ImGuiResolutionMode::MatchRenderResolution));
  528. ImGui::SameLine();
  529. ImGui::RadioButton("Match Render Resolution To Max", &resModeRadioBtn, static_cast<int>(ImGuiResolutionMode::MatchToMaxRenderResolution));
  530. ImGuiResolutionMode resModeRadioBtnResult = static_cast<ImGuiResolutionMode>(resModeRadioBtn);
  531. if (resModeRadioBtnResult != resMode)
  532. {
  533. ImGuiManagerBus::Broadcast(&IImGuiManager::SetResolutionMode, resModeRadioBtnResult);
  534. }
  535. // Resolutions
  536. ImVec2 imGuiRes;
  537. ImGuiManagerBus::BroadcastResult(imGuiRes, &IImGuiManager::GetImGuiRenderResolution);
  538. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Current ImGui Resolution: ");
  539. ImGui::SameLine();
  540. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "%.0f x %.0f", imGuiRes.x, imGuiRes.y);
  541. const int aspectRatioCount = sizeof(s_renderAspectRatios) / sizeof(s_renderAspectRatios[0]);
  542. const int resolutionWidthsCount = sizeof(s_renderResolutionWidths) / sizeof(s_renderResolutionWidths[0]);
  543. for (int i = 0; i < aspectRatioCount; i++)
  544. {
  545. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "%d:%d", s_renderAspectRatios[i][0], s_renderAspectRatios[i][1]);
  546. for (int j = 0; j < resolutionWidthsCount; j++)
  547. {
  548. const int renderHeight = s_renderResolutionWidths[j] * s_renderAspectRatios[i][1] / s_renderAspectRatios[i][0];
  549. if (ImGui::Button(AZStd::string::format("%d x %d", s_renderResolutionWidths[j], renderHeight).c_str(), ImVec2(400, 0)))
  550. {
  551. ImVec2 newRenderRes(static_cast<float>(s_renderResolutionWidths[j]), static_cast<float>(renderHeight));
  552. ImGuiManagerBus::Broadcast(&IImGuiManager::SetImGuiRenderResolution, newRenderRes);
  553. }
  554. }
  555. }
  556. // End ImGui Render Resolution menu
  557. ImGui::EndMenu();
  558. }
  559. // End ImGui Options Menu
  560. ImGui::EndMenu();
  561. }
  562. if (ImGui::BeginMenu("Misc."))
  563. {
  564. // Assert Level
  565. static ICVar* gAssertLevelCVAR = gEnv->pConsole->GetCVar("sys_asserts");
  566. if (gAssertLevelCVAR)
  567. {
  568. int assertLevelValue = gAssertLevelCVAR->GetIVal();
  569. int dragIntVal = assertLevelValue;
  570. ImGui::Text("sys_asserts: %d ( 0-off | 1-log | 2-popup | 3-crash )", assertLevelValue);
  571. ImGui::SliderInt("##sys_asserts", &dragIntVal, 0, 3);
  572. if (dragIntVal != assertLevelValue)
  573. {
  574. gAssertLevelCVAR->Set(dragIntVal);
  575. }
  576. }
  577. // End Misc Options Menu
  578. ImGui::EndMenu();
  579. }
  580. if (ImGui::MenuItem("ImGui Demo"))
  581. {
  582. m_showImGuiDemo = true;
  583. }
  584. // End LY Common Tools menu
  585. ImGui::EndMenu();
  586. }
  587. if (m_showImGuiDemo)
  588. {
  589. ImGui::ShowDemoWindow(&m_showImGuiDemo);
  590. }
  591. const float labelSize = dpiAwareSizeFn(100.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset;
  592. const float buttonSize = dpiAwareSizeFn(40.0f + viewportBorderPadding.m_right) + rightAlignedBorderOffset;
  593. ImGuiUpdateListenerBus::Broadcast(&IImGuiUpdateListener::OnImGuiMainMenuUpdate);
  594. ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - labelSize);
  595. float backgroundHeight = ImGui::GetTextLineHeight() + dpiAwareSizeFn(3.0f);
  596. ImVec2 cursorPos = ImGui::GetCursorScreenPos();
  597. ImGui::GetWindowDrawList()->AddRectFilled(
  598. cursorPos, ImVec2(cursorPos.x + labelSize, cursorPos.y + backgroundHeight), IM_COL32(0, 115, 187, 255));
  599. ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - labelSize);
  600. ImGui::SetCursorPosY(ImGui::GetCursorPosY() - 1);
  601. ImGui::Text("ImGui:ON");
  602. ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - buttonSize);
  603. ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 0, 0, 255));
  604. ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(255, 255, 255, 255));
  605. ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(128, 128, 128, 255));
  606. if (ImGui::SmallButton("home"))
  607. {
  608. ImGuiManagerBus::Broadcast(&IImGuiManager::ToggleThroughImGuiVisibleState);
  609. }
  610. ImGui::PopStyleColor(3);
  611. ImGui::EndMainMenuBar();
  612. }
  613. // Restore original safe area.
  614. ImGui::GetStyle().DisplaySafeAreaPadding = cachedSafeArea;
  615. // Update Contextual Controller Window
  616. if (m_controllerLegendWindowVisible)
  617. {
  618. if (ImGui::Begin("Controller ImGui Input Legend", &m_controllerLegendWindowVisible, ImGuiWindowFlags_NoSavedSettings))
  619. {
  620. OnImGuiUpdate_DrawControllerLegend();
  621. }
  622. ImGui::End();
  623. }
  624. // Update sub menus
  625. m_assetExplorer.ImGuiUpdate();
  626. m_cameraMonitor.ImGuiUpdate();
  627. m_inputMonitor.ImGuiUpdate();
  628. m_entityOutliner.ImGuiUpdate();
  629. if (m_showDeltaTimeGraphs)
  630. {
  631. ImGui::SetNextWindowSize({ 500, 200 }, ImGuiCond_Once);
  632. if (ImGui::Begin(
  633. "Delta Time Graphs", &m_showDeltaTimeGraphs,
  634. ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_NoSavedSettings))
  635. {
  636. m_deltaTimeHistogram.Draw(ImGui::GetColumnWidth(), 100.0f);
  637. }
  638. ImGui::End();
  639. }
  640. }
  641. void ImGuiLYCommonMenu::OnImGuiUpdate_DrawControllerLegend()
  642. {
  643. bool contextualControllerEnabled = false;
  644. ImGuiManagerBus::BroadcastResult(contextualControllerEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Contextual);
  645. bool controllerMouseEnabled = false;
  646. ImGuiManagerBus::BroadcastResult(controllerMouseEnabled, &IImGuiManager::IsControllerSupportModeEnabled, ImGuiControllerModeFlags::Mouse);
  647. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Contextual Controller Input Legend. Currently Enabled:");
  648. ImGui::SameLine();
  649. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, contextualControllerEnabled ? "True" : "False");
  650. ImGui::NewLine();
  651. ImGui::Columns(2);
  652. ImGui::SetColumnWidth(-1, 170.0f);
  653. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Controller Input");
  654. ImGui::NextColumn();
  655. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "ImGui Action");
  656. ImGui::Separator();
  657. ImGui::NextColumn();
  658. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "D-Pad U/D/L/R");
  659. ImGui::NextColumn();
  660. ImGui::Bullet();
  661. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Move");
  662. ImGui::Bullet();
  663. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Tweak Values (when activated with A)");
  664. ImGui::Bullet();
  665. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Resize Window (when holding X)");
  666. ImGui::Separator();
  667. ImGui::NextColumn();
  668. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Left Stick");
  669. ImGui::NextColumn();
  670. ImGui::Bullet();
  671. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Scroll");
  672. ImGui::Bullet();
  673. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Move Window (when holding X)");
  674. ImGui::Separator();
  675. ImGui::NextColumn();
  676. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "X (Left Face Button)");
  677. ImGui::NextColumn();
  678. ImGui::Bullet();
  679. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Tap: Toggle Menu");
  680. ImGui::Bullet();
  681. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Hold + L1/R1: Focus Windows");
  682. ImGui::Bullet();
  683. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Hold + D-Pad: Resize Window");
  684. ImGui::Bullet();
  685. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Hold + Left Stick: Move Window");
  686. ImGui::Separator();
  687. ImGui::NextColumn();
  688. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Y (Upper Face Button)");
  689. ImGui::NextColumn();
  690. ImGui::Bullet();
  691. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Exit text / on-screen keyboard");
  692. ImGui::Separator();
  693. ImGui::NextColumn();
  694. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "B (Right Face Button)");
  695. ImGui::NextColumn();
  696. ImGui::Bullet();
  697. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Cancel / Close / Exit");
  698. ImGui::Separator();
  699. ImGui::NextColumn();
  700. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "A (Lower Face Button)");
  701. ImGui::NextColumn();
  702. ImGui::Bullet();
  703. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Activate / Open / Toggle");
  704. ImGui::Bullet();
  705. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Tweak values with D-Pad");
  706. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, " (+ L1/R1 to tweak slower/faster)");
  707. ImGui::NextColumn();
  708. ImGui::Columns(1);
  709. ImGui::Separator();
  710. ImGui::NewLine();
  711. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Controller Mouse Legend. Currently Enabled:");
  712. ImGui::SameLine();
  713. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, controllerMouseEnabled ? "True" : "False");
  714. ImGui::NewLine();
  715. ImGui::Columns(2);
  716. ImGui::SetColumnWidth(-1, 170.0f);
  717. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Controller Input");
  718. ImGui::NextColumn();
  719. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Mouse Action");
  720. ImGui::Separator();
  721. ImGui::NextColumn();
  722. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Left Stick");
  723. ImGui::NextColumn();
  724. ImGui::Bullet();
  725. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Move Mouse Pointer");
  726. ImGui::Separator();
  727. ImGui::NextColumn();
  728. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "A");
  729. ImGui::NextColumn();
  730. ImGui::Bullet();
  731. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Left Mouse Button (Btn1)");
  732. ImGui::Separator();
  733. ImGui::NextColumn();
  734. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "B");
  735. ImGui::NextColumn();
  736. ImGui::Bullet();
  737. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Right Mouse Button (Btn2)");
  738. ImGui::NextColumn();
  739. ImGui::Columns(1);
  740. }
  741. void ImGuiLYCommonMenu::StartTelemetryCapture()
  742. {
  743. // Start the Capture
  744. gEnv->pConsole->ExecuteString(s_toggleTelemetryConsoleCmd);
  745. // Set the timer and connect to tick bus to count down.
  746. m_telemetryCaptureTimeRemaining = m_telemetryCaptureTime;
  747. // Get the current ImGui Display state to restore it later.
  748. ImGuiManagerBus::BroadcastResult(m_telemetryCapturePreCaptureState, &IImGuiManager::GetDisplayState);
  749. // Turn off the ImGui Manager
  750. ImGuiManagerBus::Broadcast(&IImGuiManager::SetDisplayState, DisplayState::Hidden);
  751. }
  752. void ImGuiLYCommonMenu::StopTelemetryCapture()
  753. {
  754. // Stop the Capture
  755. gEnv->pConsole->ExecuteString(s_toggleTelemetryConsoleCmd);
  756. // Restore ImGui State
  757. // Turn off the ImGui Manager
  758. ImGuiManagerBus::Broadcast(&IImGuiManager::SetDisplayState, m_telemetryCapturePreCaptureState);
  759. // Reset timer and disconnect tick bus
  760. m_telemetryCaptureTimeRemaining = 0.0f;
  761. }
  762. // OnTick just used for telemetry captures.
  763. void ImGuiLYCommonMenu::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  764. {
  765. m_deltaTimeHistogram.PushValue(deltaTime*1000.0f); // convert to milliseconds
  766. if (m_telemetryCaptureTimeRemaining > 0.0f)
  767. {
  768. m_telemetryCaptureTimeRemaining -= deltaTime;
  769. if (m_telemetryCaptureTimeRemaining <= 0.0f)
  770. {
  771. StopTelemetryCapture();
  772. }
  773. }
  774. }
  775. } // namespace ImGui
  776. #endif // IMGUI_ENABLED