diff --git a/extern/aurora b/extern/aurora index 3bedc9950d..2a9a7943f1 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 3bedc9950da12533f34c1f51fce1b7e6fe5ddcbc +Subproject commit 2a9a7943f1ddc8cb275d31c99387aa62a338387d diff --git a/include/dusk/main.h b/include/dusk/main.h index 8c1335d5c8..a9194e2aba 100644 --- a/include/dusk/main.h +++ b/include/dusk/main.h @@ -2,6 +2,7 @@ #define DUSK_MAIN_H namespace dusk { + extern bool IsRunning; extern bool IsShuttingDown; extern bool IsGameLaunched; } diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 3e085b411e..5345f4ddde 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -73,6 +73,7 @@ struct UserSettings { struct { ConfigVar isoPath; + ConfigVar graphicsBackend; ConfigVar skipPreLaunchUI; } backend; }; diff --git a/res/Inter-Bold.ttf b/res/Inter-Bold.ttf new file mode 100644 index 0000000000..a7488869f2 Binary files /dev/null and b/res/Inter-Bold.ttf differ diff --git a/res/Inter-Regular.ttf b/res/Inter-Regular.ttf new file mode 100644 index 0000000000..963802bac9 Binary files /dev/null and b/res/Inter-Regular.ttf differ diff --git a/res/icon.png b/res/icon.png new file mode 100644 index 0000000000..b7a0614b9b Binary files /dev/null and b/res/icon.png differ diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index 3328d1b97c..7e76ba4e9a 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -253,3 +253,9 @@ void dusk::ImGuiMenuTools::ShowAudioDebug() { ImGui::End(); } + +void dusk::ImGuiMenuTools::ShowSaveEditor() { + if (m_showSaveEditor) { + m_saveEditor.draw(m_showSaveEditor); + } +} diff --git a/src/dusk/imgui/ImGuiCameraOverlay.cpp b/src/dusk/imgui/ImGuiCameraOverlay.cpp index 599af30f37..49e6040649 100644 --- a/src/dusk/imgui/ImGuiCameraOverlay.cpp +++ b/src/dusk/imgui/ImGuiCameraOverlay.cpp @@ -25,7 +25,7 @@ namespace dusk { windowFlags |= ImGuiWindowFlags_NoMove; } - ImGui::SetNextWindowBgAlpha(0.65f); + // ImGui::SetNextWindowBgAlpha(0.65f); if (!ImGui::Begin("Camera Debug", nullptr, windowFlags)) { ImGui::End(); diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 7f12b1a570..83ce09f35b 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -200,33 +200,19 @@ namespace dusk { ImGuiConsole::ImGuiConsole() {} - void ImGuiConsole::InitSettings() { - bool lockAspect = getSettings().video.lockAspectRatio; - if (lockAspect) { - VILockAspectRatio(defaultAspectRatioW, defaultAspectRatioH); - } else { - VIUnlockAspectRatio(); - } - - dusk::audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f); - dusk::audio::SetEnableReverb(getSettings().audio.enableReverb); - } - void ImGuiConsole::UpdateSettings() { getTransientSettings().skipFrameRateLimit = getSettings().game.enableTurboKeybind && ImGui::IsKeyDown(ImGuiKey_Tab); } void ImGuiConsole::PreDraw() { ZoneScoped; - if (!m_isLaunchInitialized) { - InitSettings(); - + if (dusk::IsGameLaunched && !m_isLaunchInitialized) { m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f); m_isLaunchInitialized = true; } UpdateSettings(); - + if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) || ImGui::IsKeyDown(ImGuiKey_RightCtrl)) && ImGui::IsKeyPressed(ImGuiKey_R)) { @@ -237,27 +223,11 @@ namespace dusk { ImGuiMenuGame::ToggleFullscreen(); } - if (!dusk::IsGameLaunched) { - m_preLaunchWindow.draw(); - } - else if (CheckMenuViewToggle(ImGuiKey_F1, m_isHidden)) { - ShowToasts(); - ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; - SDL_HideCursor(); - return; - } + bool showMenu = !dusk::IsGameLaunched || !CheckMenuViewToggle(ImGuiKey_F1, m_isHidden); - ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; - // Imgui will re-show cursor. - - // TODO: we need to be able to render the menu bar & any overlays separately - // The code currently ties them all together, so hiding the menu hides all windows - - if (ImGui::BeginMainMenuBar()) { + if (showMenu && ImGui::BeginMainMenuBar()) { m_menuGame.draw(); m_menuEnhancements.draw(); - - // Keep always last m_menuTools.draw(); ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 80.0f * ImGuiScale()); @@ -267,6 +237,34 @@ namespace dusk { ImGui::EndMainMenuBar(); } + if (!dusk::IsGameLaunched) { + m_preLaunchWindow.draw(); + } + + m_menuGame.windowControllerConfig(); + m_menuGame.windowInputViewer(); + if (dusk::IsGameLaunched) { + m_menuTools.ShowDebugOverlay(); + m_menuTools.ShowCameraOverlay(); + m_menuTools.ShowProcessManager(); + m_menuTools.ShowHeapOverlay(); + m_menuTools.ShowStubLog(); + m_menuTools.ShowMapLoader(); + m_menuTools.ShowPlayerInfo(); + m_menuTools.ShowAudioDebug(); + m_menuTools.ShowSaveEditor(); + } + DuskDebugPad(); // temporary, remove later + + // Only show cursor when menu or any windows are open + if (showMenu || ImGui::GetIO().MetricsRenderWindows > 0) { + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; + // Imgui will re-show cursor. + } else { + ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; + SDL_HideCursor(); + } + ShowToasts(); } @@ -306,6 +304,70 @@ namespace dusk { } } + std::string_view backend_id(AuroraBackend backend) { + switch (backend) { + default: + return "auto"sv; + case BACKEND_D3D12: + return "d3d12"sv; + case BACKEND_D3D11: + return "d3d11"sv; + case BACKEND_METAL: + return "metal"sv; + case BACKEND_VULKAN: + return "vulkan"sv; + case BACKEND_OPENGL: + return "opengl"sv; + case BACKEND_OPENGLES: + return "opengles"sv; + case BACKEND_WEBGPU: + return "webgpu"sv; + case BACKEND_NULL: + return "null"sv; + } + } + + bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend) { + if (backend == "auto") { + outBackend = BACKEND_AUTO; + return true; + } + if (backend == "d3d11") { + outBackend = BACKEND_D3D11; + return true; + } + if (backend == "d3d12") { + outBackend = BACKEND_D3D12; + return true; + } + if (backend == "metal") { + outBackend = BACKEND_METAL; + return true; + } + if (backend == "vulkan") { + outBackend = BACKEND_VULKAN; + return true; + } + if (backend == "opengl") { + outBackend = BACKEND_OPENGL; + return true; + } + if (backend == "opengles") { + outBackend = BACKEND_OPENGLES; + return true; + } + if (backend == "webgpu") { + outBackend = BACKEND_WEBGPU; + return true; + } + if (backend == "null") { + outBackend = BACKEND_NULL; + return true; + } + + return false; + } + void ImGuiConsole::ShowToasts() { if (m_toasts.empty()) { return; diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index bb420e6d1f..b7a48e8613 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "ImGuiMenuEnhancements.hpp" #include "ImGuiMenuGame.hpp" @@ -15,7 +16,6 @@ namespace dusk { class ImGuiConsole { public: ImGuiConsole(); - void InitSettings(); void UpdateSettings(); void PreDraw(); void PostDraw(); @@ -49,6 +49,8 @@ private: extern ImGuiConsole g_imguiConsole; std::string_view backend_name(AuroraBackend backend); +std::string_view backend_id(AuroraBackend backend); +bool try_parse_backend(std::string_view backend, AuroraBackend& outBackend); std::string BytesToString(size_t bytes); void SetOverlayWindowLocation(int corner); bool ShowCornerContextMenu(int& corner, int avoidCorner); diff --git a/src/dusk/imgui/ImGuiEngine.cpp b/src/dusk/imgui/ImGuiEngine.cpp index 15879d49db..03267df97a 100644 --- a/src/dusk/imgui/ImGuiEngine.cpp +++ b/src/dusk/imgui/ImGuiEngine.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "dusk/logging.h" @@ -33,21 +34,23 @@ bool AssetExists(const std::string& path) { ImFont* ImGuiEngine::fontNormal; ImFont* ImGuiEngine::fontLarge; ImFont* ImGuiEngine::fontExtraLarge; +ImFont* ImGuiEngine::fontMono; ImTextureID ImGuiEngine::duskIcon = 0; -inline ImFont* CreateFont(float size, bool hasFontFile, const std::string& fontPath) { - ImGuiIO& io = ImGui::GetIO(); - +inline ImFont* CreateFont(float size, const std::string& fontPath, std::string_view fontName) { + bool fontFileExists = !fontPath.empty() && AssetExists(fontPath); ImFontConfig fontConfig{}; fontConfig.SizePixels = size; - snprintf(static_cast(fontConfig.Name), sizeof(fontConfig.Name), - "Noto Mono Regular, %dpx", static_cast(fontConfig.SizePixels)); + auto name = fmt::format_to_n(fontConfig.Name, sizeof(fontConfig.Name) - 1, "{}, {}px", fontName, + static_cast(fontConfig.SizePixels)); + *name.out = '\0'; + const ImGuiIO& io = ImGui::GetIO(); ImFont* outFont = - hasFontFile ? + fontFileExists ? io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) : nullptr; if (outFont == nullptr) { - if (hasFontFile) { + if (fontFileExists) { DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError()); } outFont = io.Fonts->AddFontDefault(&fontConfig); @@ -56,65 +59,38 @@ inline ImFont* CreateFont(float size, bool hasFontFile, const std::string& font } void ImGuiEngine_Initialize(float scale) { + // Round font scale to nearest integer + scale = std::ceilf(scale); + ImGui::GetCurrentContext(); ImGuiIO& io = ImGui::GetIO(); io.Fonts->Clear(); io.FontGlobalScale = scale > 0.0f ? 1.0f / scale : 1.0f; - const std::string fontPath = GetAssetPath("NotoMono-Regular.ttf"); - const bool hasFontFile = AssetExists(fontPath); - - ImFontConfig fontConfig{}; - fontConfig.SizePixels = std::floor(15.f * scale); - snprintf(static_cast(fontConfig.Name), sizeof(fontConfig.Name), - "Noto Mono Regular, %dpx", static_cast(fontConfig.SizePixels)); ImGuiEngine::fontNormal = - hasFontFile ? - io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) : - nullptr; - if (ImGuiEngine::fontNormal == nullptr) { - if (hasFontFile) { - DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError()); - } - ImGuiEngine::fontNormal = io.Fonts->AddFontDefault(&fontConfig); - } - - fontConfig.SizePixels = std::floor(26.f * scale); -#ifdef IMGUI_ENABLE_FREETYPE - fontConfig.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold; - snprintf(static_cast(fontConfig.Name), sizeof(fontConfig.Name), "Noto Mono Bold, %dpx", - static_cast(fontConfig.SizePixels)); -#else - snprintf(static_cast(fontConfig.Name), sizeof(fontConfig.Name), - "Noto Mono Regular, %dpx", static_cast(fontConfig.SizePixels)); -#endif + CreateFont(std::floor(18.f * scale), GetAssetPath("Inter-Regular.ttf"), "Inter Regular"); ImGuiEngine::fontLarge = - hasFontFile ? - io.Fonts->AddFontFromFileTTF(fontPath.c_str(), fontConfig.SizePixels, &fontConfig) : - nullptr; - if (ImGuiEngine::fontLarge == nullptr) { - if (hasFontFile) { - DuskLog.warn("Failed to load font '{}': {}", fontPath, SDL_GetError()); - } - ImGuiEngine::fontLarge = io.Fonts->AddFontDefault(&fontConfig); - } - - ImGuiEngine::fontExtraLarge = CreateFont(std::floor(40.f * scale), hasFontFile, fontPath); + CreateFont(std::floor(26.f * scale), GetAssetPath("Inter-Regular.ttf"), "Inter Regular"); + ImGuiEngine::fontExtraLarge = + CreateFont(std::floor(40.f * scale), GetAssetPath("Inter-Bold.ttf"), "Inter Bold"); + ImGuiEngine::fontMono = + CreateFont(std::floor(16.f * scale), GetAssetPath("NotoMono-Regular.ttf"), + "Noto Mono Regular"); auto& style = ImGui::GetStyle(); style = {}; // Reset sizes style.WindowPadding = ImVec2(15, 15); style.WindowRounding = 5.0f; style.FrameBorderSize = 1.f; - style.FramePadding = ImVec2(5, 5); + style.FramePadding = ImVec2(8, 5); style.FrameRounding = 4.0f; style.ItemSpacing = ImVec2(12, 8); style.ItemInnerSpacing = ImVec2(8, 6); style.IndentSpacing = 25.0f; style.ScrollbarSize = 15.0f; style.ScrollbarRounding = 9.0f; - style.GrabMinSize = 5.0f; - style.GrabRounding = 3.0f; + style.GrabMinSize = 20.0f; + style.GrabRounding = 5.0f; style.PopupBorderSize = 1.f; style.PopupRounding = 7.0; style.TabBorderSize = 1.f; @@ -169,25 +145,23 @@ void ImGuiEngine_Initialize(float scale) { colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); - } -Icon GetIcon() { - const std::string iconPath = GetAssetPath("icon.png"); - if (!AssetExists(iconPath)) { +Image GetImage(const std::string& path) { + if (!AssetExists(path)) { return {}; } - SDL_Surface* loadedSurface = SDL_LoadPNG(iconPath.c_str()); + SDL_Surface* loadedSurface = SDL_LoadPNG(path.c_str()); if (loadedSurface == nullptr) { - DuskLog.warn("Failed to load icon '{}': {}", iconPath, SDL_GetError()); + DuskLog.warn("Failed to load image '{}': {}", path, SDL_GetError()); return {}; } SDL_Surface* rgbaSurface = SDL_ConvertSurface(loadedSurface, SDL_PIXELFORMAT_RGBA32); SDL_DestroySurface(loadedSurface); if (rgbaSurface == nullptr) { - DuskLog.warn("Failed to convert icon '{}': {}", iconPath, SDL_GetError()); + DuskLog.warn("Failed to convert image '{}': {}", path, SDL_GetError()); return {}; } @@ -204,7 +178,7 @@ Icon GetIcon() { } SDL_DestroySurface(rgbaSurface); - return Icon{ + return Image{ std::move(ptr), size, iconWidth, @@ -213,15 +187,13 @@ Icon GetIcon() { } void ImGuiEngine_AddTextures() { - if (ImGuiEngine::duskIcon != 0) - return; - - auto icon = GetIcon(); - if (icon.data == nullptr || icon.width == 0 || icon.height == 0) { - ImGuiEngine::duskIcon = 0; - return; + if (ImGuiEngine::duskIcon == 0) { + auto icon = GetImage(GetAssetPath("icon.png")); + if (icon.data == nullptr || icon.width == 0 || icon.height == 0) { + ImGuiEngine::duskIcon = 0; + return; + } + ImGuiEngine::duskIcon = aurora_imgui_add_texture(icon.width, icon.height, icon.data.get()); } - - ImGuiEngine::duskIcon = aurora_imgui_add_texture(icon.width, icon.height, icon.data.get()); } } // namespace dusk diff --git a/src/dusk/imgui/ImGuiEngine.hpp b/src/dusk/imgui/ImGuiEngine.hpp index 8d55c5b3d5..4b0baaa3a8 100644 --- a/src/dusk/imgui/ImGuiEngine.hpp +++ b/src/dusk/imgui/ImGuiEngine.hpp @@ -10,17 +10,18 @@ public: static ImFont* fontNormal; static ImFont* fontLarge; static ImFont* fontExtraLarge; + static ImFont* fontMono; static ImTextureID duskIcon; }; void ImGuiEngine_Initialize(float scale); void ImGuiEngine_AddTextures(); -struct Icon { +struct Image { std::unique_ptr data; size_t size; uint32_t width; uint32_t height; }; -Icon GetIcon(); +Image GetImage(std::string_view path); } // namespace dusk diff --git a/src/dusk/imgui/ImGuiMapLoader.cpp b/src/dusk/imgui/ImGuiMapLoader.cpp index 428d458903..e443f879af 100644 --- a/src/dusk/imgui/ImGuiMapLoader.cpp +++ b/src/dusk/imgui/ImGuiMapLoader.cpp @@ -16,7 +16,7 @@ namespace dusk { ImGuiWindowFlags windowFlags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; - ImGui::SetNextWindowBgAlpha(0.65f); + // ImGui::SetNextWindowBgAlpha(0.65f); if (!ImGui::Begin("Map Loader", &m_showMapLoader, windowFlags)) { ImGui::End(); diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 56f8b3b46e..2ceeb0a84c 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -17,6 +17,8 @@ #include +#include "dusk/main.h" + namespace dusk { void ImGuiMenuGame::ToggleFullscreen() { getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen); @@ -28,12 +30,6 @@ namespace dusk { void ImGuiMenuGame::draw() { if (ImGui::BeginMenu("Game")) { - if (ImGui::MenuItem("Reset", hotkeys::DO_RESET)) { - JUTGamePad::C3ButtonReset::sResetSwitchPushing = true; - } - - ImGui::Separator(); - if (ImGui::BeginMenu("Graphics")) { if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) { ToggleFullscreen(); @@ -113,11 +109,18 @@ namespace dusk { ImGui::EndMenu(); } + ImGui::Separator(); + + if (ImGui::MenuItem("Reset", hotkeys::DO_RESET)) { + JUTGamePad::C3ButtonReset::sResetSwitchPushing = true; + } + + if (ImGui::MenuItem("Exit")) { + dusk::IsRunning = false; + } + ImGui::EndMenu(); } - - windowInputViewer(); - windowControllerConfig(); } static void drawVirtualStick(const char* id, const ImVec2& stick) { @@ -182,7 +185,7 @@ namespace dusk { ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize; - ImGui::SetNextWindowBgAlpha(0.65f); + // ImGui::SetNextWindowBgAlpha(0.65f); if (!ImGui::Begin("Controller Config", &m_showControllerConfig, windowFlags)) { ImGui::End(); diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index c367b1a7f8..f6c29c0a16 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -7,22 +7,22 @@ #include "ImGuiConsole.hpp" #include "ImGuiMenuTools.hpp" +#include "ImGuiEngine.hpp" #include "d/actor/d_a_alink.h" #include "d/actor/d_a_horse.h" #include "d/d_com_inf_game.h" -#include "dusk/main.h" #include "dusk/dusk.h" +#include "dusk/main.h" #include "m_Do/m_Do_main.h" namespace dusk { ImGuiMenuTools::ImGuiMenuTools() {} void ImGuiMenuTools::draw() { - bool isToggleDevelopmentMode = false; - if (ImGui::BeginMenu("Debug")) { - if (ImGui::Checkbox("Development Mode", &m_isDevelopmentMode)) { - isToggleDevelopmentMode = true; + bool developmentMode = mDoMain::developmentMode == 1; + if (ImGui::Checkbox("Development Mode", &developmentMode)) { + mDoMain::developmentMode = developmentMode ? 1 : -1; } ImGui::Separator(); @@ -41,40 +41,27 @@ namespace dusk { ImGui::EndMenu(); } - if (dusk::IsGameLaunched) { - ImGui::MenuItem("Process Management", hotkeys::SHOW_PROCESS_MANAGEMENT, &m_showProcessManagement); - ImGui::MenuItem("Debug Overlay", hotkeys::SHOW_DEBUG_OVERLAY, &m_showDebugOverlay); - ImGui::MenuItem("Heap Viewer", hotkeys::SHOW_HEAP_VIEWER, &m_showHeapOverlay); - ImGui::MenuItem("Stub Log", hotkeys::SHOW_STUB_LOG, &m_showStubLog); - ImGui::MenuItem("Debug Camera", hotkeys::SHOW_CAMERA_DEBUG, &m_showCameraOverlay); - ImGui::MenuItem("Map Loader", nullptr, &m_showMapLoader); - ImGui::MenuItem("Player Info", nullptr, &m_showPlayerInfo); - ImGui::MenuItem("Save Editor", nullptr, &m_showSaveEditor); - ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug); + if (!dusk::IsGameLaunched) { + ImGui::BeginDisabled(); + } + + ImGui::MenuItem("Process Management", hotkeys::SHOW_PROCESS_MANAGEMENT, &m_showProcessManagement); + ImGui::MenuItem("Debug Overlay", hotkeys::SHOW_DEBUG_OVERLAY, &m_showDebugOverlay); + ImGui::MenuItem("Heap Viewer", hotkeys::SHOW_HEAP_VIEWER, &m_showHeapOverlay); + ImGui::MenuItem("Stub Log", hotkeys::SHOW_STUB_LOG, &m_showStubLog); + ImGui::MenuItem("Debug Camera", hotkeys::SHOW_CAMERA_DEBUG, &m_showCameraOverlay); + ImGui::MenuItem("Map Loader", nullptr, &m_showMapLoader); + ImGui::MenuItem("Player Info", nullptr, &m_showPlayerInfo); + ImGui::MenuItem("Save Editor", nullptr, &m_showSaveEditor); + ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug); + + if (!dusk::IsGameLaunched) { + ImGui::EndDisabled(); } ImGui::MenuItem("OSReport Force", nullptr, &OSReportReallyForceEnable); ImGui::EndMenu(); } - - if (isToggleDevelopmentMode) { - mDoMain::developmentMode = m_isDevelopmentMode ? 1 : -1; - } - - ShowDebugOverlay(); - ShowCameraOverlay(); - ShowProcessManager(); - ShowHeapOverlay(); - ShowStubLog(); - ShowMapLoader(); - ShowPlayerInfo(); - ShowAudioDebug(); - - if (m_showSaveEditor) { - m_saveEditor.draw(m_showSaveEditor); - } - - DuskDebugPad(); // temporary, remove later } void ImGuiMenuTools::ShowDebugOverlay() { @@ -82,6 +69,8 @@ namespace dusk { return; } + ImGui::PushFont(ImGuiEngine::fontMono); + ImGuiIO& io = ImGui::GetIO(); ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | @@ -94,26 +83,14 @@ namespace dusk { ImGui::SetNextWindowBgAlpha(0.65f); if (ImGui::Begin("Debug Overlay", nullptr, windowFlags)) { - bool hasPrevious = false; - if (hasPrevious) { - ImGui::Separator(); - } - hasPrevious = true; - ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.2f}\n"), io.Framerate)); ImGuiStringViewText(fmt::format(FMT_STRING("Frame usage: {:.1f}%\n"), frameUsagePct)); - if (hasPrevious) { - ImGui::Separator(); - } - hasPrevious = true; + ImGui::Separator(); ImGuiStringViewText(fmt::format(FMT_STRING("Backend: {}\n"), backend_name(aurora_get_backend()))); - if (hasPrevious) { - ImGui::Separator(); - } - hasPrevious = true; + ImGui::Separator(); const auto& stats = lastFrameAuroraStats; @@ -145,6 +122,8 @@ namespace dusk { ShowCornerContextMenu(m_debugOverlayCorner, m_cameraOverlayCorner); } ImGui::End(); + + ImGui::PopFont(); } void ImGuiMenuTools::ShowPlayerInfo() { @@ -152,13 +131,20 @@ namespace dusk { return; } - ImGuiIO& io = ImGui::GetIO(); - ImGuiWindowFlags windowFlags = - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize; + ImGui::PushFont(ImGuiEngine::fontMono); + + ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_AlwaysAutoResize | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoNav; + if (m_playerInfoOverlayCorner != -1) { + SetOverlayWindowLocation(m_playerInfoOverlayCorner); + windowFlags |= ImGuiWindowFlags_NoMove; + } ImGui::SetNextWindowBgAlpha(0.65f); - if (ImGui::Begin("Player Info", &m_showPlayerInfo, windowFlags)) { + if (ImGui::Begin("Player Info", nullptr, windowFlags)) { daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0); daHorse_c* horse = dComIfGp_getHorseActor(); @@ -200,8 +186,11 @@ namespace dusk { ? fmt::format("Speed: {0}\n", horse->speedF) : "Speed: ?\n" ); + + ShowCornerContextMenu(m_playerInfoOverlayCorner, m_debugOverlayCorner); } ImGui::End(); + ImGui::PopFont(); } } diff --git a/src/dusk/imgui/ImGuiMenuTools.hpp b/src/dusk/imgui/ImGuiMenuTools.hpp index 35604e0c93..37409ddb06 100644 --- a/src/dusk/imgui/ImGuiMenuTools.hpp +++ b/src/dusk/imgui/ImGuiMenuTools.hpp @@ -22,6 +22,7 @@ namespace dusk { void ShowMapLoader(); void ShowPlayerInfo(); void ShowAudioDebug(); + void ShowSaveEditor(); private: bool m_showDebugOverlay = false; @@ -51,8 +52,8 @@ namespace dusk { bool showInternalNames = false; } m_mapLoaderInfo; - bool m_isDevelopmentMode = false; bool m_showPlayerInfo = false; + int m_playerInfoOverlayCorner = 1; // top-right bool m_showSaveEditor = false; ImGuiSaveEditor m_saveEditor; diff --git a/src/dusk/imgui/ImGuiPreLaunchWindow.cpp b/src/dusk/imgui/ImGuiPreLaunchWindow.cpp index 965ae2c29d..15b229a3cb 100644 --- a/src/dusk/imgui/ImGuiPreLaunchWindow.cpp +++ b/src/dusk/imgui/ImGuiPreLaunchWindow.cpp @@ -19,10 +19,7 @@ namespace dusk { typedef void (ImGuiPreLaunchWindow::*drawFunc)(); -drawFunc drawTable[2] = { - &ImGuiPreLaunchWindow::drawMainMenu, - &ImGuiPreLaunchWindow::drawOptions -}; +drawFunc drawTable[2] = {&ImGuiPreLaunchWindow::drawMainMenu, &ImGuiPreLaunchWindow::drawOptions}; static constexpr std::array skGameDiscFileFilters{{ {"Game Disc Images", "iso;gcm;ciso;gcz;nfs;rvz;wbfs;wia;tgc"}, @@ -47,10 +44,7 @@ void fileDialogCallback(void* userdata, const char* const* filelist, [[maybe_unu } } - -ImGuiPreLaunchWindow::ImGuiPreLaunchWindow() { - -} +ImGuiPreLaunchWindow::ImGuiPreLaunchWindow() = default; bool ImGuiPreLaunchWindow::isSelectedPathValid() const { return !m_selectedIsoPath.empty() && SDL_GetPathInfo(m_selectedIsoPath.c_str(), nullptr); @@ -59,6 +53,7 @@ bool ImGuiPreLaunchWindow::isSelectedPathValid() const { void ImGuiPreLaunchWindow::draw() { if (m_IsFirstDraw) { m_selectedIsoPath = getSettings().backend.isoPath; + m_initialGraphicsBackend = getSettings().backend.graphicsBackend; m_IsFirstDraw = false; } @@ -71,20 +66,23 @@ void ImGuiPreLaunchWindow::draw() { ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y)); ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::SetNextWindowBgAlpha(0.65f); - ImGui::Begin("Pre Launch Window", nullptr, ImGuiWindowFlags_NoDecoration | - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus); + ImGui::Begin("Pre Launch Window", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoBringToFrontOnFocus); const auto& windowSize = ImGui::GetWindowSize(); for (int i = 0; i < 5; i++) ImGui::NewLine(); - float iconSize = 128.f; + float iconSize = 150.f; ImGui::SameLine(windowSize.x / 2 - iconSize + (iconSize / 2)); if (ImGuiEngine::duskIcon != 0) ImGui::Image(ImGuiEngine::duskIcon, ImVec2{iconSize, iconSize}); + ImGuiTextCenter("Twilit Realm presents"); ImGui::PushFont(ImGuiEngine::fontExtraLarge); ImGuiTextCenter("Dusk"); ImGui::PopFont(); @@ -96,52 +94,27 @@ void ImGuiPreLaunchWindow::draw() { void ImGuiPreLaunchWindow::drawMainMenu() { const auto& windowSize = ImGui::GetWindowSize(); - static const char* popopModalLabel = "Skip Pre-Launch UI?"; ImGui::SetCursorPosY(windowSize.y - 200); ImGui::PushFont(ImGuiEngine::fontLarge); - bool disabled = false; if (m_selectedIsoPath.empty() || !SDL_GetPathInfo(m_selectedIsoPath.c_str(), nullptr)) { - ImGui::BeginDisabled(); - disabled = true; - } - - if (ImGuiButtonCenter("Start Game")) { - if (!getSettings().backend.skipPreLaunchUI) { - ImGui::OpenPopup(popopModalLabel); - }else { + if (ImGuiButtonCenter("Select disc image...")) { + SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(), + skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()), + nullptr, false); + } + } else { + if (ImGuiButtonCenter("Start game")) { dusk::IsGameLaunched = true; } } - if (disabled) { - ImGui::EndDisabled(); - } - if (ImGuiButtonCenter("Options")) { m_CurMenu = 1; } ImGui::PopFont(); - - if (ImGui::BeginPopupModal(popopModalLabel, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Would you like to skip this menu on future launches?"); - ImGui::Separator(); - - if (ImGui::Button("Yes", ImVec2(120, 0))) { - getSettings().backend.skipPreLaunchUI.setValue(true); - config::Save(); - ImGui::CloseCurrentPopup(); - dusk::IsGameLaunched = true; - } - ImGui::SameLine(); - if (ImGui::Button("No", ImVec2(120, 0))) { - ImGui::CloseCurrentPopup(); - dusk::IsGameLaunched = true; - } - ImGui::EndPopup(); - } } void ImGuiPreLaunchWindow::drawOptions() { @@ -160,12 +133,48 @@ void ImGuiPreLaunchWindow::drawOptions() { float childWidth = windowSize.x - 400; ImGui::SetCursorPosX(windowSize.x / 2 - (childWidth / 2)); - if (ImGui::BeginChild("OptionsChild", ImVec2(childWidth, endCursorY - cursorY), ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground)) { + if (ImGui::BeginChild("OptionsChild", ImVec2(childWidth, endCursorY - cursorY), + ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground)) + { ImGui::InputText("Game ISO Path", &m_selectedIsoPath, ImGuiInputTextFlags_ReadOnly); ImGui::SameLine(); if (ImGui::Button("Set")) { - SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(), skGameDiscFileFilters.data(), - int(skGameDiscFileFilters.size()), nullptr, false); + SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(), + skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()), + nullptr, false); + } + + AuroraBackend configuredBackend = BACKEND_AUTO; + const std::string& configuredBackendId = getSettings().backend.graphicsBackend; + if (!try_parse_backend(configuredBackendId, configuredBackend)) { + configuredBackend = BACKEND_AUTO; + } + + if (ImGui::BeginCombo("Graphics Backend", backend_name(configuredBackend).data())) { + if (ImGui::Selectable("Auto", configuredBackend == BACKEND_AUTO)) { + getSettings().backend.graphicsBackend.setValue("auto"); + config::Save(); + } + + size_t backendCount = 0; + const AuroraBackend* availableBackends = aurora_get_available_backends(&backendCount); + for (size_t i = 0; i < backendCount; ++i) { + const AuroraBackend backend = availableBackends[i]; + const bool isSelected = configuredBackend == backend; + if (ImGui::Selectable(backend_name(backend).data(), isSelected)) { + getSettings().backend.graphicsBackend.setValue( + std::string(backend_id(backend))); + config::Save(); + } + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + + ImGui::EndCombo(); + } + if (configuredBackendId != m_initialGraphicsBackend) { + ImGui::TextDisabled("Restart Required"); } ImGui::EndChild(); } @@ -182,4 +191,4 @@ void ImGuiPreLaunchWindow::drawOptions() { ImGui::PopFont(); } -} +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiPreLaunchWindow.hpp b/src/dusk/imgui/ImGuiPreLaunchWindow.hpp index d70bcda342..5d16a6ba0b 100644 --- a/src/dusk/imgui/ImGuiPreLaunchWindow.hpp +++ b/src/dusk/imgui/ImGuiPreLaunchWindow.hpp @@ -5,6 +5,7 @@ class ImGuiPreLaunchWindow { private: int m_CurMenu = 0; bool m_IsFirstDraw = true; + std::string m_initialGraphicsBackend; bool isSelectedPathValid() const; @@ -18,4 +19,4 @@ public: std::string m_selectedIsoPath; std::string m_errorString; }; -} // namespace dusk \ No newline at end of file +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index 9748893571..e461545e33 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -287,7 +287,7 @@ namespace dusk { ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize; - ImGui::SetNextWindowBgAlpha(0.65f); + // ImGui::SetNextWindowBgAlpha(0.65f); if (ImGui::Begin("Save Editor", &open, windowFlags)) { if (ImGui::BeginTabBar("SaveEditorTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index bf492ed3dc..cde814a867 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -61,6 +61,7 @@ UserSettings g_userSettings = { .backend = { .isoPath {"backend.isoPath", ""}, + .graphicsBackend {"backend.graphicsBackend", "auto"}, .skipPreLaunchUI {"backend.skipPreLaunchUI", false} } }; @@ -110,6 +111,7 @@ void registerSettings() { Register(g_userSettings.game.fastSpinner); Register(g_userSettings.backend.isoPath); + Register(g_userSettings.backend.graphicsBackend); Register(g_userSettings.backend.skipPreLaunchUI); } diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index c8addc1140..41307b6fd3 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -61,6 +61,7 @@ #include "SDL3/SDL_filesystem.h" #include "cxxopts.hpp" +#include "dusk/audio/DuskAudioSystem.h" #include "dusk/config.hpp" #include "dusk/imgui/ImGuiConsole.hpp" #include "tracy/Tracy.hpp" @@ -83,6 +84,7 @@ const int audioHeapSize = 0x14D800; #define COPYDATE_PATH "/str/Final/Release/COPYDATE" #if TARGET_PC +bool dusk::IsRunning = true; bool dusk::IsShuttingDown = false; bool dusk::IsGameLaunched = false; #endif @@ -122,7 +124,7 @@ const char* configPath; AuroraWindowSize preLaunchUIWindowSize; bool launchUILoop() { - while (!dusk::IsGameLaunched) { + while (dusk::IsRunning && !dusk::IsGameLaunched) { const AuroraEvent* event = aurora_update(); while (event != nullptr && event->type != AURORA_NONE) { switch (event->type) { @@ -151,9 +153,7 @@ bool launchUILoop() { aurora_end_frame(); } - DuskLog.info("Game Launched!"); - - return true; + return dusk::IsRunning; } void main01(void) { @@ -244,42 +244,52 @@ void main01(void) { aurora_end_frame(); FrameMark; - } while (true); + } while (dusk::IsRunning); exit:; } -static AuroraBackend ParseAuroraBackend(const std::string& value) { - if (value == "auto") { - return BACKEND_AUTO; - } - if (value == "d3d11") { - return BACKEND_D3D11; - } - if (value == "d3d12") { - return BACKEND_D3D12; - } - if (value == "metal") { - return BACKEND_METAL; - } - if (value == "vulkan") { - return BACKEND_VULKAN; - } - if (value == "opengl") { - return BACKEND_OPENGL; - } - if (value == "opengles") { - return BACKEND_OPENGLES; - } - if (value == "webgpu") { - return BACKEND_WEBGPU; - } - if (value == "null") { - return BACKEND_NULL; +static bool IsBackendAvailable(AuroraBackend backend) { + if (backend == BACKEND_AUTO) { + return true; } - fmt::print(stderr, "Unknown backend: {}", value); - exit(1); + size_t availableBackendCount = 0; + const AuroraBackend* availableBackends = aurora_get_available_backends(&availableBackendCount); + for (size_t i = 0; i < availableBackendCount; ++i) { + if (availableBackends[i] == backend) { + return true; + } + } + + return false; +} + +static AuroraBackend ResolveDesiredBackend(const cxxopts::ParseResult& parsedArgOptions) { + AuroraBackend desiredBackend = BACKEND_AUTO; + + if (parsedArgOptions.count("backend") != 0) { + const std::string backendArg = parsedArgOptions["backend"].as(); + if (!dusk::try_parse_backend(backendArg, desiredBackend)) { + fmt::print(stderr, "Unknown backend: {}\n", backendArg); + exit(1); + } + } else if (!dusk::try_parse_backend( + static_cast(dusk::getSettings().backend.graphicsBackend), + desiredBackend)) + { + DuskLog.warn("Unknown configured backend '{}', falling back to Auto", + static_cast(dusk::getSettings().backend.graphicsBackend)); + desiredBackend = BACKEND_AUTO; + } + + if (!IsBackendAvailable(desiredBackend)) { + DuskLog.warn("Requested backend '{}' is unavailable, falling back to Auto", + dusk::backend_name(desiredBackend)); + desiredBackend = BACKEND_AUTO; + } + + return desiredBackend; } static void aurora_imgui_init_callback(const AuroraWindowSize* size) { @@ -372,7 +382,7 @@ int game_main(int argc, char* argv[]) { ("l,log-level", "Log level from " + std::to_string(AuroraLogLevel::LOG_DEBUG) + " to " + std::to_string(AuroraLogLevel::LOG_FATAL), cxxopts::value()->default_value("0")) ("h,help", "Print usage") ("dvd", "Path to DVD image file", cxxopts::value()->default_value("game.iso")) - ("backend", "Graphics API backend to use (auto, d3d11, d3d12, metal, vulkan, opengl, opengles, webgpu, null)", cxxopts::value()->default_value("auto")) + ("backend", "Graphics API backend to use (auto, d3d12, metal, vulkan, null)", cxxopts::value()) ("cvar", "Override configuration variables without modifying config", cxxopts::value>()); arg_options.parse_positional({"dvd"}); @@ -406,7 +416,7 @@ int game_main(int argc, char* argv[]) { config.windowPosY = -1; config.windowWidth = defaultWindowWidth * 2; config.windowHeight = defaultWindowHeight * 2; - config.desiredBackend = ParseAuroraBackend(parsed_arg_options["backend"].as()); + config.desiredBackend = ResolveDesiredBackend(parsed_arg_options); config.logCallback = &aurora_log_callback; config.logLevel = (AuroraLogLevel)parsed_arg_options["log-level"].as(); config.mem1Size = 256 * 1024 * 1024; @@ -420,7 +430,16 @@ int game_main(int argc, char* argv[]) { VISetWindowTitle( fmt::format("Dusk {} [{}]", DUSK_WC_DESCRIBE, dusk::backend_name(auroraInfo.backend)) - .c_str()); + .c_str()); + + if (dusk::getSettings().video.lockAspectRatio) { + VILockAspectRatio(defaultAspectRatioW, defaultAspectRatioH); + } else { + VIUnlockAspectRatio(); + } + + dusk::audio::SetMasterVolume(dusk::getSettings().audio.masterVolume / 100.0f); + dusk::audio::SetEnableReverb(dusk::getSettings().audio.enableReverb); // pre game launch ui main loop if (!launchUILoop()) { @@ -431,7 +450,7 @@ int game_main(int argc, char* argv[]) { std::string dvd_path; if (parsed_arg_options.count("dvd")) { dvd_path = parsed_arg_options["dvd"].as(); - }else { + } else { dvd_path = dusk::getSettings().backend.isoPath; if (dvd_path.empty()) {