From cdcacff53bc135fca55210ab3a52f136314a3ca0 Mon Sep 17 00:00:00 2001 From: "Skyth (Asilkan)" <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Wed, 11 Dec 2024 23:30:19 +0300 Subject: [PATCH] Implement SDF fonts. (#24) * Initial SDF font generation work. * Text now correctly displaying with proper spacing. * Fix untextured draws, implement custom rectangles. * Fix regular image display. * Slightly refactor ImGui rendering. * Implement outlines. * Implement bevel. * Create host device after loading the module if the installer wasn't run. * Move ImGui files to its own folder. * Fix outline sizes. * Fix default ImGui font and font scales. * Update font atlas files. --- .gitmodules | 3 + UnleashedRecomp/CMakeLists.txt | 6 +- .../gpu/{ => imgui}/imgui_common.cpp | 0 .../gpu/{ => imgui}/imgui_common.h | 8 + .../gpu/imgui/imgui_font_builder.cpp | 274 ++++++++++++++++++ .../gpu/imgui/imgui_font_builder.h | 3 + .../gpu/{ => imgui}/imgui_snapshot.cpp | 21 +- .../gpu/{ => imgui}/imgui_snapshot.h | 3 +- UnleashedRecomp/gpu/shader/imgui_common.hlsli | 3 +- UnleashedRecomp/gpu/shader/imgui_ps.hlsl | 56 +++- UnleashedRecomp/gpu/video.cpp | 131 ++++++--- UnleashedRecomp/main.cpp | 10 +- UnleashedRecomp/ui/achievement_menu.cpp | 26 +- UnleashedRecomp/ui/achievement_overlay.cpp | 10 +- UnleashedRecomp/ui/button_guide.cpp | 10 +- UnleashedRecomp/ui/imgui_utils.h | 60 ++-- UnleashedRecomp/ui/installer_wizard.cpp | 15 +- UnleashedRecomp/ui/message_window.cpp | 6 +- UnleashedRecomp/ui/options_menu.cpp | 33 ++- UnleashedRecompResources | 2 +- thirdparty/CMakeLists.txt | 6 + thirdparty/msdf-atlas-gen | 1 + vcpkg.json | 3 +- 23 files changed, 536 insertions(+), 154 deletions(-) rename UnleashedRecomp/gpu/{ => imgui}/imgui_common.cpp (100%) rename UnleashedRecomp/gpu/{ => imgui}/imgui_common.h (86%) create mode 100644 UnleashedRecomp/gpu/imgui/imgui_font_builder.cpp create mode 100644 UnleashedRecomp/gpu/imgui/imgui_font_builder.h rename UnleashedRecomp/gpu/{ => imgui}/imgui_snapshot.cpp (92%) rename UnleashedRecomp/gpu/{ => imgui}/imgui_snapshot.h (93%) create mode 160000 thirdparty/msdf-atlas-gen diff --git a/.gitmodules b/.gitmodules index 2d13581c..1accc7a0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "UnleashedRecompResources"] path = UnleashedRecompResources url = https://github.com/hedge-dev/UnleashedRecompResources.git +[submodule "thirdparty/msdf-atlas-gen"] + path = thirdparty/msdf-atlas-gen + url = https://github.com/Chlumsky/msdf-atlas-gen.git diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 00455941..9881d463 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -87,8 +87,9 @@ set(SWA_CPU_CXX_SOURCES set(SWA_GPU_CXX_SOURCES "gpu/video.cpp" - "gpu/imgui_common.cpp" - "gpu/imgui_snapshot.cpp" + "gpu/imgui/imgui_common.cpp" + "gpu/imgui/imgui_font_builder.cpp" + "gpu/imgui/imgui_snapshot.cpp" "gpu/rhi/plume_d3d12.cpp" "gpu/rhi/plume_vulkan.cpp" ) @@ -258,6 +259,7 @@ target_link_libraries(UnleashedRecomp PRIVATE magic_enum::magic_enum unofficial::tiny-aes-c::tiny-aes-c nfd::nfd + msdf-atlas-gen::msdf-atlas-gen ) target_include_directories(UnleashedRecomp PRIVATE diff --git a/UnleashedRecomp/gpu/imgui_common.cpp b/UnleashedRecomp/gpu/imgui/imgui_common.cpp similarity index 100% rename from UnleashedRecomp/gpu/imgui_common.cpp rename to UnleashedRecomp/gpu/imgui/imgui_common.cpp diff --git a/UnleashedRecomp/gpu/imgui_common.h b/UnleashedRecomp/gpu/imgui/imgui_common.h similarity index 86% rename from UnleashedRecomp/gpu/imgui_common.h rename to UnleashedRecomp/gpu/imgui/imgui_common.h index 748d5423..91d5b4ee 100644 --- a/UnleashedRecomp/gpu/imgui_common.h +++ b/UnleashedRecomp/gpu/imgui/imgui_common.h @@ -7,6 +7,8 @@ #define IMGUI_SHADER_MODIFIER_TEXT_SKEW 4 #define IMGUI_SHADER_MODIFIER_MARQUEE_FADE 5 #define IMGUI_SHADER_MODIFIER_GRAYSCALE 6 +#define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 7 +#define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 8 #ifdef __cplusplus @@ -17,6 +19,7 @@ enum class ImGuiCallback : int32_t SetOrigin = -3, SetScale = -4, SetMarqueeFade = -5, + SetOutline = -6, // -8 is ImDrawCallback_ResetRenderState, don't use! }; @@ -50,6 +53,11 @@ union ImGuiCallbackData float boundsMin[2]; float boundsMax[2]; } setMarqueeFade; + + struct + { + float outline; + } setOutline; }; extern ImGuiCallbackData* AddImGuiCallback(ImGuiCallback callback); diff --git a/UnleashedRecomp/gpu/imgui/imgui_font_builder.cpp b/UnleashedRecomp/gpu/imgui/imgui_font_builder.cpp new file mode 100644 index 00000000..73710155 --- /dev/null +++ b/UnleashedRecomp/gpu/imgui/imgui_font_builder.cpp @@ -0,0 +1,274 @@ +#include "imgui_font_builder.h" + +#include + +// Taken directly from msdf-atlas-gen, modified to support custom rectangles. +struct TightAtlasPacker { + + TightAtlasPacker() : + width(-1), height(-1), + spacing(0), + dimensionsConstraint(msdf_atlas::DimensionsConstraint::POWER_OF_TWO_SQUARE), + scale(-1), + minScale(1), + unitRange(0), + pxRange(0), + miterLimit(0), + pxAlignOriginX(false), pxAlignOriginY(false), + scaleMaximizationTolerance(.001) + { + } + + int pack(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount) { + double initialScale = scale > 0 ? scale : minScale; + if (initialScale > 0) { + if (int remaining = tryPack(glyphs, count, customRects, customRectCount, dimensionsConstraint, width, height, initialScale)) + return remaining; + } + else if (width < 0 || height < 0) + return -1; + if (scale <= 0) + scale = packAndScale(glyphs, count, customRects, customRectCount); + if (scale <= 0) + return -1; + return 0; + } + + int width, height; + int spacing; + msdf_atlas::DimensionsConstraint dimensionsConstraint; + double scale; + double minScale; + msdfgen::Range unitRange; + msdfgen::Range pxRange; + double miterLimit; + bool pxAlignOriginX, pxAlignOriginY; + msdf_atlas::Padding innerUnitPadding, outerUnitPadding; + msdf_atlas::Padding innerPxPadding, outerPxPadding; + double scaleMaximizationTolerance; + + int tryPack(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount, msdf_atlas::DimensionsConstraint dimensionsConstraint, int& width, int& height, double scale) const { + // Wrap glyphs into boxes + std::vector rectangles; + std::vector rectangleGlyphs; + rectangles.reserve(count + customRectCount); + rectangleGlyphs.reserve(count); + msdf_atlas::GlyphGeometry::GlyphAttributes attribs = { }; + attribs.scale = scale; + attribs.range = { unitRange.lower + pxRange.lower / scale, unitRange.upper + pxRange.upper / scale }; + attribs.innerPadding = innerUnitPadding + innerPxPadding / scale; + attribs.outerPadding = outerUnitPadding + outerPxPadding / scale; + attribs.miterLimit = miterLimit; + attribs.pxAlignOriginX = pxAlignOriginX; + attribs.pxAlignOriginY = pxAlignOriginY; + for (msdf_atlas::GlyphGeometry* glyph = glyphs, *end = glyphs + count; glyph < end; ++glyph) { + if (!glyph->isWhitespace()) { + msdf_atlas::Rectangle rect = { }; + glyph->wrapBox(attribs); + glyph->getBoxSize(rect.w, rect.h); + if (rect.w > 0 && rect.h > 0) { + rectangles.push_back(rect); + rectangleGlyphs.push_back(glyph); + } + } + } + rectangles.insert(rectangles.end(), customRects, customRects + customRectCount); + // No non-zero size boxes? + if (rectangles.empty()) { + if (width < 0 || height < 0) + width = 0, height = 0; + return 0; + } + // Box rectangle packing + if (width < 0 || height < 0) { + std::pair dimensions = std::make_pair(width, height); + switch (dimensionsConstraint) { + case msdf_atlas::DimensionsConstraint::POWER_OF_TWO_SQUARE: + dimensions = msdf_atlas::packRectangles(rectangles.data(), rectangles.size(), spacing); + break; + case msdf_atlas::DimensionsConstraint::POWER_OF_TWO_RECTANGLE: + dimensions = msdf_atlas::packRectangles(rectangles.data(), rectangles.size(), spacing); + break; + case msdf_atlas::DimensionsConstraint::MULTIPLE_OF_FOUR_SQUARE: + dimensions = msdf_atlas::packRectangles >(rectangles.data(), rectangles.size(), spacing); + break; + case msdf_atlas::DimensionsConstraint::EVEN_SQUARE: + dimensions = msdf_atlas::packRectangles >(rectangles.data(), rectangles.size(), spacing); + break; + case msdf_atlas::DimensionsConstraint::SQUARE: + default: + dimensions = msdf_atlas::packRectangles >(rectangles.data(), rectangles.size(), spacing); + break; + } + if (!(dimensions.first > 0 && dimensions.second > 0)) + return -1; + width = dimensions.first, height = dimensions.second; + } + else { + if (int result = packRectangles(rectangles.data(), rectangles.size(), width, height, spacing)) + return result; + } + + // Set glyph box placement + for (size_t i = 0; i < rectangleGlyphs.size(); ++i) + rectangleGlyphs[i]->placeBox(rectangles[i].x, height - (rectangles[i].y + rectangles[i].h)); + + for (int i = 0; i < customRectCount; ++i) { + customRects[i].x = rectangles[rectangleGlyphs.size() + i].x; + customRects[i].y = height - (rectangles[rectangleGlyphs.size() + i].y + rectangles[rectangleGlyphs.size() + i].h); + } + + return 0; + } + + double packAndScale(msdf_atlas::GlyphGeometry* glyphs, int count, msdf_atlas::Rectangle* customRects, int customRectCount) const { + bool lastResult = false; + int w = width, h = height; +#define TRY_PACK(scale) (lastResult = !tryPack(glyphs, count, customRects, customRectCount, msdf_atlas::DimensionsConstraint(), w, h, (scale))) + double minScale = 1, maxScale = 1; + if (TRY_PACK(1)) { + while (maxScale < 1e+32 && ((maxScale = 2 * minScale), TRY_PACK(maxScale))) + minScale = maxScale; + } + else { + while (minScale > 1e-32 && ((minScale = .5 * maxScale), !TRY_PACK(minScale))) + maxScale = minScale; + } + if (minScale == maxScale) + return 0; + while (minScale / maxScale < 1 - scaleMaximizationTolerance) { + double midScale = .5 * (minScale + maxScale); + if (TRY_PACK(midScale)) + minScale = midScale; + else + maxScale = midScale; + } + if (!lastResult) + TRY_PACK(minScale); + return minScale; + } +}; + +extern void ImFontAtlasBuildInit(ImFontAtlas* atlas); +extern void ImFontAtlasBuildFinish(ImFontAtlas* atlas); + +static bool FontBuilder_Build(ImFontAtlas* atlas) +{ + ImFontAtlasBuildInit(atlas); + + auto freeType = msdfgen::initializeFreetype(); + + std::vector glyphs; + std::vector> ranges; + std::vector customRects; + + for (auto& config : atlas->ConfigData) + { + msdf_atlas::Charset charset; + const ImWchar* glyphRanges = config.GlyphRanges; + while (*glyphRanges != NULL) + { + for (ImWchar i = glyphRanges[0]; i <= glyphRanges[1]; i++) + charset.add(i); + + glyphRanges += 2; + } + + size_t index = glyphs.size(); + + auto font = msdfgen::loadFontData(freeType, reinterpret_cast(config.FontData), config.FontDataSize); + + msdf_atlas::FontGeometry fontGeometry(&glyphs); + fontGeometry.loadCharset(font, config.SizePixels, charset); + + auto& metrics = fontGeometry.getMetrics(); + config.DstFont->FontSize = config.SizePixels; + config.DstFont->ConfigData = &config; + config.DstFont->ConfigDataCount = 1; + config.DstFont->ContainerAtlas = atlas; + config.DstFont->Ascent = metrics.ascenderY; + config.DstFont->Descent = metrics.descenderY; + + msdfgen::destroyFont(font); + + ranges.emplace_back(index, glyphs.size() - index); + } + + for (auto& glyph : glyphs) + glyph.edgeColoring(&msdfgen::edgeColoringByDistance, 3.0, 0); + + for (auto& customRect : atlas->CustomRects) + customRects.emplace_back(0, 0, int(customRect.Width), int(customRect.Height)); + + TightAtlasPacker packer; + packer.spacing = 1; + packer.dimensionsConstraint = msdf_atlas::DimensionsConstraint::POWER_OF_TWO_RECTANGLE; + packer.miterLimit = 1.0; + packer.pxRange = 8.0; + packer.pack(glyphs.data(), glyphs.size(), customRects.data(), customRects.size()); + + for (size_t i = 0; i < customRects.size(); i++) + { + auto& srcRect = customRects[i]; + auto& dstRect = atlas->CustomRects[i]; + dstRect.X = srcRect.x; + dstRect.Y = srcRect.y; + } + + msdf_atlas::ImmediateAtlasGenerator> generator(packer.width, packer.height); + generator.generate(glyphs.data(), glyphs.size()); + + for (size_t i = 0; i < atlas->ConfigData.size(); i++) + { + auto& config = atlas->ConfigData[i]; + auto& [index, count] = ranges[i]; + for (size_t j = 0; j < count; j++) + { + auto& glyph = glyphs[index + j]; + double x0, y0, x1, y1, u0, v0, u1, v1; + glyph.getQuadPlaneBounds(x0, y0, x1, y1); + glyph.getQuadAtlasBounds(u0, v0, u1, v1); + config.DstFont->AddGlyph( + &config, + glyph.getCodepoint(), + x0, + -y1 + config.DstFont->Ascent, + x1, + -y0 + config.DstFont->Ascent, + u0 / packer.width, + v1 / packer.height, + u1 / packer.width, + v0 / packer.height, + glyph.getAdvance()); + } + + config.DstFont->BuildLookupTable(); + } + + atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(packer.width * packer.height * 4); + atlas->TexWidth = packer.width; + atlas->TexHeight = packer.height; + atlas->TexUvScale = { 1.0f / packer.width, 1.0f / packer.height }; + + auto bitmapRef = (msdfgen::BitmapConstRef)generator.atlasStorage(); + for (int y = 0; y < packer.height; y++) + { + for (int x = 0; x < packer.width; x++) + { + auto* srcPixels = bitmapRef(x, y); + auto* dstPixels = (uint8_t*)&atlas->TexPixelsRGBA32[y * packer.width + x]; + dstPixels[0] = srcPixels[0]; + dstPixels[1] = srcPixels[1]; + dstPixels[2] = srcPixels[2]; + dstPixels[3] = 0xFF; + } + } + + msdfgen::deinitializeFreetype(freeType); + + ImFontAtlasBuildFinish(atlas); + + return true; +} + +ImFontBuilderIO g_fontBuilderIO = { FontBuilder_Build }; diff --git a/UnleashedRecomp/gpu/imgui/imgui_font_builder.h b/UnleashedRecomp/gpu/imgui/imgui_font_builder.h new file mode 100644 index 00000000..e0def5d2 --- /dev/null +++ b/UnleashedRecomp/gpu/imgui/imgui_font_builder.h @@ -0,0 +1,3 @@ +#pragma once + +extern ImFontBuilderIO g_fontBuilderIO; diff --git a/UnleashedRecomp/gpu/imgui_snapshot.cpp b/UnleashedRecomp/gpu/imgui/imgui_snapshot.cpp similarity index 92% rename from UnleashedRecomp/gpu/imgui_snapshot.cpp rename to UnleashedRecomp/gpu/imgui/imgui_snapshot.cpp index 67149061..dbf87eee 100644 --- a/UnleashedRecomp/gpu/imgui_snapshot.cpp +++ b/UnleashedRecomp/gpu/imgui/imgui_snapshot.cpp @@ -222,17 +222,14 @@ void ImFontAtlasSnapshot::GenerateGlyphRanges() } } - if (App::s_isInit) + for (size_t i = XDBF_LANGUAGE_ENGLISH; i <= XDBF_LANGUAGE_ITALIAN; i++) { - for (size_t i = XDBF_LANGUAGE_ENGLISH; i <= XDBF_LANGUAGE_ITALIAN; i++) + auto achievements = g_xdbfWrapper.GetAchievements(static_cast(i)); + for (auto& achievement : achievements) { - auto achievements = g_xdbfWrapper.GetAchievements(static_cast(i)); - for (auto& achievement : achievements) - { - GetGlyphs(glyphs, achievement.Name); - GetGlyphs(glyphs, achievement.UnlockedDesc); - GetGlyphs(glyphs, achievement.LockedDesc); - } + GetGlyphs(glyphs, achievement.Name); + GetGlyphs(glyphs, achievement.UnlockedDesc); + GetGlyphs(glyphs, achievement.LockedDesc); } } @@ -252,12 +249,12 @@ void ImFontAtlasSnapshot::GenerateGlyphRanges() g_glyphRanges.push_back(0); } -ImFont* ImFontAtlasSnapshot::GetFont(const char* name, float size) +ImFont* ImFontAtlasSnapshot::GetFont(const char* name) { auto fontAtlas = ImGui::GetIO().Fonts; for (auto& configData : fontAtlas->ConfigData) { - if (strstr(configData.Name, name) != nullptr && abs(configData.SizePixels - size) < 0.001f) + if (strstr(configData.Name, name) != nullptr) { assert(configData.DstFont != nullptr); return configData.DstFont; @@ -268,5 +265,5 @@ ImFont* ImFontAtlasSnapshot::GetFont(const char* name, float size) assert(false && "Unable to locate equivalent font in the atlas file."); #endif - return fontAtlas->AddFontFromFileTTF(name, size, nullptr, g_glyphRanges.data()); + return fontAtlas->AddFontFromFileTTF(name, 24.0f, nullptr, g_glyphRanges.data()); } diff --git a/UnleashedRecomp/gpu/imgui_snapshot.h b/UnleashedRecomp/gpu/imgui/imgui_snapshot.h similarity index 93% rename from UnleashedRecomp/gpu/imgui_snapshot.h rename to UnleashedRecomp/gpu/imgui/imgui_snapshot.h index 26b60d0e..cda49add 100644 --- a/UnleashedRecomp/gpu/imgui_snapshot.h +++ b/UnleashedRecomp/gpu/imgui/imgui_snapshot.h @@ -34,7 +34,6 @@ struct ImDrawDataSnapshot // Undefine this to generate a font atlas file in working directory. // You also need to do this if you are testing localization, as only // characters in the locale get added to the atlas. -// Don't forget to compress the generated atlas texture to BC4 with no mips! #define ENABLE_IM_FONT_ATLAS_SNAPSHOT struct ImFontAtlasSnapshot @@ -59,5 +58,5 @@ struct ImFontAtlasSnapshot static void GenerateGlyphRanges(); // When ENABLE_IM_FONT_ATLAS_SNAPSHOT is undefined, this creates the font runtime instead. - static ImFont* GetFont(const char* name, float size); + static ImFont* GetFont(const char* name); }; diff --git a/UnleashedRecomp/gpu/shader/imgui_common.hlsli b/UnleashedRecomp/gpu/shader/imgui_common.hlsli index 6c79a48b..dbc812ec 100644 --- a/UnleashedRecomp/gpu/shader/imgui_common.hlsli +++ b/UnleashedRecomp/gpu/shader/imgui_common.hlsli @@ -1,6 +1,6 @@ #pragma once -#include "../imgui_common.h" +#include "../imgui/imgui_common.h" struct PushConstants { @@ -13,6 +13,7 @@ struct PushConstants float2 InverseDisplaySize; float2 Origin; float2 Scale; + float Outline; }; Texture2D g_Texture2DDescriptorHeap[] : register(t0, space0); diff --git a/UnleashedRecomp/gpu/shader/imgui_ps.hlsl b/UnleashedRecomp/gpu/shader/imgui_ps.hlsl index 6888b895..18928e0f 100644 --- a/UnleashedRecomp/gpu/shader/imgui_ps.hlsl +++ b/UnleashedRecomp/gpu/shader/imgui_ps.hlsl @@ -73,13 +73,67 @@ float4 PixelAntialiasing(float2 uvTexspace) return SampleLinear(uvTexspace); } +float median(float r, float g, float b) +{ + return max(min(r, g), min(max(r, g), b)); +} + float4 main(in Interpolators interpolators) : SV_Target { float4 color = interpolators.Color; color *= PixelAntialiasing(interpolators.Position.xy - 0.5); if (g_PushConstants.Texture2DDescriptorIndex != 0) - color *= g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex].Sample(g_SamplerDescriptorHeap[0], interpolators.UV); + { + Texture2D texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF]; + float4 textureColor = texture.Sample(g_SamplerDescriptorHeap[0], interpolators.UV); + + if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0) + { + uint width, height; + texture.GetDimensions(width, height); + + float pxRange = 8.0; + float2 unitRange = pxRange / float2(width, height); + float2 screenTexSize = 1.0 / fwidth(interpolators.UV); + float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0); + + float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5; + float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 2.0)); + + if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL) + { + float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.01)).xy; + float3 rimColor = float3(1, 0.8, 0.29); + float3 shadowColor = float3(0.84, 0.57, 0); + + float cosTheta = dot(normal, normalize(float2(1, 1))); + float3 gradient = lerp(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta)); + color.rgb = lerp(gradient, color.rgb, pow(saturate(sd + 0.8), 32.0)); + } + else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL) + { + float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.25)).xy; + float cosTheta = dot(normal, normalize(float2(1, 1))); + float gradient = 1.0 + cosTheta * 0.5; + color.rgb = saturate(color.rgb * gradient); + } + else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW) + { + float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.5)).xy; + float cosTheta = dot(normal, normalize(float2(1, 1))); + float gradient = saturate(1.0 + cosTheta); + color.rgb = lerp(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0)); + } + + color.a *= saturate(screenPxDistance + 0.5); + color.a *= textureColor.a; + } + else + { + color *= textureColor; + } + } if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_MARQUEE_FADE) { diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index be7ec78d..af102859 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -1,35 +1,37 @@ -#include +#include "video.h" -#include -#include +#include "imgui/imgui_common.h" +#include "imgui/imgui_snapshot.h" +#include "imgui/imgui_font_builder.h" + +#include #include #include #include +#include +#include +#include #include #include -#include +#include +#include #include +#include #include #include #include #include +#include #include #include -#include - -#include "imgui_snapshot.h" -#include "imgui_common.h" -#include "video.h" #include #include #include +#include -#include -#include -#include -#include - -#include +#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING) +#include +#endif #include "../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #include "shader/copy_vs.hlsl.dxil.h" @@ -38,6 +40,8 @@ #include "shader/csd_filter_ps.hlsl.spirv.h" #include "shader/enhanced_motion_blur_ps.hlsl.dxil.h" #include "shader/enhanced_motion_blur_ps.hlsl.spirv.h" +#include "shader/gamma_correction_ps.hlsl.dxil.h" +#include "shader/gamma_correction_ps.hlsl.spirv.h" #include "shader/gaussian_blur_3x3.hlsl.dxil.h" #include "shader/gaussian_blur_3x3.hlsl.spirv.h" #include "shader/gaussian_blur_5x5.hlsl.dxil.h" @@ -46,16 +50,14 @@ #include "shader/gaussian_blur_7x7.hlsl.spirv.h" #include "shader/gaussian_blur_9x9.hlsl.dxil.h" #include "shader/gaussian_blur_9x9.hlsl.spirv.h" -#include "shader/gamma_correction_ps.hlsl.dxil.h" -#include "shader/gamma_correction_ps.hlsl.spirv.h" #include "shader/imgui_ps.hlsl.dxil.h" #include "shader/imgui_ps.hlsl.spirv.h" #include "shader/imgui_vs.hlsl.dxil.h" #include "shader/imgui_vs.hlsl.spirv.h" -#include "shader/movie_vs.hlsl.dxil.h" -#include "shader/movie_vs.hlsl.spirv.h" #include "shader/movie_ps.hlsl.dxil.h" #include "shader/movie_ps.hlsl.spirv.h" +#include "shader/movie_vs.hlsl.dxil.h" +#include "shader/movie_vs.hlsl.spirv.h" #include "shader/resolve_msaa_depth_2x.hlsl.dxil.h" #include "shader/resolve_msaa_depth_2x.hlsl.spirv.h" #include "shader/resolve_msaa_depth_4x.hlsl.dxil.h" @@ -63,10 +65,6 @@ #include "shader/resolve_msaa_depth_8x.hlsl.dxil.h" #include "shader/resolve_msaa_depth_8x.hlsl.spirv.h" -#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING) -#include -#endif - extern "C" { __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; @@ -1110,8 +1108,11 @@ struct ImGuiPushConstants ImVec2 inverseDisplaySize{}; ImVec2 origin{ 0.0f, 0.0f }; ImVec2 scale{ 1.0f, 1.0f }; + float outline{}; }; +extern ImFontBuilderIO g_fontBuilderIO; + static void CreateImGuiBackend() { ImGuiIO& io = ImGui::GetIO(); @@ -1123,7 +1124,6 @@ static void CreateImGuiBackend() IM_DELETE(io.Fonts); io.Fonts = ImFontAtlasSnapshot::Load(); #else - io.Fonts->TexDesiredWidth = 4096; io.Fonts->AddFontDefault(); ImFontAtlasSnapshot::GenerateGlyphRanges(); #endif @@ -1137,17 +1137,18 @@ static void CreateImGuiBackend() ImGui_ImplSDL2_InitForOther(Window::s_pWindow); - RenderComponentMapping componentMapping(RenderSwizzle::ONE, RenderSwizzle::ONE, RenderSwizzle::ONE, RenderSwizzle::R); - #ifdef ENABLE_IM_FONT_ATLAS_SNAPSHOT - g_imFontTexture = LoadTexture(decompressZstd(g_im_font_atlas_texture, g_im_font_atlas_texture_uncompressed_size).get(), - g_im_font_atlas_texture_uncompressed_size, componentMapping); + g_imFontTexture = LoadTexture( + decompressZstd(g_im_font_atlas_texture, g_im_font_atlas_texture_uncompressed_size).get(), g_im_font_atlas_texture_uncompressed_size); #else + io.Fonts->FontBuilderIO = &g_fontBuilderIO; + io.Fonts->Build(); + g_imFontTexture = std::make_unique(ResourceType::Texture); uint8_t* pixels; int width, height; - io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height); + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); RenderTextureDesc textureDesc; textureDesc.dimension = RenderTextureDimension::TEXTURE_2D; @@ -1156,17 +1157,17 @@ static void CreateImGuiBackend() textureDesc.depth = 1; textureDesc.mipLevels = 1; textureDesc.arraySize = 1; - textureDesc.format = RenderFormat::R8_UNORM; + textureDesc.format = RenderFormat::R8G8B8A8_UNORM; g_imFontTexture->textureHolder = g_device->createTexture(textureDesc); g_imFontTexture->texture = g_imFontTexture->textureHolder.get(); - uint32_t rowPitch = (width + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); + uint32_t rowPitch = (width * 4 + PITCH_ALIGNMENT - 1) & ~(PITCH_ALIGNMENT - 1); uint32_t slicePitch = (rowPitch * height + PLACEMENT_ALIGNMENT - 1) & ~(PLACEMENT_ALIGNMENT - 1); auto uploadBuffer = g_device->createBuffer(RenderBufferDesc::UploadBuffer(slicePitch)); uint8_t* mappedMemory = reinterpret_cast(uploadBuffer->map()); - if (rowPitch == width) + if (rowPitch == (width * 4)) { memcpy(mappedMemory, pixels, slicePitch); } @@ -1174,8 +1175,8 @@ static void CreateImGuiBackend() { for (size_t i = 0; i < height; i++) { - memcpy(mappedMemory, pixels, width); - pixels += width; + memcpy(mappedMemory, pixels, width * 4); + pixels += width * 4; mappedMemory += rowPitch; } } @@ -1188,7 +1189,7 @@ static void CreateImGuiBackend() g_copyCommandList->copyTextureRegion( RenderTextureCopyLocation::Subresource(g_imFontTexture->texture, 0), - RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8_UNORM, width, height, 1, rowPitch, 0)); + RenderTextureCopyLocation::PlacedFootprint(uploadBuffer.get(), RenderFormat::R8G8B8A8_UNORM, width, height, 1, rowPitch / 4, 0)); }); g_imFontTexture->layout = RenderTextureLayout::COPY_DEST; @@ -1197,7 +1198,6 @@ static void CreateImGuiBackend() textureViewDesc.format = textureDesc.format; textureViewDesc.dimension = RenderTextureViewDimension::TEXTURE_2D; textureViewDesc.mipLevels = 1; - textureViewDesc.componentMapping = componentMapping; g_imFontTexture->textureView = g_imFontTexture->texture->createTextureView(textureViewDesc); g_imFontTexture->descriptorIndex = g_textureDescriptorAllocator.allocate(); @@ -1261,7 +1261,7 @@ static void CreateImGuiBackend() ddspp::Header header; ddspp::HeaderDXT10 headerDX10; - ddspp::encode_header(ddspp::R8_UNORM, width, height, 1, ddspp::Texture2D, 1, 1, header, headerDX10); + ddspp::encode_header(ddspp::R8G8B8A8_UNORM, width, height, 1, ddspp::Texture2D, 1, 1, header, headerDX10); file = fopen("im_font_atlas.dds", "wb"); if (file) @@ -1269,7 +1269,7 @@ static void CreateImGuiBackend() fwrite(&ddspp::DDS_MAGIC, 4, 1, file); fwrite(&header, sizeof(header), 1, file); fwrite(&headerDX10, sizeof(headerDX10), 1, file); - fwrite(pixels, 1, width * height, file); + fwrite(pixels, 4, width * height, file); fclose(file); } #endif @@ -1897,6 +1897,25 @@ static void ProcDrawImGui(const RenderCommand& cmd) pushConstants.inverseDisplaySize = { 1.0f / drawData.DisplaySize.x, 1.0f / drawData.DisplaySize.y }; commandList->setGraphicsPushConstants(0, &pushConstants); + size_t pushConstantRangeMin = ~0; + size_t pushConstantRangeMax = 0; + + auto setPushConstants = [&](void* destination, const void* source, size_t size) + { + bool dirty = memcmp(destination, source, size) != 0; + + memcpy(destination, source, size); + + if (dirty) + { + size_t offset = reinterpret_cast(destination) - reinterpret_cast(&pushConstants); + pushConstantRangeMin = std::min(pushConstantRangeMin, offset); + pushConstantRangeMax = std::max(pushConstantRangeMax, offset + size); + } + }; + + ImRect clipRect{}; + for (int i = 0; i < drawData.CmdListsCount; i++) { auto& drawList = drawData.CmdLists[i]; @@ -1921,19 +1940,25 @@ static void ProcDrawImGui(const RenderCommand& cmd) switch (static_cast(reinterpret_cast(drawCmd.UserCallback))) { case ImGuiCallback::SetGradient: - commandList->setGraphicsPushConstants(0, &callbackData->setGradient, offsetof(ImGuiPushConstants, boundsMin), sizeof(callbackData->setGradient)); + setPushConstants(&pushConstants.boundsMin, &callbackData->setGradient, sizeof(callbackData->setGradient)); break; case ImGuiCallback::SetShaderModifier: - commandList->setGraphicsPushConstants(0, &callbackData->setShaderModifier, offsetof(ImGuiPushConstants, shaderModifier), sizeof(callbackData->setShaderModifier)); + setPushConstants(&pushConstants.shaderModifier, &callbackData->setShaderModifier, sizeof(callbackData->setShaderModifier)); break; case ImGuiCallback::SetOrigin: - commandList->setGraphicsPushConstants(0, &callbackData->setOrigin, offsetof(ImGuiPushConstants, origin), sizeof(callbackData->setOrigin)); + setPushConstants(&pushConstants.origin, &callbackData->setOrigin, sizeof(callbackData->setOrigin)); break; case ImGuiCallback::SetScale: - commandList->setGraphicsPushConstants(0, &callbackData->setScale, offsetof(ImGuiPushConstants, scale), sizeof(callbackData->setScale)); + setPushConstants(&pushConstants.scale, &callbackData->setScale, sizeof(callbackData->setScale)); break; case ImGuiCallback::SetMarqueeFade: - commandList->setGraphicsPushConstants(0, &callbackData->setScale, offsetof(ImGuiPushConstants, boundsMin), sizeof(callbackData->setMarqueeFade)); + setPushConstants(&pushConstants.boundsMin, &callbackData->setMarqueeFade, sizeof(callbackData->setMarqueeFade)); + break; + case ImGuiCallback::SetOutline: + setPushConstants(&pushConstants.outline, &callbackData->setOutline, sizeof(callbackData->setOutline)); + break; + default: + assert(false && "Unknown ImGui callback type."); break; } } @@ -1955,10 +1980,26 @@ static void ProcDrawImGui(const RenderCommand& cmd) } descriptorIndex = texture->descriptorIndex; + + if (texture == g_imFontTexture.get()) + descriptorIndex |= 0x80000000; + + setPushConstants(&pushConstants.texture2DDescriptorIndex, &descriptorIndex, sizeof(descriptorIndex)); + } + + if (pushConstantRangeMin < pushConstantRangeMax) + { + commandList->setGraphicsPushConstants(0, reinterpret_cast(&pushConstants) + pushConstantRangeMin, pushConstantRangeMin, pushConstantRangeMax - pushConstantRangeMin); + pushConstantRangeMin = ~0; + pushConstantRangeMax = 0; + } + + if (memcmp(&clipRect, &drawCmd.ClipRect, sizeof(clipRect)) != 0) + { + commandList->setScissors(RenderRect(int32_t(drawCmd.ClipRect.x), int32_t(drawCmd.ClipRect.y), int32_t(drawCmd.ClipRect.z), int32_t(drawCmd.ClipRect.w))); + clipRect = drawCmd.ClipRect; } - commandList->setGraphicsPushConstants(0, &descriptorIndex, offsetof(ImGuiPushConstants, texture2DDescriptorIndex), sizeof(descriptorIndex)); - commandList->setScissors(RenderRect(int32_t(drawCmd.ClipRect.x), int32_t(drawCmd.ClipRect.y), int32_t(drawCmd.ClipRect.z), int32_t(drawCmd.ClipRect.w))); commandList->drawIndexedInstanced(drawCmd.ElemCount, 1, drawCmd.IdxOffset, drawCmd.VtxOffset, 0); } } diff --git a/UnleashedRecomp/main.cpp b/UnleashedRecomp/main.cpp index 42b394f7..b7cad94d 100644 --- a/UnleashedRecomp/main.cpp +++ b/UnleashedRecomp/main.cpp @@ -156,11 +156,12 @@ int main(int argc, char *argv[]) HostStartup(); - Video::CreateHostDevice(); - bool isGameInstalled = Installer::checkGameInstall("."); - if (forceInstaller || forceDLCInstaller || !isGameInstalled) + bool runInstallerWizard = forceInstaller || forceDLCInstaller || !isGameInstalled; + if (runInstallerWizard) { + Video::CreateHostDevice(); + if (!InstallerWizard::Run(isGameInstalled && forceDLCInstaller)) { return 1; @@ -173,6 +174,9 @@ int main(int argc, char *argv[]) uint32_t entry = LdrLoadModule(FileSystem::TransformPath(GAME_XEX_PATH)); + if (!runInstallerWizard) + Video::CreateHostDevice(); + Video::StartPipelinePrecompilation(); GuestThread::Start(entry); diff --git a/UnleashedRecomp/ui/achievement_menu.cpp b/UnleashedRecomp/ui/achievement_menu.cpp index b0928046..eeea6dc0 100644 --- a/UnleashedRecomp/ui/achievement_menu.cpp +++ b/UnleashedRecomp/ui/achievement_menu.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include constexpr double HEADER_CONTAINER_INTRO_MOTION_START = 0; constexpr double HEADER_CONTAINER_INTRO_MOTION_END = 15; @@ -141,14 +141,14 @@ static void DrawHeaderContainer(const char* text) SetTextSkew((min.y + max.y) / 2.0f, Scale(3.0f)); // TODO: Apply bevel. - DrawTextWithOutline + DrawTextWithOutline ( g_fntNewRodinUB, fontSize, { /* X */ min.x + textMarginX, /* Y */ CENTRE_TEXT_VERT(min, max, textSize) - Scale(5) }, IM_COL32(255, 255, 255, 255 * alpha), text, - 1.65f, + 4, IM_COL32(0, 0, 0, 255 * alpha) ); @@ -222,7 +222,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen isUnlocked ? IM_COL32(252, 243, 5, 255) : colLockedText, achievement.Name.c_str(), shadowOffset, - 0.4f, + 1.0f, colTextShadow ); @@ -261,7 +261,7 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen isUnlocked ? IM_COL32_WHITE : colLockedText, desc, shadowOffset, - 0.4f, + 1.0f, colTextShadow ); } @@ -349,14 +349,14 @@ static void DrawAchievement(int rowIndex, float yOffset, Achievement& achievemen ); // Draw timestamp text. - DrawTextWithOutline + DrawTextWithOutline ( g_fntNewRodinDB, fontSize, { /* X */ CENTRE_TEXT_HORZ(timestampMin, timestampMax, textSize), /* Y */ CENTRE_TEXT_VERT(timestampMin, timestampMax, textSize) }, IM_COL32(255, 255, 255, 255), buffer, - 2, + 4, IM_COL32(8, 8, 8, 255) ); @@ -523,14 +523,14 @@ static void DrawAchievementTotal(ImVec2 min, ImVec2 max) auto fontSize = Scale(20); auto textSize = g_fntNewRodinDB->CalcTextSizeA(fontSize, FLT_MAX, 0, str.c_str()); - DrawTextWithOutline + DrawTextWithOutline ( g_fntNewRodinDB, fontSize, { /* X */ imageMin.x - textSize.x - Scale(6), /* Y */ CENTRE_TEXT_VERT(imageMin, imageMax, textSize) }, IM_COL32(255, 255, 255, 255 * alpha), str.c_str(), - 2, + 4, IM_COL32(0, 0, 0, 255 * alpha) ); } @@ -765,11 +765,9 @@ void AchievementMenu::Init() { auto& io = ImGui::GetIO(); - constexpr float FONT_SCALE = 2.0f; - - g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE); - g_fntNewRodinDB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); - g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf", 20.0f * FONT_SCALE); + g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); + g_fntNewRodinDB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf"); + g_fntNewRodinUB = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-UB.otf"); g_upTrophyIcon = LOAD_ZSTD_TEXTURE(g_trophy); g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fill); diff --git a/UnleashedRecomp/ui/achievement_overlay.cpp b/UnleashedRecomp/ui/achievement_overlay.cpp index 8bd398dc..4303e66e 100644 --- a/UnleashedRecomp/ui/achievement_overlay.cpp +++ b/UnleashedRecomp/ui/achievement_overlay.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; @@ -76,9 +76,7 @@ void AchievementOverlay::Init() { auto& io = ImGui::GetIO(); - constexpr float FONT_SCALE = 2.0f; - - g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE); + g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window); } @@ -145,7 +143,7 @@ void AchievementOverlay::Draw() IM_COL32(252, 243, 5, 255), // colour strAchievementUnlocked, // text 2, // offset - 0.4f, // radius + 1.0f, // radius IM_COL32(0, 0, 0, 255) // shadowColour ); @@ -158,7 +156,7 @@ void AchievementOverlay::Draw() IM_COL32(255, 255, 255, 255), // colour strAchievementName, // text 2, // offset - 0.4f, // radius + 1.0f, // radius IM_COL32(0, 0, 0, 255) // shadowColour ); diff --git a/UnleashedRecomp/ui/button_guide.cpp b/UnleashedRecomp/ui/button_guide.cpp index f415f9cf..1bec66aa 100644 --- a/UnleashedRecomp/ui/button_guide.cpp +++ b/UnleashedRecomp/ui/button_guide.cpp @@ -1,6 +1,6 @@ #include "button_guide.h" #include "imgui_utils.h" -#include +#include #include #include #include @@ -188,7 +188,7 @@ static void DrawGuide(float* offset, ImVec2 regionMin, ImVec2 regionMax, EButton ImVec2 textPosition = { textMarginX, textMarginY }; - DrawTextWithOutline(font, fontSize, textPosition, IM_COL32_WHITE, text, 2, IM_COL32_BLACK); + DrawTextWithOutline(font, fontSize, textPosition, IM_COL32_WHITE, text, 4, IM_COL32_BLACK); // Add extra luminance to low quality text. if (fontQuality == EFontQuality::Low) @@ -214,10 +214,8 @@ void ButtonGuide::Init() { auto& io = ImGui::GetIO(); - constexpr float FONT_SCALE = 2.0f; - - g_fntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf", 24.0f * FONT_SCALE); - g_fntNewRodinLQ = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf", 19.0f); + g_fntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf"); + g_fntNewRodinLQ = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf"); g_upControllerIcons = LOAD_ZSTD_TEXTURE(g_controller); g_upKBMIcons = LOAD_ZSTD_TEXTURE(g_kbm); diff --git a/UnleashedRecomp/ui/imgui_utils.h b/UnleashedRecomp/ui/imgui_utils.h index b23a2c9f..73e28d4c 100644 --- a/UnleashedRecomp/ui/imgui_utils.h +++ b/UnleashedRecomp/ui/imgui_utils.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -83,6 +83,17 @@ static void ResetMarqueeFade() SetScale({ 1.0f, 1.0f }); } +static void SetOutline(float outline) +{ + auto callbackData = AddImGuiCallback(ImGuiCallback::SetOutline); + callbackData->setOutline.outline = outline; +} + +static void ResetOutline() +{ + SetOutline(0.0f); +} + // Aspect ratio aware. static float Scale(float size) { @@ -179,53 +190,32 @@ static void DrawTextWithMarquee(const ImFont* font, float fontSize, const ImVec2 drawList->PopClipRect(); } -template -static void DrawTextWithOutline(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 color, const char* text, T outlineSize, ImU32 outlineColor) +static 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(); - outlineSize = Scale(outlineSize); + SetOutline(outlineSize); + drawList->AddText(font, fontSize, pos, outlineColor, text); + ResetOutline(); - if constexpr (std::is_same_v || std::is_same_v) - { - auto step = outlineSize / 2.0f; - - // TODO: This is still very inefficient! - for (float i = -outlineSize; i <= outlineSize; i += step) - { - for (float j = -outlineSize; j <= outlineSize; j += step) - { - if (i == 0.0f && j == 0.0f) - continue; - - drawList->AddText(font, fontSize, { pos.x + i, pos.y + j }, outlineColor, text); - } - } - } - else if constexpr (std::is_integral_v) - { - // TODO: This is very inefficient! - for (int32_t i = -outlineSize + 1; i < outlineSize; i++) - { - for (int32_t j = -outlineSize + 1; j < outlineSize; j++) - { - if (i != 0 || j != 0) - drawList->AddText(font, fontSize, { pos.x + i, pos.y + j }, outlineColor, text); - } - } - } + 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); } -static void DrawTextWithShadow(const ImFont* font, float fontSize, const ImVec2& pos, ImU32 colour, const char* text, float offset = 2.0f, float radius = 0.4f, ImU32 shadowColour = IM_COL32(0, 0, 0, 255)) +static 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); - radius = Scale(radius); - DrawTextWithOutline(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text, radius, shadowColour); + SetOutline(radius); + drawList->AddText(font, fontSize, { pos.x + offset, pos.y + offset }, shadowColour, text); + ResetOutline(); drawList->AddText(font, fontSize, pos, colour, text, nullptr); } diff --git a/UnleashedRecomp/ui/installer_wizard.cpp b/UnleashedRecomp/ui/installer_wizard.cpp index e090e6bb..5e4e85e5 100644 --- a/UnleashedRecomp/ui/installer_wizard.cpp +++ b/UnleashedRecomp/ui/installer_wizard.cpp @@ -4,7 +4,7 @@ #include #include -#include +#include #include #include #include @@ -559,7 +559,7 @@ static void DrawScanlineBars() // Installer text const std::string &headerText = Localise(g_currentPage == WizardPage::Installing ? "Installer_Header_Installing" : "Installer_Header_Installer"); int textAlpha = std::lround(255.0f * ComputeMotionInstaller(g_appearTime, g_disappearTime, TITLE_ANIMATION_TIME, TITLE_ANIMATION_DURATION)); - DrawTextWithOutline(g_dfsogeistdFont, Scale(42.0f), { Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, textAlpha), headerText.c_str(), 4, IM_COL32(0, 0, 0, textAlpha)); + DrawTextWithOutline(g_dfsogeistdFont, Scale(42.0f), { Scale(285.0f), Scale(57.0f) }, IM_COL32(255, 195, 0, textAlpha), headerText.c_str(), 4, IM_COL32(0, 0, 0, textAlpha), IMGUI_SHADER_MODIFIER_TITLE_BEVEL); // Top bar line drawList->AddLine @@ -761,14 +761,14 @@ static void DrawButton(ImVec2 min, ImVec2 max, const char *buttonText, bool sour IM_COL32(baser + 128, baseg + 170, 0, 255) ); - DrawTextWithOutline + DrawTextWithOutline ( font, size, min, IM_COL32(255, 255, 255, 255 * alpha), buttonText, - 2, + 4, IM_COL32(baser, baseg, 0, 255 * alpha) ); @@ -1334,10 +1334,9 @@ static void DrawMessagePrompt() void InstallerWizard::Init() { auto &io = ImGui::GetIO(); - constexpr float FONT_SCALE = 2.0f; - g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE); - g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf", 48.0f * FONT_SCALE); - g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); + g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); + g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf"); + g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf"); g_installTextures[0] = LOAD_ZSTD_TEXTURE(g_install_001); g_installTextures[1] = LOAD_ZSTD_TEXTURE(g_install_002); g_installTextures[2] = LOAD_ZSTD_TEXTURE(g_install_003); diff --git a/UnleashedRecomp/ui/message_window.cpp b/UnleashedRecomp/ui/message_window.cpp index d75787cf..081ed2ac 100644 --- a/UnleashedRecomp/ui/message_window.cpp +++ b/UnleashedRecomp/ui/message_window.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include constexpr double OVERLAY_CONTAINER_COMMON_MOTION_START = 0; constexpr double OVERLAY_CONTAINER_COMMON_MOTION_END = 11; @@ -256,9 +256,7 @@ void MessageWindow::Init() { auto& io = ImGui::GetIO(); - constexpr float FONT_SCALE = 2.0f; - - g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE); + g_fntSeurat = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); g_upSelectionCursor = LOAD_ZSTD_TEXTURE(g_select_fade); g_upWindow = LOAD_ZSTD_TEXTURE(g_general_window); diff --git a/UnleashedRecomp/ui/options_menu.cpp b/UnleashedRecomp/ui/options_menu.cpp index ea63d655..9cd1f05d 100644 --- a/UnleashedRecomp/ui/options_menu.cpp +++ b/UnleashedRecomp/ui/options_menu.cpp @@ -5,9 +5,9 @@ #include "exports.h" #include -#include +#include #include -#include +#include #include #include #include @@ -138,7 +138,15 @@ static void DrawScanlineBars() // Options text // TODO: localise this. - DrawTextWithOutline(g_dfsogeistdFont, Scale(48.0f), { Scale(122.0f), Scale(56.0f) }, IM_COL32(255, 195, 0, 255), "OPTIONS", 4, IM_COL32_BLACK); + DrawTextWithOutline( + g_dfsogeistdFont, + Scale(48.0f), + { Scale(122.0f), Scale(56.0f) }, + IM_COL32(255, 190, 33, 255), + "OPTIONS", + 4, + IM_COL32_BLACK, + IMGUI_SHADER_MODIFIER_TITLE_BEVEL); // Top bar line drawList->AddLine @@ -389,15 +397,16 @@ static bool DrawCategories() IM_COL32(255, 192, 0, alpha) ); - DrawTextWithOutline + DrawTextWithOutline ( g_dfsogeistdFont, size, min, IM_COL32_WHITE, GetCategory(i).c_str(), - 3, - IM_COL32_BLACK + 4, + IM_COL32_BLACK, + IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL ); ResetGradient(); @@ -755,14 +764,14 @@ static void DrawConfigOption(int32_t rowIndex, float yOffset, ConfigDef* conf IM_COL32(128, 170, 0, 255) ); - DrawTextWithOutline + DrawTextWithOutline ( g_newRodinFont, size, min, IM_COL32(255, 255, 255, 255 * alpha), valueText.data(), - 2, + 4, IM_COL32(0, 0, 0, 255 * alpha) ); @@ -990,11 +999,9 @@ void OptionsMenu::Init() { auto& io = ImGui::GetIO(); - constexpr float FONT_SCALE = 2.0f; - - g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf", 24.0f * FONT_SCALE); - g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf", 48.0f * FONT_SCALE); - g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf", 20.0f * FONT_SCALE); + g_seuratFont = ImFontAtlasSnapshot::GetFont("FOT-SeuratPro-M.otf"); + g_dfsogeistdFont = ImFontAtlasSnapshot::GetFont("DFSoGeiStd-W7.otf"); + g_newRodinFont = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-DB.otf"); LoadThumbnails(); } diff --git a/UnleashedRecompResources b/UnleashedRecompResources index c901d06b..a9019c0a 160000 --- a/UnleashedRecompResources +++ b/UnleashedRecompResources @@ -1 +1 @@ -Subproject commit c901d06b8e1fb1c60ad75f22c5e01452f291f949 +Subproject commit a9019c0ab4f8990cbb09916df57e33d2dbed13e0 diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 9f43e072..696296ca 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -1,4 +1,10 @@ +set(MSDF_ATLAS_BUILD_STANDALONE OFF) +set(MSDF_ATLAS_USE_SKIA OFF) +set(MSDF_ATLAS_NO_ARTERY_FONT ON) +set(MSDFGEN_DISABLE_PNG ON) + add_subdirectory(${SWA_THIRDPARTY_ROOT}/PowerRecomp) add_subdirectory(${SWA_THIRDPARTY_ROOT}/ShaderRecomp) add_subdirectory(${SWA_THIRDPARTY_ROOT}/o1heap) add_subdirectory(${SWA_THIRDPARTY_ROOT}/fshasher) +add_subdirectory(${SWA_THIRDPARTY_ROOT}/msdf-atlas-gen) diff --git a/thirdparty/msdf-atlas-gen b/thirdparty/msdf-atlas-gen new file mode 160000 index 00000000..7e8d3464 --- /dev/null +++ b/thirdparty/msdf-atlas-gen @@ -0,0 +1 @@ +Subproject commit 7e8d34645a67c6e4def5d281f7adec1c2501dc49 diff --git a/vcpkg.json b/vcpkg.json index d778cb96..e93e1219 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -23,6 +23,7 @@ }, "magic-enum", "nativefiledialog-extended", - "miniaudio" + "miniaudio", + "freetype" ] }