Do not crop kNoBorder menus on widescreen

This commit is contained in:
Eddoursul 2026-07-30 22:11:28 +02:00
parent 1b2eccfd6c
commit e610f6b796

View File

@ -1,15 +1,17 @@
#pragma once
// Restores the Scaleform scale modes menus were loaded with before Skyrim 1.6.1130.
// Restores the Scaleform scale modes menus were loaded with before Skyrim 1.6.1130, and stops
// kNoBorder from cropping them on displays wider than 16:9.
//
// 1.6.1130 switched nearly every menu to kExactFit, which stretches the movie across the whole
// viewport instead of preserving its aspect ratio. Enderal's menus - including the SkyUI-derived
// widescreen versions shipped with them - are authored for the old modes, so on anything that is
// not 16:9 they come out distorted.
//
// The table below is what 1.6.640 passes to BSScaleformManager::LoadMovie. Menus the update left
// alone (Book, BookMenu, Console, CreditsMenu, CursorMenu, FaderMenu, GiftMenu, StartMenu) are
// deliberately absent and keep whatever the caller asked for.
// The table below is what 1.6.640 passes to BSScaleformManager::LoadMovie, plus BookMenu and
// GiftMenu, which 1.6.1130 left on kNoBorder and which need the same clamp. Menus that want no
// help (Book, Console, CreditsMenu, CursorMenu, FaderMenu, StartMenu) are absent and keep
// whatever the caller asked for.
//
// Based on Widescreen Scale Removed by SkyHorizon (GPL-3.0)
// https://www.nexusmods.com/skyrimspecialedition/mods/136793
@ -34,9 +36,11 @@ namespace MenuAspectRatioFix
{ "TweenMenu"sv, ScaleModeType::kShowAll },
{ "BarterMenu"sv, ScaleModeType::kNoBorder },
{ "BookMenu"sv, ScaleModeType::kNoBorder },
{ "ContainerMenu"sv, ScaleModeType::kNoBorder },
{ "CraftingMenu"sv, ScaleModeType::kNoBorder },
{ "DialogueMenu"sv, ScaleModeType::kNoBorder },
{ "GiftMenu"sv, ScaleModeType::kNoBorder },
{ "InventoryMenu"sv, ScaleModeType::kNoBorder },
{ "LockpickingMenu"sv, ScaleModeType::kNoBorder },
{ "MagicMenu"sv, ScaleModeType::kNoBorder },
@ -53,13 +57,38 @@ namespace MenuAspectRatioFix
return it != modes.end() ? std::optional{ it->second } : std::nullopt;
}
// The movie gets the whole framebuffer as its viewport, so kNoBorder scales a 1280x720 menu
// until it covers the full width and crops whatever no longer fits: at 32:9 that is a 2x zoom
// with half the menu off screen. Past 16:9 it can only ever crop, so hand out kShowAll instead -
// that keeps the 16:9 scale and merely leaves more of the stage surroundings visible, which is
// exactly what SkyUI's widescreen menus stretch their bars over (they cover up to 32:9).
// Narrower than 16:9 kNoBorder is still the better answer: it hides the strips above and below
// the stage that kShowAll would expose.
ScaleModeType ClampToViewport(ScaleModeType a_mode)
{
if (a_mode != ScaleModeType::kNoBorder) {
return a_mode;
}
const auto state = RE::BSGraphics::State::GetSingleton();
if (!state || state->screenHeight == 0) {
return a_mode;
}
// Every vanilla menu is authored on a 1280x720 stage
constexpr float stageAspect = 1280.0f / 720.0f;
const float viewportAspect = static_cast<float>(state->screenWidth) / static_cast<float>(state->screenHeight);
return viewportAspect > stageAspect ? ScaleModeType::kShowAll : a_mode;
}
struct LoadMovie
{
static bool thunk(RE::BSScaleformManager* a_scaleformManager, RE::IMenu* a_menu, RE::GPtr<RE::GFxMovieView>& a_viewOut, const char* a_fileName, ScaleModeType a_mode, float a_backgroundAlpha)
{
if (a_fileName && a_fileName[0]) {
if (const auto mode = GetScaleMode(a_fileName); mode) {
a_mode = *mode;
a_mode = ClampToViewport(*mode);
}
}