diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 6bda5c8..92c7ade 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -150,13 +150,14 @@ set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES set(UNLEASHED_RECOMP_UI_CXX_SOURCES "ui/achievement_menu.cpp" "ui/achievement_overlay.cpp" - "ui/installer_wizard.cpp" "ui/button_guide.cpp" "ui/fader.cpp" + "ui/game_window.cpp" + "ui/imgui_utils.cpp" + "ui/installer_wizard.cpp" "ui/message_window.cpp" - "ui/options_menu_thumbnails.cpp" "ui/options_menu.cpp" - "ui/game_window.cpp" + "ui/options_menu_thumbnails.cpp" ) set(UNLEASHED_RECOMP_INSTALL_CXX_SOURCES @@ -464,6 +465,7 @@ BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/co BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/kbm.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/kbm.dds" ARRAY_NAME "g_kbm" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fade.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fade.dds" ARRAY_NAME "g_select_fade" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/select_fill.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/select_fill.dds" ARRAY_NAME "g_select_fill" COMPRESSION_TYPE "zstd") +BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/common/light.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/common/light.dds" ARRAY_NAME "g_light" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/arrow_circle.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/arrow_circle.dds" ARRAY_NAME "g_arrow_circle" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_001.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_001.dds" ARRAY_NAME "g_install_001" COMPRESSION_TYPE "zstd") BIN2C(TARGET_OBJ UnleashedRecomp SOURCE_FILE "${RESOURCES_SOURCE_PATH}/images/installer/install_002.dds" DEST_FILE "${RESOURCES_OUTPUT_PATH}/images/installer/install_002.dds" ARRAY_NAME "g_install_002" COMPRESSION_TYPE "zstd") diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 050bf9d..682b991 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1223,6 +1224,7 @@ static void CreateImGuiBackend() ImFontAtlasSnapshot::GenerateGlyphRanges(); #endif + InitImGuiUtils(); AchievementMenu::Init(); AchievementOverlay::Init(); ButtonGuide::Init(); diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index df3f986..122be3c 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -1,20 +1,20 @@ #include "achievement_menu.h" -#include "imgui_utils.h" #include +#include #include +#include #include #include +#include #include +#include #include #include #include #include #include + #include -#include -#include -#include -#include constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0; constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15; @@ -44,8 +44,6 @@ static ImFont* g_fntNewRodinDB; static ImFont* g_fntNewRodinUB; static std::unique_ptr g_upTrophyIcon; -static std::unique_ptr g_upSelectionCursor; -static std::unique_ptr g_upWindow; static int g_firstVisibleRowIndex; static int g_selectedRowIndex; @@ -71,43 +69,11 @@ static void DrawContainer(ImVec2 min, ImVec2 max, ImU32 gradientTop, ImU32 gradi { auto drawList = ImGui::GetForegroundDrawList(); - DrawPauseContainer(g_upWindow.get(), min, max, alpha); + DrawPauseContainer(min, max, alpha); drawList->PushClipRect({ min.x, min.y + Scale(20) }, { max.x, max.y - Scale(5) }); } -static void DrawSelectionContainer(ImVec2 min, ImVec2 max) -{ - auto drawList = ImGui::GetForegroundDrawList(); - - static auto breatheStart = ImGui::GetTime(); - auto alpha = BREATHE_MOTION(1.0f, 0.65f, breatheStart, 0.92f); - auto colour = IM_COL32(255, 255, 255, 255 * alpha); - - auto commonWidth = Scale(11); - auto commonHeight = Scale(24); - - auto tl = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 24); - auto tc = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 24); - auto tr = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 24); - auto cl = PIXELS_TO_UV_COORDS(64, 64, 0, 24, 11, 2); - auto cc = PIXELS_TO_UV_COORDS(64, 64, 11, 24, 8, 2); - auto cr = PIXELS_TO_UV_COORDS(64, 64, 19, 24, 11, 2); - auto bl = PIXELS_TO_UV_COORDS(64, 64, 0, 26, 11, 24); - auto bc = PIXELS_TO_UV_COORDS(64, 64, 11, 26, 8, 24); - auto br = PIXELS_TO_UV_COORDS(64, 64, 19, 26, 11, 24); - - drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); - drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour); - drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour); - drawList->AddImage(g_upSelectionCursor.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour); - drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour); - drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour); - drawList->AddImage(g_upSelectionCursor.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y }, GET_UV_COORDS(bl), colour); - drawList->AddImage(g_upSelectionCursor.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y }, GET_UV_COORDS(bc), colour); - drawList->AddImage(g_upSelectionCursor.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y }, GET_UV_COORDS(br), colour); -} - static void DrawHeaderContainer(const char* text) { auto drawList = ImGui::GetForegroundDrawList(); @@ -137,7 +103,7 @@ static void DrawHeaderContainer(const char* text) ImVec2 min = { g_aspectRatioOffsetX + Scale(containerMarginX), g_aspectRatioOffsetY + Scale(136) }; ImVec2 max = { min.x + textMarginX * 2 + textSize.x + Scale(5), g_aspectRatioOffsetY + Scale(196) }; - DrawPauseHeaderContainer(g_upWindow.get(), min, max, alpha); + DrawPauseHeaderContainer(min, max, alpha); SetTextSkew((min.y + max.y) / 2.0f, Scale(3.0f)); @@ -780,8 +746,6 @@ void AchievementMenu::Init() g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf"); g_upTrophyIcon = LOAD_ZSTD_TEXTURE(g_trophy); - g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fill); - g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window); } void AchievementMenu::Draw() diff --git a/UnleashedRecomp/ui/achievement_overlay.cpp b/UnleashedRecomp/ui/achievement_overlay.cpp index 626533e..2679180 100644 --- a/UnleashedRecomp/ui/achievement_overlay.cpp +++ b/UnleashedRecomp/ui/achievement_overlay.cpp @@ -1,16 +1,15 @@ #include "achievement_overlay.h" -#include "imgui_utils.h" +#include #include #include #include #include -#include +#include #include +#include #include #include #include -#include -#include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; @@ -29,8 +28,6 @@ static Achievement g_achievement; static ImFont* g_fntSeurat; -static std::unique_ptr g_upWindow; - static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25) { auto drawList = ImGui::GetForegroundDrawList(); @@ -65,7 +62,7 @@ static bool DrawContainer(ImVec2 min, ImVec2 max, float cornerRadius = 25) ? Hermite(1, 0, colourMotion) : Hermite(0, 1, colourMotion); - DrawPauseContainer(g_upWindow.get(), min, max, alpha); + DrawPauseContainer(min, max, alpha); if (containerMotion >= 1.0f) { @@ -81,8 +78,6 @@ void AchievementOverlay::Init() auto& io = ImGui::GetIO(); g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); - - g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window); } void AchievementOverlay::Draw() diff --git a/UnleashedRecomp/ui/button_guide.cpp b/UnleashedRecomp/ui/button_guide.cpp index 3318cdd..252fb08 100644 --- a/UnleashedRecomp/ui/button_guide.cpp +++ b/UnleashedRecomp/ui/button_guide.cpp @@ -3,9 +3,11 @@ #include #include #include +#include #include #include #include + #include #include diff --git a/UnleashedRecomp/ui/imgui_utils.cpp b/UnleashedRecomp/ui/imgui_utils.cpp new file mode 100644 index 0000000..be38d09 --- /dev/null +++ b/UnleashedRecomp/ui/imgui_utils.cpp @@ -0,0 +1,543 @@ +#include "imgui_utils.h" +#include +#include +#include +#include + +#include +#include +#include +#include + +std::unique_ptr g_texGeneralWindow; +std::unique_ptr g_texLight; +std::unique_ptr g_texSelectFade; +std::unique_ptr g_texSelectFill; + +void InitImGuiUtils() +{ + g_texGeneralWindow = LOAD_ZSTD_TEXTURE(g_general_window); + g_texLight = LOAD_ZSTD_TEXTURE(g_light); + g_texSelectFade = LOAD_ZSTD_TEXTURE(g_select_fade); + g_texSelectFill = LOAD_ZSTD_TEXTURE(g_select_fill); +} + +void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); + callbackData->setGradient.boundsMin[0] = min.x; + callbackData->setGradient.boundsMin[1] = min.y; + callbackData->setGradient.boundsMax[0] = max.x; + callbackData->setGradient.boundsMax[1] = max.y; + callbackData->setGradient.gradientTop = top; + callbackData->setGradient.gradientBottom = bottom; +} + +void ResetGradient() +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); + memset(&callbackData->setGradient, 0, sizeof(callbackData->setGradient)); +} + +void SetShaderModifier(uint32_t shaderModifier) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier); + callbackData->setShaderModifier.shaderModifier = shaderModifier; +} + +void SetOrigin(ImVec2 origin) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetOrigin); + callbackData->setOrigin.origin[0] = origin.x; + callbackData->setOrigin.origin[1] = origin.y; +} + +void SetScale(ImVec2 scale) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetScale); + callbackData->setScale.scale[0] = scale.x; + callbackData->setScale.scale[1] = scale.y; +} + +void SetTextSkew(float yCenter, float skewScale) +{ + SetShaderModifier(IMGUI_SHADER_MODIFIER_TEXT_SKEW); + SetOrigin({ 0.0f, yCenter }); + SetScale({ skewScale, 1.0f }); +} + +void ResetTextSkew() +{ + SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); + SetOrigin({ 0.0f, 0.0f }); + SetScale({ 1.0f, 1.0f }); +} + +void SetMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade); + callbackData->setMarqueeFade.boundsMin[0] = min.x; + callbackData->setMarqueeFade.boundsMin[1] = min.y; + callbackData->setMarqueeFade.boundsMax[0] = max.x; + callbackData->setMarqueeFade.boundsMax[1] = max.y; + + SetShaderModifier(IMGUI_SHADER_MODIFIER_MARQUEE_FADE); + SetScale({ fadeScale, 1.0f }); +} + +void ResetMarqueeFade() +{ + ResetGradient(); + SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); + SetScale({ 1.0f, 1.0f }); +} + +void SetOutline(float outline) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetOutline); + callbackData->setOutline.outline = outline; +} + +void ResetOutline() +{ + SetOutline(0.0f); +} + +void SetProceduralOrigin(ImVec2 proceduralOrigin) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetProceduralOrigin); + callbackData->setProceduralOrigin.proceduralOrigin[0] = proceduralOrigin.x; + callbackData->setProceduralOrigin.proceduralOrigin[1] = proceduralOrigin.y; +} + +void ResetProceduralOrigin() +{ + SetProceduralOrigin({ 0.0f, 0.0f }); +} + +void SetAdditive(bool enabled) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetAdditive); + callbackData->setAdditive.enabled = enabled; +} + +void ResetAdditive() +{ + SetAdditive(false); +} + +float Scale(float size) +{ + return size * g_aspectRatioScale; +} + +double ComputeLinearMotion(double duration, double offset, double total) +{ + return std::clamp((ImGui::GetTime() - duration - offset / 60.0) / total * 60.0, 0.0, 1.0); +} + +double ComputeMotion(double duration, double offset, double total) +{ + return sqrt(ComputeLinearMotion(duration, offset, total)); +} + +void DrawPauseContainer(ImVec2 min, ImVec2 max, float alpha) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + auto commonWidth = Scale(35); + auto commonHeight = Scale(35); + auto bottomHeight = Scale(5); + + auto tl = PIXELS_TO_UV_COORDS(128, 512, 0, 0, 35, 35); + auto tc = PIXELS_TO_UV_COORDS(128, 512, 51, 0, 5, 35); + auto tr = PIXELS_TO_UV_COORDS(128, 512, 70, 0, 35, 35); + auto cl = PIXELS_TO_UV_COORDS(128, 512, 0, 35, 35, 235); + auto cc = PIXELS_TO_UV_COORDS(128, 512, 51, 35, 5, 235); + auto cr = PIXELS_TO_UV_COORDS(128, 512, 70, 35, 35, 235); + auto bl = PIXELS_TO_UV_COORDS(128, 512, 0, 270, 35, 40); + auto bc = PIXELS_TO_UV_COORDS(128, 512, 51, 270, 5, 40); + auto br = PIXELS_TO_UV_COORDS(128, 512, 70, 270, 35, 40); + + auto colour = IM_COL32(255, 255, 255, 255 * alpha); + + drawList->AddImage(g_texGeneralWindow.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); + drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour); + drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour); + drawList->AddImage(g_texGeneralWindow.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour); + drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour); + drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour); + drawList->AddImage(g_texGeneralWindow.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bl), colour); + drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bc), colour); + drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y + bottomHeight }, GET_UV_COORDS(br), colour); +} + +void DrawPauseHeaderContainer(ImVec2 min, ImVec2 max, float alpha) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + auto commonWidth = Scale(35); + + auto left = PIXELS_TO_UV_COORDS(128, 512, 0, 314, 35, 60); + auto centre = PIXELS_TO_UV_COORDS(128, 512, 51, 314, 5, 60); + auto right = PIXELS_TO_UV_COORDS(128, 512, 70, 314, 35, 60); + + auto colour = IM_COL32(255, 255, 255, 255 * alpha); + + drawList->AddImage(g_texGeneralWindow.get(), min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour); + drawList->AddImage(g_texGeneralWindow.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour); + drawList->AddImage(g_texGeneralWindow.get(), { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour); +} + +void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed) +{ + auto drawList = ImGui::GetForegroundDrawList(); + auto rectWidth = max.x - min.x; + auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text); + auto textX = pos.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth); + + drawList->PushClipRect(min, max, true); + + if (textX <= pos.x) + drawList->AddText(font, fontSize, { textX, pos.y }, color, text); + + if (textX + textSize.x < pos.x) + drawList->AddText(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, color, text); + + drawList->PopClipRect(); +} + +void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + SetOutline(outlineSize); + drawList->AddText(font, fontSize, pos, outlineColor, text); + ResetOutline(); + + if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE) + SetShaderModifier(shaderModifier); + + drawList->AddText(font, fontSize, pos, color, text); + + if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE) + SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); +} + +void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset, float radius, ImU32 shadowColour) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + offset = Scale(offset); + + SetOutline(radius); + drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text); + ResetOutline(); + + drawList->AddText(font, fontSize, pos, colour, text, nullptr); +} + +float CalcWidestTextSize(const ImFont* font, float fontSize, std::span strs) +{ + auto result = 0.0f; + + for (auto& str : strs) + result = std::max(result, font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()).x); + + return result; +} + +std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis, bool usePrefixEllipsis) +{ + const std::string ellipsis = "..."; + + if (input.length() > maxLength) + { + if (useEllipsis && maxLength > ellipsis.length()) + { + if (usePrefixEllipsis) + { + return ellipsis + input.substr(0, maxLength - ellipsis.length()); + } + else + { + return input.substr(0, maxLength - ellipsis.length()) + ellipsis; + } + } + else + { + return input.substr(0, maxLength); + } + } + + return input; +} + +std::vector Split(const char* strStart, const ImFont *font, float fontSize, float maxWidth) +{ + if (!strStart) + return {}; + + std::vector result; + float textWidth = 0.0f; + float lineWidth = 0.0f; + const float scale = fontSize / font->FontSize; + const char *str = strStart; + const char *strEnd = strStart + strlen(strStart); + const char *lineStart = strStart; + const bool wordWrapEnabled = (maxWidth > 0.0f); + const char *wordWrapEOL = nullptr; + while (*str != 0) + { + if (wordWrapEnabled) + { + if (wordWrapEOL == nullptr) + { + wordWrapEOL = font->CalcWordWrapPositionA(scale, str, strEnd, maxWidth - lineWidth); + } + + if (str >= wordWrapEOL) + { + if (textWidth < lineWidth) + textWidth = lineWidth; + + result.emplace_back(lineStart, str); + lineWidth = 0.0f; + wordWrapEOL = nullptr; + + while (str < strEnd && ImCharIsBlankA(*str)) + str++; + + if (*str == '\n') + str++; + + lineStart = str; + continue; + } + } + + const char *prevStr = str; + unsigned int c = (unsigned int)*str; + if (c < 0x80) + str += 1; + else + str += ImTextCharFromUtf8(&c, str, strEnd); + + if (c < 32) + { + if (c == '\n') + { + result.emplace_back(lineStart, str - 1); + lineStart = str; + textWidth = ImMax(textWidth, lineWidth); + lineWidth = 0.0f; + continue; + } + + if (c == '\r') + { + lineStart = str; + continue; + } + } + + const float charWidth = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale; + if (lineWidth + charWidth >= maxWidth) + { + str = prevStr; + break; + } + + lineWidth += charWidth; + } + + if (str != lineStart) + { + result.emplace_back(lineStart, str); + } + + return result; +} + +ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, std::vector lines) +{ + auto x = 0.0f; + auto y = 0.0f; + + for (auto& str : lines) + { + auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()); + + x = std::max(x, textSize.x); + y += textSize.y + Scale(lineMargin); + } + + return { x, y }; +} + +ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text) +{ + return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, font, fontSize, maxWidth)); +} + +void DrawCentredParagraph(const ImFont* font, float fontSize, float maxWidth, const ImVec2& centre, float lineMargin, const char* text, std::function drawMethod) +{ + auto lines = Split(text, font, fontSize, maxWidth); + auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines); + auto offsetY = 0.0f; + + for (int i = 0; i < lines.size(); i++) + { + auto& str = lines[i]; + auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()); + + auto textX = str.starts_with("- ") + ? centre.x - paragraphSize.x / 2 + : centre.x - textSize.x / 2; + + auto textY = centre.y - paragraphSize.y / 2 + offsetY; + + drawMethod(str.c_str(), { textX, textY }); + + offsetY += textSize.y + Scale(lineMargin); + } +} + +void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 colour, const char* text, double time, double delay, double speed, float offset, float radius, ImU32 shadowColour) +{ + auto drawList = ImGui::GetForegroundDrawList(); + auto rectWidth = max.x - min.x; + auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text); + auto textX = pos.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth); + + drawList->PushClipRect(min, max, true); + + if (textX <= pos.x) + DrawTextWithShadow(font, fontSize, { textX, pos.y }, colour, text, offset, radius, shadowColour); + + if (textX + textSize.x < pos.x) + DrawTextWithShadow(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, colour, text, offset, radius, shadowColour); + + drawList->PopClipRect(); +} + +float Lerp(float a, float b, float t) +{ + return a + (b - a) * t; +} + +float Cubic(float a, float b, float t) +{ + return a + (b - a) * (t * t * t); +} + +float Hermite(float a, float b, float t) +{ + return a + (b - a) * (t * t * (3 - 2 * t)); +} + +ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t) +{ + return { a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t }; +} + +ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t) +{ + auto a = ImGui::ColorConvertU32ToFloat4(c0); + auto b = ImGui::ColorConvertU32ToFloat4(c1); + + ImVec4 result; + result.x = a.x + (b.x - a.x) * t; + result.y = a.y + (b.y - a.y) * t; + result.z = a.z + (b.z - a.z) * t; + result.w = a.w + (b.w - a.w) * t; + + return ImGui::ColorConvertFloat4ToU32(result); +} + +void DrawVersionString(const ImFont* font, const ImU32 col) +{ + auto drawList = ImGui::GetForegroundDrawList(); + auto& res = ImGui::GetIO().DisplaySize; + auto fontSize = Scale(12); + auto textMargin = Scale(2); + auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, g_versionString); + + drawList->AddText(font, fontSize, { res.x - textSize.x - textMargin, res.y - textSize.y - textMargin }, col, g_versionString); +} + +void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop) +{ + auto drawList = ImGui::GetForegroundDrawList(); + + static auto breatheStart = ImGui::GetTime(); + auto alpha = BREATHE_MOTION(1.0f, 0.55f, breatheStart, 0.92f); + auto colour = IM_COL32(255, 255, 255, 255 * alpha); + auto commonWidth = Scale(11); + auto commonHeight = Scale(24); + + if (fadeTop) + { + auto left = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 50); + auto centre = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 50); + auto right = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 50); + + drawList->AddImage(g_texSelectFade.get(), min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour); + drawList->AddImage(g_texSelectFade.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour); + drawList->AddImage(g_texSelectFade.get(), { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour); + + return; + } + + auto tl = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 24); + auto tc = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 24); + auto tr = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 24); + auto cl = PIXELS_TO_UV_COORDS(64, 64, 0, 24, 11, 2); + auto cc = PIXELS_TO_UV_COORDS(64, 64, 11, 24, 8, 2); + auto cr = PIXELS_TO_UV_COORDS(64, 64, 19, 24, 11, 2); + auto bl = PIXELS_TO_UV_COORDS(64, 64, 0, 26, 11, 24); + auto bc = PIXELS_TO_UV_COORDS(64, 64, 11, 26, 8, 24); + auto br = PIXELS_TO_UV_COORDS(64, 64, 19, 26, 11, 24); + + drawList->AddImage(g_texSelectFill.get(), min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); + drawList->AddImage(g_texSelectFill.get(), { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour); + drawList->AddImage(g_texSelectFill.get(), { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour); + drawList->AddImage(g_texSelectFill.get(), { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour); + drawList->AddImage(g_texSelectFill.get(), { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour); + drawList->AddImage(g_texSelectFill.get(), { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour); + drawList->AddImage(g_texSelectFill.get(), { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y }, GET_UV_COORDS(bl), colour); + drawList->AddImage(g_texSelectFill.get(), { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y }, GET_UV_COORDS(bc), colour); + drawList->AddImage(g_texSelectFill.get(), { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y }, GET_UV_COORDS(br), colour); +} + +void DrawToggleLight(ImVec2 pos, bool isEnabled) +{ + auto drawList = ImGui::GetForegroundDrawList(); + auto lightSize = Scale(14); + + ImVec2 min = { pos.x, pos.y }; + ImVec2 max = { min.x + lightSize, min.y + lightSize }; + + if (isEnabled) + { + auto lightGlowSize = Scale(24); + auto lightGlowUVs = PIXELS_TO_UV_COORDS(64, 64, 31, 31, 32, 32); + + ImVec2 lightGlowMin = { min.x - (lightGlowSize / 2) + Scale(2), min.y - lightGlowSize / 2 }; + ImVec2 lightGlowMax = { min.x + lightGlowSize, min.y + lightGlowSize }; + + SetAdditive(true); + drawList->AddImage(g_texLight.get(), lightGlowMin, lightGlowMax, GET_UV_COORDS(lightGlowUVs), IM_COL32(255, 255, 0, 127)); + SetAdditive(false); + + auto lightOnUVs = PIXELS_TO_UV_COORDS(64, 64, 14, 0, 14, 14); + + drawList->AddImage(g_texLight.get(), min, max, GET_UV_COORDS(lightOnUVs)); + } + else + { + auto lightOffUVs = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 14, 14); + + drawList->AddImage(g_texLight.get(), min, max, GET_UV_COORDS(lightOffUVs)); + } +} diff --git a/UnleashedRecomp/ui/imgui_utils.h b/UnleashedRecomp/ui/imgui_utils.h index 1c9a971..cb84497 100644 --- a/UnleashedRecomp/ui/imgui_utils.h +++ b/UnleashedRecomp/ui/imgui_utils.h @@ -2,9 +2,6 @@ #include #include -#include -#include -#include #define PIXELS_TO_UV_COORDS(textureWidth, textureHeight, x, y, width, height) \ std::make_tuple(ImVec2((float)x / (float)textureWidth, (float)y / (float)textureHeight), \ @@ -17,446 +14,48 @@ #define BREATHE_MOTION(start, end, time, rate) Lerp(start, end, (sin((ImGui::GetTime() - time) * (2.0f * M_PI / rate)) + 1.0f) / 2.0f) -inline void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); - callbackData->setGradient.boundsMin[0] = min.x; - callbackData->setGradient.boundsMin[1] = min.y; - callbackData->setGradient.boundsMax[0] = max.x; - callbackData->setGradient.boundsMax[1] = max.y; - callbackData->setGradient.gradientTop = top; - callbackData->setGradient.gradientBottom = bottom; -} - -inline void ResetGradient() -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetGradient); - memset(&callbackData->setGradient, 0, sizeof(callbackData->setGradient)); -} - -inline void SetShaderModifier(uint32_t shaderModifier) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier); - callbackData->setShaderModifier.shaderModifier = shaderModifier; -} - -inline void SetOrigin(ImVec2 origin) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetOrigin); - callbackData->setOrigin.origin[0] = origin.x; - callbackData->setOrigin.origin[1] = origin.y; -} - -inline void SetScale(ImVec2 scale) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetScale); - callbackData->setScale.scale[0] = scale.x; - callbackData->setScale.scale[1] = scale.y; -} - -inline void SetTextSkew(float yCenter, float skewScale) -{ - SetShaderModifier(IMGUI_SHADER_MODIFIER_TEXT_SKEW); - SetOrigin({ 0.0f, yCenter }); - SetScale({ skewScale, 1.0f }); -} - -inline void ResetTextSkew() -{ - SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); - SetOrigin({ 0.0f, 0.0f }); - SetScale({ 1.0f, 1.0f }); -} - -inline void SetMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetMarqueeFade); - callbackData->setMarqueeFade.boundsMin[0] = min.x; - callbackData->setMarqueeFade.boundsMin[1] = min.y; - callbackData->setMarqueeFade.boundsMax[0] = max.x; - callbackData->setMarqueeFade.boundsMax[1] = max.y; - - SetShaderModifier(IMGUI_SHADER_MODIFIER_MARQUEE_FADE); - SetScale({ fadeScale, 1.0f }); -} - -inline void ResetMarqueeFade() -{ - ResetGradient(); - SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); - SetScale({ 1.0f, 1.0f }); -} - -inline void SetOutline(float outline) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetOutline); - callbackData->setOutline.outline = outline; -} - -inline void ResetOutline() -{ - SetOutline(0.0f); -} - -inline void SetProceduralOrigin(ImVec2 proceduralOrigin) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetProceduralOrigin); - callbackData->setProceduralOrigin.proceduralOrigin[0] = proceduralOrigin.x; - callbackData->setProceduralOrigin.proceduralOrigin[1] = proceduralOrigin.y; -} - -inline void ResetProceduralOrigin() -{ - SetProceduralOrigin({ 0.0f, 0.0f }); -} - -inline void SetAdditive(bool enabled) -{ - auto callbackData = AddImGuiCallback(ImGuiCallback::SetAdditive); - callbackData->setAdditive.enabled = enabled; -} - -inline void ResetAdditive() -{ - SetAdditive(false); -} - -inline float Scale(float size) -{ - return size * g_aspectRatioScale; -} - -inline double ComputeLinearMotion(double duration, double offset, double total) -{ - return std::clamp((ImGui::GetTime() - duration - offset / 60.0) / total * 60.0, 0.0, 1.0); -} - -inline double ComputeMotion(double duration, double offset, double total) -{ - return sqrt(ComputeLinearMotion(duration, offset, total)); -} - -inline void DrawPauseContainer(GuestTexture* texture, ImVec2 min, ImVec2 max, float alpha = 1) -{ - auto drawList = ImGui::GetForegroundDrawList(); - - auto commonWidth = Scale(35); - auto commonHeight = Scale(35); - auto bottomHeight = Scale(5); - - auto tl = PIXELS_TO_UV_COORDS(128, 512, 0, 0, 35, 35); - auto tc = PIXELS_TO_UV_COORDS(128, 512, 51, 0, 5, 35); - auto tr = PIXELS_TO_UV_COORDS(128, 512, 70, 0, 35, 35); - auto cl = PIXELS_TO_UV_COORDS(128, 512, 0, 35, 35, 235); - auto cc = PIXELS_TO_UV_COORDS(128, 512, 51, 35, 5, 235); - auto cr = PIXELS_TO_UV_COORDS(128, 512, 70, 35, 35, 235); - auto bl = PIXELS_TO_UV_COORDS(128, 512, 0, 270, 35, 40); - auto bc = PIXELS_TO_UV_COORDS(128, 512, 51, 270, 5, 40); - auto br = PIXELS_TO_UV_COORDS(128, 512, 70, 270, 35, 40); - - auto colour = IM_COL32(255, 255, 255, 255 * alpha); - - drawList->AddImage(texture, min, { min.x + commonWidth, min.y + commonHeight }, GET_UV_COORDS(tl), colour); - drawList->AddImage(texture, { min.x + commonWidth, min.y }, { max.x - commonWidth, min.y + commonHeight }, GET_UV_COORDS(tc), colour); - drawList->AddImage(texture, { max.x - commonWidth, min.y }, { max.x, min.y + commonHeight }, GET_UV_COORDS(tr), colour); - drawList->AddImage(texture, { min.x, min.y + commonHeight }, { min.x + commonWidth, max.y - commonHeight }, GET_UV_COORDS(cl), colour); - drawList->AddImage(texture, { min.x + commonWidth, min.y + commonHeight }, { max.x - commonWidth, max.y - commonHeight }, GET_UV_COORDS(cc), colour); - drawList->AddImage(texture, { max.x - commonWidth, min.y + commonHeight }, { max.x, max.y - commonHeight }, GET_UV_COORDS(cr), colour); - drawList->AddImage(texture, { min.x, max.y - commonHeight }, { min.x + commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bl), colour); - drawList->AddImage(texture, { min.x + commonWidth, max.y - commonHeight }, { max.x - commonWidth, max.y + bottomHeight }, GET_UV_COORDS(bc), colour); - drawList->AddImage(texture, { max.x - commonWidth, max.y - commonHeight }, { max.x, max.y + bottomHeight }, GET_UV_COORDS(br), colour); -} - -inline void DrawPauseHeaderContainer(GuestTexture* texture, ImVec2 min, ImVec2 max, float alpha = 1) -{ - auto drawList = ImGui::GetForegroundDrawList(); - - auto commonWidth = Scale(35); - - auto left = PIXELS_TO_UV_COORDS(128, 512, 0, 314, 35, 60); - auto centre = PIXELS_TO_UV_COORDS(128, 512, 51, 314, 5, 60); - auto right = PIXELS_TO_UV_COORDS(128, 512, 70, 314, 35, 60); - - auto colour = IM_COL32(255, 255, 255, 255 * alpha); - - drawList->AddImage(texture, min, { min.x + commonWidth, max.y }, GET_UV_COORDS(left), colour); - drawList->AddImage(texture, { min.x + commonWidth, min.y }, { max.x - commonWidth, max.y }, GET_UV_COORDS(centre), colour); - drawList->AddImage(texture, { max.x - commonWidth, min.y }, max, GET_UV_COORDS(right), colour); -} - -inline void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed) -{ - auto drawList = ImGui::GetForegroundDrawList(); - auto rectWidth = max.x - min.x; - auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text); - auto textX = pos.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth); - - drawList->PushClipRect(min, max, true); - - if (textX <= pos.x) - drawList->AddText(font, fontSize, { textX, pos.y }, color, text); - - if (textX + textSize.x < pos.x) - drawList->AddText(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, color, text); - - drawList->PopClipRect(); -} - -inline void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier = IMGUI_SHADER_MODIFIER_NONE) -{ - auto drawList = ImGui::GetForegroundDrawList(); - - SetOutline(outlineSize); - drawList->AddText(font, fontSize, pos, outlineColor, text); - ResetOutline(); - - if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE) - SetShaderModifier(shaderModifier); - - drawList->AddText(font, fontSize, pos, color, text); - - if (shaderModifier != IMGUI_SHADER_MODIFIER_NONE) - SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); -} - -inline void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)) -{ - auto drawList = ImGui::GetForegroundDrawList(); - - offset = Scale(offset); - - SetOutline(radius); - drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text); - ResetOutline(); - - drawList->AddText(font, fontSize, pos, colour, text, nullptr); -} - -inline float CalcWidestTextSize(const ImFont* font, float fontSize, std::span strs) -{ - auto result = 0.0f; - - for (auto& str : strs) - result = std::max(result, font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()).x); - - return result; -} - -inline std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis = true, bool usePrefixEllipsis = false) -{ - const std::string ellipsis = "..."; - - if (input.length() > maxLength) - { - if (useEllipsis && maxLength > ellipsis.length()) - { - if (usePrefixEllipsis) - { - return ellipsis + input.substr(0, maxLength - ellipsis.length()); - } - else - { - return input.substr(0, maxLength - ellipsis.length()) + ellipsis; - } - } - else - { - return input.substr(0, maxLength); - } - } - - return input; -} - -inline std::vector Split(const char* strStart, const ImFont *font, float fontSize, float maxWidth) -{ - if (!strStart) - return {}; - - std::vector result; - float textWidth = 0.0f; - float lineWidth = 0.0f; - const float scale = fontSize / font->FontSize; - const char *str = strStart; - const char *strEnd = strStart + strlen(strStart); - const char *lineStart = strStart; - const bool wordWrapEnabled = (maxWidth > 0.0f); - const char *wordWrapEOL = nullptr; - while (*str != 0) - { - if (wordWrapEnabled) - { - if (wordWrapEOL == nullptr) - { - wordWrapEOL = font->CalcWordWrapPositionA(scale, str, strEnd, maxWidth - lineWidth); - } - - if (str >= wordWrapEOL) - { - if (textWidth < lineWidth) - textWidth = lineWidth; - - result.emplace_back(lineStart, str); - lineWidth = 0.0f; - wordWrapEOL = nullptr; - - while (str < strEnd && ImCharIsBlankA(*str)) - str++; - - if (*str == '\n') - str++; - - lineStart = str; - continue; - } - } - - const char *prevStr = str; - unsigned int c = (unsigned int)*str; - if (c < 0x80) - str += 1; - else - str += ImTextCharFromUtf8(&c, str, strEnd); - - if (c < 32) - { - if (c == '\n') - { - result.emplace_back(lineStart, str - 1); - lineStart = str; - textWidth = ImMax(textWidth, lineWidth); - lineWidth = 0.0f; - continue; - } - - if (c == '\r') - { - lineStart = str; - continue; - } - } - - const float charWidth = ((int)c < font->IndexAdvanceX.Size ? font->IndexAdvanceX.Data[c] : font->FallbackAdvanceX) * scale; - if (lineWidth + charWidth >= maxWidth) - { - str = prevStr; - break; - } - - lineWidth += charWidth; - } - - if (str != lineStart) - { - result.emplace_back(lineStart, str); - } - - return result; -} - -inline ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, std::vector lines) -{ - auto x = 0.0f; - auto y = 0.0f; - - for (auto& str : lines) - { - auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()); - - x = std::max(x, textSize.x); - y += textSize.y + Scale(lineMargin); - } - - return { x, y }; -} - -inline ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text) -{ - return MeasureCentredParagraph(font, fontSize, lineMargin, Split(text, font, fontSize, maxWidth)); -} - -inline void DrawCentredParagraph(const ImFont* font, float fontSize, float maxWidth, const ImVec2& centre, float lineMargin, const char* text, std::function drawMethod) -{ - auto lines = Split(text, font, fontSize, maxWidth); - auto paragraphSize = MeasureCentredParagraph(font, fontSize, lineMargin, lines); - auto offsetY = 0.0f; - - for (int i = 0; i < lines.size(); i++) - { - auto& str = lines[i]; - auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()); - - auto textX = str.starts_with("- ") - ? centre.x - paragraphSize.x / 2 - : centre.x - textSize.x / 2; - - auto textY = centre.y - paragraphSize.y / 2 + offsetY; - - drawMethod(str.c_str(), { textX, textY }); - - offsetY += textSize.y + Scale(lineMargin); - } -} - -inline void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 colour, const char* text, double time, double delay, double speed, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)) -{ - auto drawList = ImGui::GetForegroundDrawList(); - auto rectWidth = max.x - min.x; - auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, text); - auto textX = pos.x - fmodf(std::max(0.0, ImGui::GetTime() - (time + delay)) * speed, textSize.x + rectWidth); - - drawList->PushClipRect(min, max, true); - - if (textX <= pos.x) - DrawTextWithShadow(font, fontSize, { textX, pos.y }, colour, text, offset, radius, shadowColour); - - if (textX + textSize.x < pos.x) - DrawTextWithShadow(font, fontSize, { textX + textSize.x + rectWidth, pos.y }, colour, text, offset, radius, shadowColour); - - drawList->PopClipRect(); -} - -inline float Lerp(float a, float b, float t) -{ - return a + (b - a) * t; -} - -inline float Cubic(float a, float b, float t) -{ - return a + (b - a) * (t * t * t); -} - -inline float Hermite(float a, float b, float t) -{ - return a + (b - a) * (t * t * (3 - 2 * t)); -} - -inline ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t) -{ - return { a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t }; -} - -inline ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t) -{ - auto a = ImGui::ColorConvertU32ToFloat4(c0); - auto b = ImGui::ColorConvertU32ToFloat4(c1); - - ImVec4 result; - result.x = a.x + (b.x - a.x) * t; - result.y = a.y + (b.y - a.y) * t; - result.z = a.z + (b.z - a.z) * t; - result.w = a.w + (b.w - a.w) * t; - - return ImGui::ColorConvertFloat4ToU32(result); -} - -inline void DrawVersionString(const ImFont* font, const ImU32 col = IM_COL32(255, 255, 255, 70)) -{ - auto drawList = ImGui::GetForegroundDrawList(); - auto& res = ImGui::GetIO().DisplaySize; - auto fontSize = Scale(12); - auto textMargin = Scale(2); - auto textSize = font->CalcTextSizeA(fontSize, FLT_MAX, 0, g_versionString); - - drawList->AddText(font, fontSize, { res.x - textSize.x - textMargin, res.y - textSize.y - textMargin }, col, g_versionString); -} +extern std::unique_ptr g_texGeneralWindow; +extern std::unique_ptr g_texLight; +extern std::unique_ptr g_texSelectFade; +extern std::unique_ptr g_texSelectFill; + +void InitImGuiUtils(); + +void SetGradient(const ImVec2& min, const ImVec2& max, ImU32 top, ImU32 bottom); +void ResetGradient(); +void SetShaderModifier(uint32_t shaderModifier); +void SetOrigin(ImVec2 origin); +void SetScale(ImVec2 scale); +void SetTextSkew(float yCenter, float skewScale); +void ResetTextSkew(); +void SetMarqueeFade(ImVec2 min, ImVec2 max, float fadeScale); +void ResetMarqueeFade(); +void SetOutline(float outline); +void ResetOutline(); +void SetProceduralOrigin(ImVec2 proceduralOrigin); +void ResetProceduralOrigin(); +void SetAdditive(bool enabled); +void ResetAdditive(); +float Scale(float size); +double ComputeLinearMotion(double duration, double offset, double total); +double ComputeMotion(double duration, double offset, double total); +void DrawPauseContainer(ImVec2 min, ImVec2 max, float alpha = 1); +void DrawPauseHeaderContainer(ImVec2 min, ImVec2 max, float alpha = 1); +void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 color, const char* text, double time, double delay, double speed); +void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, float outlineSize, ImU32 outlineColor, uint32_t shaderModifier = IMGUI_SHADER_MODIFIER_NONE); +void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)); +float CalcWidestTextSize(const ImFont* font, float fontSize, std::span strs); +std::string Truncate(const std::string& input, size_t maxLength, bool useEllipsis = true, bool usePrefixEllipsis = false); +std::vector Split(const char* strStart, const ImFont* font, float fontSize, float maxWidth); +ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float lineMargin, std::vector lines); +ImVec2 MeasureCentredParagraph(const ImFont* font, float fontSize, float maxWidth, float lineMargin, const char* text); +void DrawCentredParagraph(const ImFont* font, float fontSize, float maxWidth, const ImVec2& centre, float lineMargin, const char* text, std::function drawMethod); +void DrawTextWithMarqueeShadow(const ImFont* font, float fontSize, const ImVec2& pos, const ImVec2& min, const ImVec2& max, ImU32 colour, const char* text, double time, double delay, double speed, float offset = 2.0f, float radius = 1.0f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)); +float Lerp(float a, float b, float t); +float Cubic(float a, float b, float t); +float Hermite(float a, float b, float t); +ImVec2 Lerp(const ImVec2& a, const ImVec2& b, float t); +ImU32 ColourLerp(ImU32 c0, ImU32 c1, float t); +void DrawVersionString(const ImFont* font, const ImU32 col = IM_COL32(255, 255, 255, 70)); +void DrawSelectionContainer(ImVec2 min, ImVec2 max, bool fadeTop = false); +void DrawToggleLight(ImVec2 pos, bool isEnabled); diff --git a/UnleashedRecomp/ui/installer_wizard.cpp b/UnleashedRecomp/ui/installer_wizard.cpp index 13b100c..cfee102 100644 --- a/UnleashedRecomp/ui/installer_wizard.cpp +++ b/UnleashedRecomp/ui/installer_wizard.cpp @@ -838,7 +838,7 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour DrawButtonContainer(min, max, baser, baseg, alpha); ImFont *font = sourceButton ? g_newRodinFont : g_dfsogeistdFont; - float size = Scale(sourceButton ? 15.0f : 20.0f); + float size = Scale(sourceButton ? 16.5f : 20.0f); float squashRatio; ImVec2 textSize = ComputeTextSize(font, buttonText, size, squashRatio, Scale(maxTextWidth)); min.x += ((max.x - min.x) - textSize.x) / 2.0f; @@ -911,7 +911,11 @@ static void DrawSourceButton(ButtonColumn buttonColumn, float yRatio, const char float minusY = (CONTAINER_BUTTON_GAP + BUTTON_HEIGHT) * yRatio; ImVec2 min = { minX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - BUTTON_HEIGHT - minusY) }; ImVec2 max = { maxX, g_aspectRatioOffsetY + Scale(CONTAINER_Y + CONTAINER_HEIGHT - CONTAINER_BUTTON_GAP - minusY) }; - DrawButton(min, max, sourceText, true, sourceSet, buttonPressed); + + auto lightSize = Scale(14); + + DrawButton(min, max, sourceText, true, sourceSet, buttonPressed, (max.x - min.x) - lightSize * 10); + DrawToggleLight({ min.x + lightSize, min.y + ((max.y - min.y) - lightSize) / 2 + Scale(1) }, sourceSet); } static void DrawProgressBar(float progressRatio) diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp index 9c7898d..fb48688 100644 --- a/UnleashedRecomp/ui/message_window.cpp +++ b/UnleashedRecomp/ui/message_window.cpp @@ -11,9 +11,6 @@ #include #include -#include -#include - constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; constexpr double OVERLAY_CONTAINER_INTRO_FADE_START = 5; @@ -40,9 +37,6 @@ static double g_controlsAppearTime; static ImFont* g_fntSeurat; -static std::unique_ptr g_upSelectionCursor; -static std::unique_ptr g_upWindow; - std::string g_text; int g_result; std::vector g_buttons; @@ -190,7 +184,7 @@ bool DrawContainer(float appearTime, ImVec2 centre, ImVec2 max, bool isForegroun if (isForeground) drawList->AddRectFilled({ 0.0f, 0.0f }, ImGui::GetIO().DisplaySize, IM_COL32(0, 0, 0, 190 * (g_foregroundCount ? 1 : alpha))); - DrawPauseContainer(g_upWindow.get(), _min, _max, alpha); + DrawPauseContainer(_min, _max, alpha); if (containerMotion >= 1.0f && !g_isClosing) { @@ -214,20 +208,7 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str bool isSelected = rowIndex == g_selectedRowIndex; if (isSelected) - { - static auto breatheStart = ImGui::GetTime(); - auto alpha = BREATHE_MOTION(1.0f, 0.55f, breatheStart, 0.92f); - auto colour = IM_COL32(255, 255, 255, 255 * alpha); - - auto width = Scale(11); - auto left = PIXELS_TO_UV_COORDS(64, 64, 0, 0, 11, 50); - auto centre = PIXELS_TO_UV_COORDS(64, 64, 11, 0, 8, 50); - auto right = PIXELS_TO_UV_COORDS(64, 64, 19, 0, 11, 50); - - drawList->AddImage(g_upSelectionCursor.get(), min, { min.x + width, max.y }, GET_UV_COORDS(left), colour); - drawList->AddImage(g_upSelectionCursor.get(), { min.x + width, min.y }, { max.x - width, max.y }, GET_UV_COORDS(centre), colour); - drawList->AddImage(g_upSelectionCursor.get(), { max.x - width, min.y }, max, GET_UV_COORDS(right), colour); - } + DrawSelectionContainer(min, max, true); auto fontSize = Scale(28); auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str()); @@ -277,9 +258,6 @@ void MessageWindow::Init() auto& io = ImGui::GetIO(); g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); - - g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fade); - g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window); } void MessageWindow::Draw() diff --git a/UnleashedRecomp/ui/options_menu.cpp b/UnleashedRecomp/ui/options_menu.cpp index 204a195..9afcb4d 100644 --- a/UnleashedRecomp/ui/options_menu.cpp +++ b/UnleashedRecomp/ui/options_menu.cpp @@ -1,10 +1,7 @@ #include "options_menu.h" #include "options_menu_thumbnails.h" -#include "imgui_utils.h" -#include "game_window.h" -#include "exports.h" -#include +#include #include #include #include @@ -13,11 +10,14 @@ #include #include #include +#include +#include #include +#include +#include #include #include - -#include +#include #include @@ -802,6 +802,9 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef* conf SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE); + if constexpr (std::is_same_v) + DrawToggleLight({ min.x + Scale(14), min.y + ((max.y - min.y) - Scale(14)) / 2 + Scale(1) }, config->Value); + // Selection triangles if (lockedOnOption) { diff --git a/UnleashedRecompResources b/UnleashedRecompResources index 84a56ea..0ab8dc8 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit 84a56eaf9a563147c91fb3a1a5dac682a84e8415 +Subproject commit 0ab8dc8976f04691242519e315e6d499d845df28