Skip to content

Commit ba522c0

Browse files
Draw low quality text for custom UI that is directly part of the game. (#434)
1 parent 0afd01f commit ba522c0

11 files changed

Lines changed: 141 additions & 61 deletions

File tree

UnleashedRecomp/gpu/imgui/imgui_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define IMGUI_SHADER_MODIFIER_TITLE_BEVEL 8
1212
#define IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL 9
1313
#define IMGUI_SHADER_MODIFIER_RECTANGLE_BEVEL 10
14+
#define IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT 11
1415

1516
#ifdef __cplusplus
1617

UnleashedRecomp/gpu/shader/imgui_common.hlsli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct PushConstants
1212
uint GradientBottomLeft;
1313
uint ShaderModifier;
1414
uint Texture2DDescriptorIndex;
15+
float2 DisplaySize;
1516
float2 InverseDisplaySize;
1617
float2 Origin;
1718
float2 Scale;

UnleashedRecomp/gpu/shader/imgui_ps.hlsl

Lines changed: 82 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ float4 SampleLinear(float2 uvTexspace)
6161

6262
float4 PixelAntialiasing(float2 uvTexspace)
6363
{
64-
if ((g_PushConstants.InverseDisplaySize.y / g_PushConstants.InverseDisplaySize.x) >= (4.0 / 3.0))
65-
uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0f;
64+
if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0))
65+
uvTexspace *= g_PushConstants.InverseDisplaySize.y * 720.0;
6666
else
67-
uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0f;
67+
uvTexspace *= g_PushConstants.InverseDisplaySize.x * 960.0;
6868

6969
float2 seam = floor(uvTexspace + 0.5);
7070
uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam;
@@ -78,6 +78,51 @@ float median(float r, float g, float b)
7878
return max(min(r, g), min(max(r, g), b));
7979
}
8080

81+
float4 SampleSdfFont(float4 color, Texture2D<float4> texture, float2 uv, float2 screenTexSize)
82+
{
83+
float4 textureColor = texture.Sample(g_SamplerDescriptorHeap[0], uv);
84+
85+
uint width, height;
86+
texture.GetDimensions(width, height);
87+
88+
float pxRange = 8.0;
89+
float2 unitRange = pxRange / float2(width, height);
90+
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
91+
92+
float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5;
93+
float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 2.0));
94+
95+
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL)
96+
{
97+
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.01)).xy;
98+
float3 rimColor = float3(1, 0.8, 0.29);
99+
float3 shadowColor = float3(0.84, 0.57, 0);
100+
101+
float cosTheta = dot(normal, normalize(float2(1, 1)));
102+
float3 gradient = lerp(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta));
103+
color.rgb = lerp(gradient, color.rgb, pow(saturate(sd + 0.8), 32.0));
104+
}
105+
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL)
106+
{
107+
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.25)).xy;
108+
float cosTheta = dot(normal, normalize(float2(1, 1)));
109+
float gradient = 1.0 + cosTheta * 0.5;
110+
color.rgb = saturate(color.rgb * gradient);
111+
}
112+
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW)
113+
{
114+
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.5)).xy;
115+
float cosTheta = dot(normal, normalize(float2(1, 1)));
116+
float gradient = saturate(1.0 + cosTheta);
117+
color.rgb = lerp(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0));
118+
}
119+
120+
color.a *= saturate(screenPxDistance + 0.5);
121+
color.a *= textureColor.a;
122+
123+
return color;
124+
}
125+
81126
float4 main(in Interpolators interpolators) : SV_Target
82127
{
83128
float4 color = interpolators.Color;
@@ -86,52 +131,50 @@ float4 main(in Interpolators interpolators) : SV_Target
86131
if (g_PushConstants.Texture2DDescriptorIndex != 0)
87132
{
88133
Texture2D<float4> texture = g_Texture2DDescriptorHeap[g_PushConstants.Texture2DDescriptorIndex & 0x7FFFFFFF];
89-
float4 textureColor = texture.Sample(g_SamplerDescriptorHeap[0], interpolators.UV);
90134

91135
if ((g_PushConstants.Texture2DDescriptorIndex & 0x80000000) != 0)
92136
{
93-
uint width, height;
94-
texture.GetDimensions(width, height);
95-
96-
float pxRange = 8.0;
97-
float2 unitRange = pxRange / float2(width, height);
98-
float2 screenTexSize = 1.0 / fwidth(interpolators.UV);
99-
float screenPxRange = max(0.5 * dot(unitRange, screenTexSize), 1.0);
100-
101-
float sd = median(textureColor.r, textureColor.g, textureColor.b) - 0.5;
102-
float screenPxDistance = screenPxRange * (sd + g_PushConstants.Outline / (pxRange * 2.0));
103-
104-
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TITLE_BEVEL)
105-
{
106-
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.01)).xy;
107-
float3 rimColor = float3(1, 0.8, 0.29);
108-
float3 shadowColor = float3(0.84, 0.57, 0);
109-
110-
float cosTheta = dot(normal, normalize(float2(1, 1)));
111-
float3 gradient = lerp(color.rgb, cosTheta >= 0.0 ? rimColor : shadowColor, abs(cosTheta));
112-
color.rgb = lerp(gradient, color.rgb, pow(saturate(sd + 0.8), 32.0));
113-
}
114-
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_CATEGORY_BEVEL)
137+
if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT)
115138
{
116-
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.25)).xy;
117-
float cosTheta = dot(normal, normalize(float2(1, 1)));
118-
float gradient = 1.0 + cosTheta * 0.5;
119-
color.rgb = saturate(color.rgb * gradient);
139+
float scale;
140+
float invScale;
141+
142+
if ((g_PushConstants.DisplaySize.x * g_PushConstants.InverseDisplaySize.y) >= (4.0 / 3.0))
143+
{
144+
scale = g_PushConstants.InverseDisplaySize.y * 720.0;
145+
invScale = g_PushConstants.DisplaySize.y / 720.0;
146+
}
147+
else
148+
{
149+
scale = g_PushConstants.InverseDisplaySize.x * 960.0;
150+
invScale = g_PushConstants.DisplaySize.x / 960.0;
151+
}
152+
153+
float2 lowQualityPosition = (interpolators.Position.xy - 0.5) * scale;
154+
float2 fracPart = frac(lowQualityPosition);
155+
156+
float2 uvStep = fwidth(interpolators.UV) * invScale;
157+
float2 lowQualityUV = interpolators.UV - fracPart * uvStep;
158+
float2 screenTexSize = 1.0 / uvStep;
159+
160+
float4 topLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, 0), screenTexSize);
161+
float4 topRight = SampleSdfFont(color, texture, lowQualityUV + float2(uvStep.x, 0), screenTexSize);
162+
float4 bottomLeft = SampleSdfFont(color, texture, lowQualityUV + float2(0, uvStep.y), screenTexSize);
163+
float4 bottomRight = SampleSdfFont(color, texture, lowQualityUV + uvStep.xy, screenTexSize);
164+
165+
float4 top = lerp(topLeft, topRight, fracPart.x);
166+
float4 bottom = lerp(bottomLeft, bottomRight, fracPart.x);
167+
168+
color = lerp(top, bottom, fracPart.y);
120169
}
121-
else if (g_PushConstants.ShaderModifier == IMGUI_SHADER_MODIFIER_TEXT_SKEW)
170+
else
122171
{
123-
float2 normal = normalize(float3(ddx(sd), ddy(sd), 0.5)).xy;
124-
float cosTheta = dot(normal, normalize(float2(1, 1)));
125-
float gradient = saturate(1.0 + cosTheta);
126-
color.rgb = lerp(color.rgb * gradient, color.rgb, pow(saturate(sd + 0.77), 32.0));
172+
color = SampleSdfFont(color, texture, interpolators.UV, 1.0 / fwidth(interpolators.UV));
127173
}
128-
129-
color.a *= saturate(screenPxDistance + 0.5);
130-
color.a *= textureColor.a;
131174
}
132175
else
133176
{
134-
color *= textureColor;
177+
color *= texture.Sample(g_SamplerDescriptorHeap[0], interpolators.UV);
135178
}
136179
}
137180

UnleashedRecomp/gpu/video.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,6 +1340,7 @@ struct ImGuiPushConstants
13401340
ImU32 gradientBottomLeft{};
13411341
uint32_t shaderModifier{};
13421342
uint32_t texture2DDescriptorIndex{};
1343+
ImVec2 displaySize{};
13431344
ImVec2 inverseDisplaySize{};
13441345
ImVec2 origin{ 0.0f, 0.0f };
13451346
ImVec2 scale{ 1.0f, 1.0f };
@@ -2496,6 +2497,7 @@ static void ProcDrawImGui(const RenderCommand& cmd)
24962497
commandList->setViewports(RenderViewport(drawData.DisplayPos.x, drawData.DisplayPos.y, drawData.DisplaySize.x, drawData.DisplaySize.y));
24972498

24982499
ImGuiPushConstants pushConstants{};
2500+
pushConstants.displaySize = drawData.DisplaySize;
24992501
pushConstants.inverseDisplaySize = { 1.0f / drawData.DisplaySize.x, 1.0f / drawData.DisplaySize.y };
25002502
commandList->setGraphicsPushConstants(0, &pushConstants);
25012503

UnleashedRecomp/ui/achievement_menu.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ void AchievementMenu::Open()
763763
return std::get<1>(a) > std::get<1>(b);
764764
});
765765

766-
ButtonGuide::Open(Button("Common_Back", FLT_MAX, EButtonIcon::B));
766+
ButtonGuide::Open(Button("Common_Back", FLT_MAX, EButtonIcon::B, EFontQuality::Low));
767767

768768
ResetSelection();
769769
Game_PlaySound("sys_actstg_pausewinopen");

UnleashedRecomp/ui/achievement_overlay.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ void AchievementOverlay::Draw()
183183
IM_COL32(255, 255, 255, 255) // col
184184
);
185185

186+
// Use low quality text.
187+
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
188+
186189
// Draw header text.
187190
DrawTextWithShadow
188191
(
@@ -208,6 +211,9 @@ void AchievementOverlay::Draw()
208211
1.0f, // radius
209212
IM_COL32(0, 0, 0, 255) // shadowColour
210213
);
214+
215+
// Reset low quality text shader modifier.
216+
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
211217
}
212218
else
213219
{

UnleashedRecomp/ui/button_guide.cpp

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
constexpr float DEFAULT_SIDE_MARGINS = 379;
1515

1616
ImFont* g_fntNewRodin;
17-
ImFont* g_fntNewRodinLQ;
1817

1918
std::unique_ptr<GuestTexture> g_upControllerIcons;
2019
std::unique_ptr<GuestTexture> g_upKBMIcons;
@@ -144,21 +143,13 @@ std::tuple<std::tuple<ImVec2, ImVec2>, GuestTexture*> GetButtonIcon(EButtonIcon
144143
return std::make_tuple(btn, texture);
145144
}
146145

147-
ImFont* GetFont(EFontQuality fontQuality)
148-
{
149-
return fontQuality == EFontQuality::Low
150-
? g_fntNewRodinLQ
151-
: g_fntNewRodin;
152-
}
153-
154146
static void DrawGuide(float* offset, ImVec2 regionMin, ImVec2 regionMax, EButtonIcon icon,
155147
EButtonAlignment alignment, ImVec2 iconMin, ImVec2 iconMax, EFontQuality fontQuality,
156148
float textWidth, float maxTextWidth, float textScale, float fontSize, const char* text)
157149
{
158150
auto drawList = ImGui::GetBackgroundDrawList();
159151
auto iconWidth = Scale(g_iconWidths[icon]);
160-
auto font = GetFont(fontQuality);
161-
auto textMarginY = regionMin.y + Scale(8.89f);
152+
auto textMarginY = regionMin.y + Scale(9.0f);
162153

163154
ImVec2 textPos;
164155

@@ -206,11 +197,17 @@ static void DrawGuide(float* offset, ImVec2 regionMin, ImVec2 regionMax, EButton
206197
SetScale({ textScale, 1.0f });
207198
SetOrigin(textPos);
208199

209-
DrawTextWithOutline(font, fontSize, textPos, IM_COL32_WHITE, text, 4, IM_COL32_BLACK);
200+
if (fontQuality == EFontQuality::Low)
201+
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
202+
203+
DrawTextWithOutline(g_fntNewRodin, fontSize, textPos, IM_COL32_WHITE, text, 4, IM_COL32_BLACK);
210204

211-
// Add extra luminance to low quality text.
212205
if (fontQuality == EFontQuality::Low)
213-
drawList->AddText(font, fontSize, textPos, IM_COL32(255, 255, 255, 127), text);
206+
{
207+
// Add extra luminance to low quality text.
208+
drawList->AddText(g_fntNewRodin, fontSize, textPos, IM_COL32(255, 255, 255, 127), text);
209+
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
210+
}
214211

215212
SetScale({ 1.0f, 1.0f });
216213
SetOrigin({ 0.0f, 0.0f });
@@ -221,8 +218,6 @@ void ButtonGuide::Init()
221218
auto& io = ImGui::GetIO();
222219

223220
g_fntNewRodin = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf");
224-
g_fntNewRodinLQ = ImFontAtlasSnapshot::GetFont("FOT-NewRodinPro-M.otf");
225-
226221
g_upControllerIcons = LOAD_ZSTD_TEXTURE(g_controller);
227222
g_upKBMIcons = LOAD_ZSTD_TEXTURE(g_kbm);
228223
}

UnleashedRecomp/ui/button_guide.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ class Button
5555
Button(std::string name, float maxWidth, EButtonIcon icon, bool* visibility)
5656
: Name(name), MaxWidth(maxWidth), Icon(icon), Visibility(visibility) {}
5757

58-
Button(std::string name, float maxWidth, EButtonIcon icon)
59-
: Name(name), MaxWidth(maxWidth), Icon(icon) {}
58+
Button(std::string name, float maxWidth, EButtonIcon icon, EFontQuality fontQuality = EFontQuality::High)
59+
: Name(name), MaxWidth(maxWidth), Icon(icon), FontQuality(fontQuality) {}
6060
};
6161

6262
class ButtonGuide

UnleashedRecomp/ui/imgui_utils.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ void ResetGradient()
4545

4646
void SetShaderModifier(uint32_t shaderModifier)
4747
{
48+
if (shaderModifier == IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT && Config::DisableLowResolutionFontOnCustomUI)
49+
shaderModifier = IMGUI_SHADER_MODIFIER_NONE;
50+
4851
auto callbackData = AddImGuiCallback(ImGuiCallback::SetShaderModifier);
4952
callbackData->setShaderModifier.shaderModifier = shaderModifier;
5053
}

UnleashedRecomp/ui/message_window.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str
223223
auto fontSize = Scale(28);
224224
auto textSize = g_fntSeurat->CalcTextSizeA(fontSize, FLT_MAX, 0, text.c_str());
225225

226+
// Show low quality text in-game.
227+
if (App::s_isInit)
228+
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
229+
226230
DrawTextWithShadow
227231
(
228232
g_fntSeurat,
@@ -231,6 +235,10 @@ void DrawButton(int rowIndex, float yOffset, float width, float height, std::str
231235
isSelected ? IM_COL32(255, 128, 0, 255) : IM_COL32(255, 255, 255, 255),
232236
text.c_str()
233237
);
238+
239+
// Reset the shader modifier.
240+
if (App::s_isInit)
241+
SetShaderModifier(IMGUI_SHADER_MODIFIER_NONE);
234242
}
235243

236244
void DrawNextButtonGuide(bool isController, bool isKeyboard)
@@ -241,11 +249,16 @@ void DrawNextButtonGuide(bool isController, bool isKeyboard)
241249
? EButtonIcon::Enter
242250
: EButtonIcon::LMB;
243251

244-
// Always show controller prompt in-game.
252+
auto fontQuality = EFontQuality::High;
253+
254+
// Always show controller prompt and low quality text in-game.
245255
if (App::s_isInit)
256+
{
246257
icon = EButtonIcon::A;
258+
fontQuality = EFontQuality::Low;
259+
}
247260

248-
ButtonGuide::Open(Button("Common_Next", FLT_MAX, icon));
261+
ButtonGuide::Open(Button("Common_Next", FLT_MAX, icon, fontQuality));
249262
}
250263

251264
static void ResetSelection()
@@ -338,6 +351,10 @@ void MessageWindow::Draw()
338351

339352
if (DrawContainer(g_appearTime, centre, { textSize.x / 2 + textMarginX, textSize.y / 2 + textMarginY }, !g_isControlsVisible))
340353
{
354+
// Use low quality text when the game is booted to not clash with existing UI.
355+
if (App::s_isInit)
356+
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
357+
341358
DrawRubyAnnotatedText
342359
(
343360
g_fntSeurat,
@@ -359,6 +376,10 @@ void MessageWindow::Draw()
359376
true
360377
);
361378

379+
// Reset the shader modifier.
380+
if (App::s_isInit)
381+
SetShaderModifier(IMGUI_SHADER_MODIFIER_LOW_QUALITY_TEXT);
382+
362383
drawList->PopClipRect();
363384

364385
if (g_buttons.size())
@@ -423,10 +444,17 @@ void MessageWindow::Draw()
423444
backIcon = EButtonIcon::Escape;
424445
}
425446

447+
auto fontQuality = EFontQuality::High;
448+
if (App::s_isInit)
449+
{
450+
// Show low quality text in-game.
451+
fontQuality = EFontQuality::Low;
452+
}
453+
426454
std::array<Button, 2> buttons =
427455
{
428-
Button("Common_Select", 115.0f, selectIcon),
429-
Button("Common_Back", FLT_MAX, backIcon),
456+
Button("Common_Select", 115.0f, selectIcon, fontQuality),
457+
Button("Common_Back", FLT_MAX, backIcon, fontQuality),
430458
};
431459

432460
ButtonGuide::Open(buttons);

0 commit comments

Comments
 (0)