Fix split-screen UI wrong positioning on window resize (#989)

* Fix split-screen UI wrong positioning on window resize

In vertical split at window heights below 1080, ComputeTileScale's min-scale clamp (>= 1.0) prevented the SWF from scaling down to fit, cropping the bottom and causing repositionHud to shift HUD elements downward. Chat and Tooltips additionally applied an offset from ComputeSplitContentOffset that only produced correct values at the 1920x1080 design resolution.

Override the scale for vertical split so the SWF fits the full window height when it is shorter than the movie. Remove the broken content offset from Chat and Tooltips -- the tile crop already positions the content correctly.

* Fix gamma post-process in split-screen

The gamma shader sampled the full backbuffer texture (UV 0..1) into each player's viewport, stretching the entire screen into every split region. Extended the shader constant buffer with per-viewport UV offset and scale so each pass samples only its own portion of the backbuffer.

ComputeViewportForPlayer was hardcoded to top/bottom for 2 players, ignoring the vertical split setting. Rewrote it to read each player's m_iScreenSection directly, which already accounts for the split orientation preference.

Secondary players have no Graphics menu and cannot change gamma. CachePlayerGammas now reads the primary player's setting and applies it uniformly to all viewports.
This commit is contained in:
MrTheShy
2026-03-09 04:16:58 +01:00
committed by GitHub
parent 42164eeb89
commit e2adaa082c
6 changed files with 994 additions and 976 deletions

View File

@@ -954,91 +954,70 @@ float GameRenderer::ComputeGammaFromSlider(float slider0to100)
void GameRenderer::CachePlayerGammas()
{
for (int j = 0; j < XUSER_MAX_COUNT && j < NUM_LIGHT_TEXTURES; ++j)
{
std::shared_ptr<MultiplayerLocalPlayer> player = Minecraft::GetInstance()->localplayers[j];
if (!player)
{
m_cachedGammaPerPlayer[j] = 1.0f;
continue;
}
const float slider = app.GetGameSettings(ProfileManager.GetPrimaryPad(), eGameSetting_Gamma);
const float gamma = ComputeGammaFromSlider(slider);
const float slider = app.GetGameSettings(j, eGameSetting_Gamma); // 0..100
m_cachedGammaPerPlayer[j] = ComputeGammaFromSlider(slider);
}
for (int j = 0; j < XUSER_MAX_COUNT && j < NUM_LIGHT_TEXTURES; ++j)
m_cachedGammaPerPlayer[j] = gamma;
}
bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport) const
{
// Use the actual backbuffer dimensions so viewports adapt to window resize.
extern int g_rScreenWidth;
extern int g_rScreenHeight;
int active = 0;
int indexMap[NUM_LIGHT_TEXTURES] = {-1, -1, -1, -1};
for (int i = 0; i < XUSER_MAX_COUNT && i < NUM_LIGHT_TEXTURES; ++i)
{
if (Minecraft::GetInstance()->localplayers[i])
indexMap[active++] = i;
}
if (active <= 1)
{
outViewport.TopLeftX = 0.0f;
outViewport.TopLeftY = 0.0f;
outViewport.Width = static_cast<FLOAT>(g_rScreenWidth);
outViewport.Height = static_cast<FLOAT>(g_rScreenHeight);
outViewport.MinDepth = 0.0f;
outViewport.MaxDepth = 1.0f;
return true;
}
int k = -1;
for (int ord = 0; ord < active; ++ord)
if (indexMap[ord] == j)
{
k = ord;
break;
}
if (k < 0)
std::shared_ptr<MultiplayerLocalPlayer> player = Minecraft::GetInstance()->localplayers[j];
if (!player)
return false;
const float width = static_cast<float>(g_rScreenWidth);
const float height = static_cast<float>(g_rScreenHeight);
const float w = static_cast<float>(g_rScreenWidth);
const float h = static_cast<float>(g_rScreenHeight);
const float halfW = w * 0.5f;
const float halfH = h * 0.5f;
if (active == 2)
outViewport.MinDepth = 0.0f;
outViewport.MaxDepth = 1.0f;
switch (static_cast<C4JRender::eViewportType>(player->m_iScreenSection))
{
const float halfH = height * 0.5f;
outViewport.TopLeftX = 0.0f;
outViewport.Width = width;
outViewport.MinDepth = 0.0f;
outViewport.MaxDepth = 1.0f;
if (k == 0)
{
outViewport.TopLeftY = 0.0f;
outViewport.Height = halfH;
}
else
{
outViewport.TopLeftY = halfH;
outViewport.Height = halfH;
}
return true;
}
else
{
const float halfW = width * 0.5f;
const float halfH = height * 0.5f;
const int row = (k >= 2) ? 1 : 0;
const int col = (k % 2);
outViewport.TopLeftX = col ? halfW : 0.0f;
outViewport.TopLeftY = row ? halfH : 0.0f;
outViewport.Width = halfW;
outViewport.Height = halfH;
outViewport.MinDepth = 0.0f;
outViewport.MaxDepth = 1.0f;
return true;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
outViewport.TopLeftX = 0; outViewport.TopLeftY = 0;
outViewport.Width = w; outViewport.Height = halfH;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
outViewport.TopLeftX = 0; outViewport.TopLeftY = halfH;
outViewport.Width = w; outViewport.Height = halfH;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
outViewport.TopLeftX = 0; outViewport.TopLeftY = 0;
outViewport.Width = halfW; outViewport.Height = h;
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
outViewport.TopLeftX = halfW; outViewport.TopLeftY = 0;
outViewport.Width = halfW; outViewport.Height = h;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
outViewport.TopLeftX = 0; outViewport.TopLeftY = 0;
outViewport.Width = halfW; outViewport.Height = halfH;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
outViewport.TopLeftX = halfW; outViewport.TopLeftY = 0;
outViewport.Width = halfW; outViewport.Height = halfH;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
outViewport.TopLeftX = 0; outViewport.TopLeftY = halfH;
outViewport.Width = halfW; outViewport.Height = halfH;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
outViewport.TopLeftX = halfW; outViewport.TopLeftY = halfH;
outViewport.Width = halfW; outViewport.Height = halfH;
break;
default:
outViewport.TopLeftX = 0; outViewport.TopLeftY = 0;
outViewport.Width = w; outViewport.Height = h;
break;
}
return true;
}
uint32_t GameRenderer::BuildPlayerViewports(D3D11_VIEWPORT *outViewports, float *outGammas, UINT maxCount) const