Split-screen UI system with full ultrawide and multi-aspect-ratio support

Screen resolution is now auto-detected from the monitor at startup
instead of being hardcoded to 1920x1080. This fixes rendering on
ultrawide (21:9), super-ultrawide (32:9), 16:10, and any other
aspect ratio -- both in singleplayer and split-screen multiplayer.

The 3D world renders at native resolution so the full monitor is used.
Flash UI is 16:9-fitted and centered inside each viewport, pillarboxed
on wide displays and letterboxed on tall ones. Logical game dimensions
(used for ortho projection and HUD layout) are computed proportionally
from the real screen aspect ratio, fixing the stretched world projection
and HUD that the old hardcoded 1280x720 caused on non-16:9 monitors.

GameRenderer::ComputeViewportForPlayer uses the actual backbuffer size
instead of the logical game size, which was causing split-screen
viewports to be sized incorrectly.

UIScene::render fits menus to 16:9 within each split viewport using
GetViewportRect + Fit16x9, keeping inventory/crafting/options screens
at their designed aspect ratio instead of stretching.

Panorama and MenuBackground render at full viewport size with proper
tile scaling so the background fills the entire area without gaps in
vertical split and quadrant layouts.

HUD tile rendering uses ComputeTileScale to uniformly scale the SWF
and show the bottom portion (hotbar, hearts, hunger) in horizontal
and quadrant splits. repositionHud passes visible SWF-space dimensions
to ActionScript for proper element centering within each viewport.

Chat and Tooltips overlays use ComputeTileScale and
ComputeSplitContentOffset to anchor correctly to the bottom of each
player's viewport tile.

Container menus apply Fit16x9 to pointer coordinate mapping so the
cursor tracks correctly in split-screen. getMouseToSWFScale moved out
of the header into the .cpp. Mouse input in onMouseTick is gated to
pad 0 since raw mouse deltas should only drive player 1.

All shared viewport math lives in UISplitScreenHelpers.h:
- GetViewportRect: origin and dimensions for any viewport type
- Fit16x9: aspect-correct fitting with centering offsets
- ComputeTileScale: uniform scale and Y-offset for tile rendering
- ComputeSplitContentOffset: content centering for overlay components
This commit is contained in:
MrTheShy 2026-03-07 02:02:08 +01:00
parent 8c3eaff0f4
commit 7cd990eefb
16 changed files with 339 additions and 190 deletions

View file

@ -481,25 +481,15 @@ void IUIScene_AbstractContainerMenu::onMouseTick()
#endif
#ifdef _WINDOWS64
if (!g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
if (iPad == 0 && !g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsKBMActive())
{
int deltaX = g_KBMInput.GetMouseDeltaX();
int deltaY = g_KBMInput.GetMouseDeltaY();
extern HWND g_hWnd;
RECT rc;
GetClientRect(g_hWnd, &rc);
int winW = rc.right - rc.left;
int winH = rc.bottom - rc.top;
if (winW > 0 && winH > 0)
{
float scaleX = (float)getMovieWidth() / (float)winW;
float scaleY = (float)getMovieHeight() / (float)winH;
vPointerPos.x += (float)deltaX * scaleX;
vPointerPos.y += (float)deltaY * scaleY;
}
float scaleX, scaleY;
getMouseToSWFScale(scaleX, scaleY);
vPointerPos.x += (float)deltaX * scaleX;
vPointerPos.y += (float)deltaY * scaleY;
if (deltaX != 0 || deltaY != 0)
{

View file

@ -277,4 +277,5 @@ public:
virtual int getPad() = 0;
virtual int getMovieWidth() = 0;
virtual int getMovieHeight() = 0;
virtual void getMouseToSWFScale(float &scaleX, float &scaleY) = 0;
};

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIComponent_Chat.h"
#include "UISplitScreenHelpers.h"
#include "..\..\Minecraft.h"
#include "..\..\Gui.h"
@ -114,6 +115,7 @@ void UIComponent_Chat::render(S32 width, S32 height, C4JRender::eViewportType vi
S32 tileWidth = width;
S32 tileHeight = height;
bool needsYTile = false;
switch( viewport )
{
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
@ -121,22 +123,27 @@ void UIComponent_Chat::render(S32 width, S32 height, C4JRender::eViewportType vi
tileHeight = (S32)(ui.getScreenHeight());
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
needsYTile = true;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
tileYStart = (S32)(m_movieHeight / 2);
needsYTile = true;
break;
}
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
F32 scale;
ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart);
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
S32 contentOffX, contentOffY;
ComputeSplitContentOffset(viewport, m_movieWidth, m_movieHeight, scale, tileWidth, tileHeight, tileYStart, contentOffX, contentOffY);
xPos += contentOffX;
yPos += contentOffY;
ui.setupRenderPosition(xPos, yPos);
IggyPlayerDrawTilesStart ( getMovie() );

View file

@ -68,21 +68,26 @@ void UIComponent_MenuBackground::render(S32 width, S32 height, C4JRender::eViewp
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
tileYStart = (S32)(ui.getScreenHeight() / 2);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
tileYStart = (S32)(ui.getScreenHeight() / 2);
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
tileYStart = (S32)(m_movieHeight / 2);
tileYStart = (S32)(ui.getScreenHeight() / 2);
break;
}
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
F32 scaleW = (F32)(tileXStart + tileWidth) / (F32)m_movieWidth;
F32 scaleH = (F32)(tileYStart + tileHeight) / (F32)m_movieHeight;
F32 scale = (scaleW > scaleH) ? scaleW : scaleH;
if(scale < 1.0f) scale = 1.0f;
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
IggyPlayerDrawTilesStart ( getMovie() );
@ -98,6 +103,10 @@ void UIComponent_MenuBackground::render(S32 width, S32 height, C4JRender::eViewp
}
else
{
UIScene::render(width, height, viewport);
if(m_bIsReloading) return;
if(!m_hasTickedOnce || !getMovie()) return;
ui.setupRenderPosition(0, 0);
IggyPlayerSetDisplaySize( getMovie(), (S32)ui.getScreenWidth(), (S32)ui.getScreenHeight() );
IggyPlayerDraw( getMovie() );
}
}

View file

@ -93,38 +93,47 @@ void UIComponent_Panorama::render(S32 width, S32 height, C4JRender::eViewportTyp
}
ui.setupRenderPosition(xPos, yPos);
if((viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT) || (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT))
S32 tileXStart = 0;
S32 tileYStart = 0;
S32 tileWidth = width;
S32 tileHeight = height;
if((viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT) || (viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT))
{
// Need to render at full height, but only the left side of the scene
S32 tileXStart = 0;
S32 tileYStart = 0;
S32 tileWidth = width;
S32 tileHeight = (S32)(ui.getScreenHeight());
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
IggyPlayerDrawTilesStart ( getMovie() );
m_renderWidth = tileWidth;
m_renderHeight = tileHeight;
IggyPlayerDrawTile ( getMovie() ,
tileXStart ,
tileYStart ,
tileXStart + tileWidth ,
tileYStart + tileHeight ,
0 );
IggyPlayerDrawTilesEnd ( getMovie() );
tileHeight = (S32)(ui.getScreenHeight());
}
else
{
// Need to render at full height, and full width. But compressed into the viewport
IggyPlayerSetDisplaySize( getMovie(), ui.getScreenWidth(), ui.getScreenHeight()/2 );
IggyPlayerDraw( getMovie() );
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(ui.getScreenHeight() / 2);
}
F32 scaleW = (F32)(tileXStart + tileWidth) / (F32)m_movieWidth;
F32 scaleH = (F32)(tileYStart + tileHeight) / (F32)m_movieHeight;
F32 scale = (scaleW > scaleH) ? scaleW : scaleH;
if(scale < 1.0f) scale = 1.0f;
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
IggyPlayerDrawTilesStart ( getMovie() );
m_renderWidth = tileWidth;
m_renderHeight = tileHeight;
IggyPlayerDrawTile ( getMovie() ,
tileXStart ,
tileYStart ,
tileXStart + tileWidth ,
tileYStart + tileHeight ,
0 );
IggyPlayerDrawTilesEnd ( getMovie() );
}
else
{
UIScene::render(width, height, viewport);
if(m_bIsReloading) return;
if(!m_hasTickedOnce || !getMovie()) return;
ui.setupRenderPosition(0, 0);
IggyPlayerSetDisplaySize( getMovie(), (S32)ui.getScreenWidth(), (S32)ui.getScreenHeight() );
IggyPlayerDraw( getMovie() );
}
}

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIComponent_Tooltips.h"
#include "UISplitScreenHelpers.h"
UIComponent_Tooltips::UIComponent_Tooltips(int iPad, void *initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
@ -224,6 +225,7 @@ void UIComponent_Tooltips::render(S32 width, S32 height, C4JRender::eViewportTyp
S32 tileWidth = width;
S32 tileHeight = height;
bool needsYTile = false;
switch( viewport )
{
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
@ -231,22 +233,27 @@ void UIComponent_Tooltips::render(S32 width, S32 height, C4JRender::eViewportTyp
tileHeight = (S32)(ui.getScreenHeight());
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
needsYTile = true;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
tileYStart = (S32)(m_movieHeight / 2);
needsYTile = true;
break;
}
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
F32 scale;
ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart);
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
S32 contentOffX, contentOffY;
ComputeSplitContentOffset(viewport, m_movieWidth, m_movieHeight, scale, tileWidth, tileHeight, tileYStart, contentOffX, contentOffY);
xPos += contentOffX;
yPos += contentOffY;
ui.setupRenderPosition(xPos, yPos);
IggyPlayerDrawTilesStart ( getMovie() );

View file

@ -13,6 +13,7 @@
#include "..\..\EnderDragonRenderer.h"
#include "..\..\MultiPlayerLocalPlayer.h"
#include "UIFontData.h"
#include "UISplitScreenHelpers.h"
#ifdef _WINDOWS64
#include "..\..\Windows64\KeyboardMouseInput.h"
#endif
@ -57,6 +58,8 @@ bool UIController::ms_bReloadSkinCSInitialised = false;
DWORD UIController::m_dwTrialTimerLimitSecs=DYNAMIC_CONFIG_DEFAULT_TRIAL_TIME;
// GetViewportRect and Fit16x9 are now in UISplitScreenHelpers.h
#ifdef _WINDOWS64
static UIControl_Slider *FindSliderById(UIScene *pScene, int sliderId)
{
@ -854,28 +857,15 @@ void UIController::tickInput()
S32 displayW = 0, displayH = 0;
getRenderDimensions(vp, displayW, displayH);
S32 originX = 0, originY = 0;
switch (vp)
{
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
originX = (S32)(getScreenWidth() / 4); break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
originX = (S32)(getScreenWidth() / 4);
originY = (S32)(getScreenHeight() / 2); break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
originY = (S32)(getScreenHeight() / 4); break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
originX = (S32)(getScreenWidth() / 2);
originY = (S32)(getScreenHeight() / 4); break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
originX = (S32)(getScreenWidth() / 2); break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
originY = (S32)(getScreenHeight() / 2); break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
originX = (S32)(getScreenWidth() / 2);
originY = (S32)(getScreenHeight() / 2); break;
default: break; // FULLSCREEN and QUADRANT_TOP_LEFT: origin (0,0)
}
F32 vpOriginX, vpOriginY, vpW, vpH;
GetViewportRect(getScreenWidth(), getScreenHeight(), vp, vpOriginX, vpOriginY, vpW, vpH);
// All viewports use Fit16x9 for menu scenes
S32 fitW, fitH, fitOffsetX, fitOffsetY;
Fit16x9(vpW, vpH, fitW, fitH, fitOffsetX, fitOffsetY);
S32 originX = (S32)vpOriginX + fitOffsetX;
S32 originY = (S32)vpOriginY + fitOffsetY;
displayW = fitW;
displayH = fitH;
if (displayW > 0 && displayH > 0)
{
@ -1609,73 +1599,47 @@ void UIController::renderScenes()
void UIController::getRenderDimensions(C4JRender::eViewportType viewport, S32 &width, S32 &height)
{
switch( viewport )
F32 originX, originY, viewW, viewH;
GetViewportRect(getScreenWidth(), getScreenHeight(), viewport, originX, originY, viewW, viewH);
if(viewport == C4JRender::VIEWPORT_TYPE_FULLSCREEN)
{
case C4JRender::VIEWPORT_TYPE_FULLSCREEN:
width = (S32)(getScreenWidth());
height = (S32)(getScreenHeight());
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
width = (S32)(getScreenWidth() / 2);
height = (S32)(getScreenHeight() / 2);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
width = (S32)(getScreenWidth() / 2);
height = (S32)(getScreenHeight() / 2);
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
width = (S32)(getScreenWidth() / 2);
height = (S32)(getScreenHeight() / 2);
break;
S32 offsetX, offsetY;
Fit16x9(viewW, viewH, width, height, offsetX, offsetY);
}
else
{
// Split-screen: use raw viewport dims — the SWF tiling code handles non-16:9
width = (S32)viewW;
height = (S32)viewH;
}
}
void UIController::setupRenderPosition(C4JRender::eViewportType viewport)
{
if(m_bCustomRenderPosition || m_currentRenderViewport != viewport)
m_currentRenderViewport = viewport;
m_bCustomRenderPosition = false;
F32 vpOriginX, vpOriginY, vpW, vpH;
GetViewportRect(getScreenWidth(), getScreenHeight(), viewport, vpOriginX, vpOriginY, vpW, vpH);
S32 xPos, yPos;
if(viewport == C4JRender::VIEWPORT_TYPE_FULLSCREEN)
{
m_currentRenderViewport = viewport;
m_bCustomRenderPosition = false;
S32 xPos = 0;
S32 yPos = 0;
switch( viewport )
{
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
xPos = (S32)(getScreenWidth() / 4);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
xPos = (S32)(getScreenWidth() / 4);
yPos = (S32)(getScreenHeight() / 2);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
yPos = (S32)(getScreenHeight() / 4);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
xPos = (S32)(getScreenWidth() / 2);
yPos = (S32)(getScreenHeight() / 4);
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
xPos = (S32)(getScreenWidth() / 2);
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
yPos = (S32)(getScreenHeight() / 2);
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
xPos = (S32)(getScreenWidth() / 2);
yPos = (S32)(getScreenHeight() / 2);
break;
}
m_tileOriginX = xPos;
m_tileOriginY = yPos;
setTileOrigin(xPos, yPos);
S32 fitW, fitH, fitOffsetX, fitOffsetY;
Fit16x9(vpW, vpH, fitW, fitH, fitOffsetX, fitOffsetY);
xPos = (S32)vpOriginX + fitOffsetX;
yPos = (S32)vpOriginY + fitOffsetY;
}
else
{
// Split-screen: position at viewport origin, no 16:9 fitting
xPos = (S32)vpOriginX;
yPos = (S32)vpOriginY;
}
m_tileOriginX = xPos;
m_tileOriginY = yPos;
setTileOrigin(xPos, yPos);
}
void UIController::setupRenderPosition(S32 xOrigin, S32 yOrigin)

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIScene.h"
#include "UISplitScreenHelpers.h"
#include "..\..\Lighting.h"
#include "..\..\LocalPlayer.h"
@ -685,9 +686,23 @@ void UIScene::render(S32 width, S32 height, C4JRender::eViewportType viewport)
{
if(m_bIsReloading) return;
if(!m_hasTickedOnce || !swf) return;
ui.setupRenderPosition(viewport);
IggyPlayerSetDisplaySize( swf, width, height );
IggyPlayerDraw( swf );
if(viewport != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
{
F32 originX, originY, viewW, viewH;
GetViewportRect(ui.getScreenWidth(), ui.getScreenHeight(), viewport, originX, originY, viewW, viewH);
S32 fitW, fitH, offsetX, offsetY;
Fit16x9(viewW, viewH, fitW, fitH, offsetX, offsetY);
ui.setupRenderPosition((S32)originX + offsetX, (S32)originY + offsetY);
IggyPlayerSetDisplaySize( swf, fitW, fitH );
IggyPlayerDraw( swf );
}
else
{
ui.setupRenderPosition(viewport);
IggyPlayerSetDisplaySize( swf, width, height );
IggyPlayerDraw( swf );
}
}
void UIScene::setOpacity(float percent)

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIScene_AbstractContainerMenu.h"
#include "UISplitScreenHelpers.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.inventory.h"
#include "..\..\..\Minecraft.World\net.minecraft.world.item.h"
@ -187,6 +188,11 @@ void UIScene_AbstractContainerMenu::PlatformInitialize(int iPad, int startIndex)
IggyEvent mouseEvent;
S32 width, height;
m_parentLayer->getRenderDimensions(width, height);
C4JRender::eViewportType vp = m_parentLayer->getViewport();
if(vp != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
Fit16x9(width, height);
S32 x = m_pointerPos.x*((float)width/m_movieWidth);
S32 y = m_pointerPos.y*((float)height/m_movieHeight);
IggyMakeEventMouseMove( &mouseEvent, x, y);
@ -212,6 +218,10 @@ void UIScene_AbstractContainerMenu::tick()
S32 width, height;
m_parentLayer->getRenderDimensions(width, height);
C4JRender::eViewportType vp = m_parentLayer->getViewport();
if(vp != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
Fit16x9(width, height);
S32 x = (S32)(m_pointerPos.x * ((float)width / m_movieWidth));
S32 y = (S32)(m_pointerPos.y * ((float)height / m_movieHeight));
@ -251,6 +261,27 @@ void UIScene_AbstractContainerMenu::render(S32 width, S32 height, C4JRender::eVi
m_needsCacheRendered = false;
}
void UIScene_AbstractContainerMenu::getMouseToSWFScale(float &scaleX, float &scaleY)
{
extern HWND g_hWnd;
RECT rc;
GetClientRect(g_hWnd, &rc);
int winW = rc.right - rc.left;
int winH = rc.bottom - rc.top;
if(winW <= 0 || winH <= 0) { scaleX = 1.0f; scaleY = 1.0f; return; }
S32 renderW, renderH;
C4JRender::eViewportType vp = GetParentLayer()->getViewport();
ui.getRenderDimensions(vp, renderW, renderH);
if(vp != C4JRender::VIEWPORT_TYPE_FULLSCREEN)
Fit16x9(renderW, renderH);
float screenW = (float)ui.getScreenWidth();
float screenH = (float)ui.getScreenHeight();
scaleX = (float)m_movieWidth * screenW / ((float)renderW * (float)winW);
scaleY = (float)m_movieHeight * screenH / ((float)renderH * (float)winH);
}
void UIScene_AbstractContainerMenu::customDraw(IggyCustomDrawCallbackRegion *region)
{
Minecraft *pMinecraft = Minecraft::GetInstance();

View file

@ -38,6 +38,7 @@ public:
int getPad() { return m_iPad; }
int getMovieWidth() { return m_movieWidth; }
int getMovieHeight() { return m_movieHeight; }
void getMouseToSWFScale(float &scaleX, float &scaleY);
bool getIgnoreInput() { return m_bIgnoreInput; }
void setIgnoreInput(bool bVal) { m_bIgnoreInput=bVal; }

View file

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "UI.h"
#include "UIScene_HUD.h"
#include "UISplitScreenHelpers.h"
#include "BossMobGuiInfo.h"
#include "..\..\Minecraft.h"
#include "..\..\MultiplayerLocalPlayer.h"
@ -266,8 +267,6 @@ void UIScene_HUD::handleReload()
SetDisplayName(ProfileManager.GetDisplayName(m_iPad));
repositionHud();
SetTooltipsEnabled(((ui.GetMenuDisplayed(ProfileManager.GetPrimaryPad())) || (app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Tooltips) != 0)));
}
@ -697,6 +696,7 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
S32 tileWidth = width;
S32 tileHeight = height;
bool needsYTile = false;
switch( viewport )
{
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
@ -704,22 +704,24 @@ void UIScene_HUD::render(S32 width, S32 height, C4JRender::eViewportType viewpor
tileHeight = (S32)(ui.getScreenHeight());
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
tileWidth = (S32)(ui.getScreenWidth());
tileYStart = (S32)(m_movieHeight / 2);
needsYTile = true;
break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
tileYStart = (S32)(m_movieHeight / 2);
needsYTile = true;
break;
}
IggyPlayerSetDisplaySize( getMovie(), m_movieWidth, m_movieHeight );
F32 scale;
ComputeTileScale(tileWidth, tileHeight, m_movieWidth, m_movieHeight, needsYTile, scale, tileYStart);
IggyPlayerSetDisplaySize( getMovie(), (S32)(m_movieWidth * scale), (S32)(m_movieHeight * scale) );
repositionHud(tileWidth, tileHeight, scale);
m_renderWidth = tileWidth;
m_renderHeight = tileHeight;
@ -784,34 +786,24 @@ void UIScene_HUD::handleTimerComplete(int id)
//setVisible(anyVisible);
}
void UIScene_HUD::repositionHud()
void UIScene_HUD::repositionHud(S32 tileWidth, S32 tileHeight, F32 scale)
{
if(!m_bSplitscreen) return;
S32 width = 0;
S32 height = 0;
m_parentLayer->getRenderDimensions( width, height );
// Pass the visible tile area in SWF coordinates so ActionScript
// positions elements (crosshair, hotbar, etc.) centered in the
// actually visible region, not the raw viewport.
S32 visibleW = (S32)(tileWidth / scale);
S32 visibleH = (S32)(tileHeight / scale);
switch( m_parentLayer->getViewport() )
{
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
height = (S32)(ui.getScreenHeight());
break;
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
width = (S32)(ui.getScreenWidth());
break;
}
app.DebugPrintf(app.USER_SR, "Reposition HUD with dims %d, %d\n", width, height );
app.DebugPrintf(app.USER_SR, "Reposition HUD: tile %dx%d, scale %.3f, visible SWF %dx%d\n", tileWidth, tileHeight, scale, visibleW, visibleH );
IggyDataValue result;
IggyDataValue value[2];
value[0].type = IGGY_DATATYPE_number;
value[0].number = width;
value[0].number = visibleW;
value[1].type = IGGY_DATATYPE_number;
value[1].number = height;
value[1].number = visibleH;
IggyResult out = IggyPlayerCallMethodRS ( getMovie() , &result, IggyPlayerRootPath( getMovie() ), m_funcRepositionHud , 2 , value );
}

View file

@ -176,5 +176,5 @@ protected:
#endif
private:
void repositionHud();
void repositionHud(S32 tileWidth, S32 tileHeight, F32 scale);
};

View file

@ -0,0 +1,114 @@
#pragma once
// Shared split-screen UI helpers to avoid duplicating viewport math
// across HUD, Chat, Tooltips, and container menus.
// Compute the raw viewport rectangle for a given viewport type.
inline void GetViewportRect(F32 screenW, F32 screenH, C4JRender::eViewportType viewport,
F32 &originX, F32 &originY, F32 &viewW, F32 &viewH)
{
originX = originY = 0;
viewW = screenW;
viewH = screenH;
switch(viewport)
{
case C4JRender::VIEWPORT_TYPE_SPLIT_TOP:
viewH = screenH * 0.5f; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM:
originY = screenH * 0.5f; viewH = screenH * 0.5f; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_LEFT:
viewW = screenW * 0.5f; break;
case C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT:
originX = screenW * 0.5f; viewW = screenW * 0.5f; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT:
viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT:
originX = screenW * 0.5f; viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT:
originY = screenH * 0.5f; viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
case C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_RIGHT:
originX = screenW * 0.5f; originY = screenH * 0.5f;
viewW = screenW * 0.5f; viewH = screenH * 0.5f; break;
default: break;
}
}
// Fit a 16:9 rectangle inside the given dimensions.
inline void Fit16x9(F32 viewW, F32 viewH, S32 &fitW, S32 &fitH, S32 &offsetX, S32 &offsetY)
{
const F32 kAspect = 16.0f / 9.0f;
if(viewW / viewH > kAspect)
{
fitH = (S32)viewH;
fitW = (S32)(viewH * kAspect);
}
else
{
fitW = (S32)viewW;
fitH = (S32)(viewW / kAspect);
}
offsetX = (S32)((viewW - fitW) * 0.5f);
offsetY = (S32)((viewH - fitH) * 0.5f);
}
// Convenience: just fit 16:9 dimensions, ignore offsets.
inline void Fit16x9(S32 &width, S32 &height)
{
S32 offX, offY;
Fit16x9((F32)width, (F32)height, width, height, offX, offY);
}
// Compute the uniform scale and tileYStart for split-screen tile rendering.
// Used by HUD, Chat, and Tooltips to scale the SWF movie to cover the viewport tile.
inline void ComputeTileScale(S32 tileWidth, S32 tileHeight, S32 movieWidth, S32 movieHeight,
bool needsYTile, F32 &outScale, S32 &outTileYStart)
{
F32 scaleW = (F32)tileWidth / (F32)movieWidth;
F32 scaleH = (F32)tileHeight / (F32)movieHeight;
F32 scale = (scaleW > scaleH) ? scaleW : scaleH;
if(scale < 1.0f) scale = 1.0f;
outTileYStart = 0;
if(needsYTile)
{
S32 dispH = (S32)(movieHeight * scale);
outTileYStart = dispH - tileHeight;
if(outTileYStart < 0) outTileYStart = 0;
scaleH = (F32)(outTileYStart + tileHeight) / (F32)movieHeight;
scale = (scaleW > scaleH) ? scaleW : scaleH;
if(scale < 1.0f) scale = 1.0f;
}
outScale = scale;
}
// Compute the render offset to center split-screen SWF content in the viewport.
// Used by Chat and Tooltips (HUD uses repositionHud instead).
inline void ComputeSplitContentOffset(C4JRender::eViewportType viewport, S32 movieWidth, S32 movieHeight,
F32 scale, S32 tileWidth, S32 tileHeight, S32 tileYStart,
S32 &outXOffset, S32 &outYOffset)
{
S32 contentCenterX, contentCenterY;
if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT || viewport == C4JRender::VIEWPORT_TYPE_SPLIT_RIGHT)
{
contentCenterX = (S32)(movieWidth * scale / 4);
contentCenterY = (S32)(movieHeight * scale / 2);
}
else if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_TOP || viewport == C4JRender::VIEWPORT_TYPE_SPLIT_BOTTOM)
{
contentCenterX = (S32)(movieWidth * scale / 2);
contentCenterY = (S32)(movieHeight * scale * 3 / 4);
}
else
{
contentCenterX = (S32)(movieWidth * scale / 4);
contentCenterY = (S32)(movieHeight * scale * 3 / 4);
}
outXOffset = 0;
outYOffset = 0;
if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_LEFT || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_BOTTOM_LEFT)
outXOffset = -(tileWidth / 2 - contentCenterX);
if(viewport == C4JRender::VIEWPORT_TYPE_SPLIT_TOP || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_LEFT || viewport == C4JRender::VIEWPORT_TYPE_QUADRANT_TOP_RIGHT)
outYOffset = -(tileHeight / 2 - (contentCenterY - tileYStart));
}

View file

@ -960,6 +960,10 @@ void GameRenderer::CachePlayerGammas()
bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport) const
{
// Use the actual backbuffer dimensions, not the game's logical size.
extern int g_iScreenWidth;
extern int g_iScreenHeight;
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)
@ -972,8 +976,8 @@ bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport)
{
outViewport.TopLeftX = 0.0f;
outViewport.TopLeftY = 0.0f;
outViewport.Width = static_cast<FLOAT>(mc->width);
outViewport.Height = static_cast<FLOAT>(mc->height);
outViewport.Width = static_cast<FLOAT>(g_iScreenWidth);
outViewport.Height = static_cast<FLOAT>(g_iScreenHeight);
outViewport.MinDepth = 0.0f;
outViewport.MaxDepth = 1.0f;
return true;
@ -989,8 +993,8 @@ bool GameRenderer::ComputeViewportForPlayer(int j, D3D11_VIEWPORT &outViewport)
if (k < 0)
return false;
const float width = static_cast<float>(mc->width);
const float height = static_cast<float>(mc->height);
const float width = static_cast<float>(g_iScreenWidth);
const float height = static_cast<float>(g_iScreenHeight);
if (active == 2)
{

View file

@ -4590,8 +4590,14 @@ void Minecraft::startAndConnectTo(const wstring& name, const wstring& sid, const
Minecraft *minecraft;
// 4J - was new Minecraft(frame, canvas, NULL, 854, 480, fullScreen);
// Logical width is proportional to the real screen aspect ratio so that
// the ortho projection and HUD layout match the viewport without stretching.
extern int g_iScreenWidth;
extern int g_iScreenHeight;
int logicalH = 720;
int logicalW = logicalH * g_iScreenWidth / g_iScreenHeight;
minecraft = new Minecraft(NULL, NULL, NULL, 1280, 720, fullScreen);
minecraft = new Minecraft(NULL, NULL, NULL, logicalW, logicalH, fullScreen);
/* - 4J - removed
{

View file

@ -86,12 +86,9 @@ DWORD dwProfileSettingsA[NUM_PROFILE_VALUES]=
BOOL g_bWidescreen = TRUE;
// DO NOT auto-detect from the monitor (GetSystemMetrics etc.) — the game resolution
// must stay 1920x1080. The precompiled 4J_Render lib, all Flash SWF assets, and the
// split-screen viewport math (RenderManager.StateSetViewport, UIController::getRenderDimensions,
// setupRenderPosition) all assume a 16:9 framebuffer. Using the native monitor resolution
// breaks split-screen on ultrawide/non-16:9 displays: the UI renders offset and the
// bottom portion of each viewport is left empty.
// Screen resolution — auto-detected from the monitor at startup.
// The 3D world renders at native resolution; Flash UI is 16:9-fitted and centered
// within each viewport (pillarboxed on ultrawide, letterboxed on tall displays).
// ApplyScreenMode() can still override these for debug/test resolutions via launch args.
int g_iScreenWidth = 1920;
int g_iScreenHeight = 1080;
@ -1184,8 +1181,10 @@ int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
if (pSlash) { *(pSlash + 1) = '\0'; SetCurrentDirectoryA(szExeDir); }
}
// Declare DPI awareness so pixel coordinates are consistent
// Declare DPI awareness so GetSystemMetrics returns physical pixels
SetProcessDPIAware();
g_iScreenWidth = GetSystemMetrics(SM_CXSCREEN);
g_iScreenHeight = GetSystemMetrics(SM_CYSCREEN);
// Load username from username.txt
char exePath[MAX_PATH] = {};