diff --git a/neo/d3xp/Player.cpp b/neo/d3xp/Player.cpp index 4e1dcabc1..16f6d3fbc 100644 --- a/neo/d3xp/Player.cpp +++ b/neo/d3xp/Player.cpp @@ -1771,6 +1771,13 @@ void idPlayer::Spawn( void ) { cursor = uiManager->FindGui( temp, true, gameLocal.isMultiplayer, gameLocal.isMultiplayer ); } if ( cursor ) { + // DG: make it scale to 4:3 so crosshair looks properly round + // yes, like so many scaling-related things this is a bit hacky + // and note that this is special cased in StateChanged and you + // can *not* generally set windowDef properties like this. + cursor->SetStateBool("scaleto43", true); + cursor->StateChanged(gameLocal.time); // DG end + cursor->Activate( true, gameLocal.time ); } @@ -2459,6 +2466,12 @@ void idPlayer::Restore( idRestoreGame *savefile ) { savefile->ReadInt( focusTime ); savefile->ReadObject( reinterpret_cast( focusVehicle ) ); savefile->ReadUserInterface( cursor ); + // DG: make it scale to 4:3 so crosshair looks properly round + // yes, like so many scaling-related things this is a bit hacky + // and note that this is special cased in StateChanged and you + // can *not* generally set windowDef properties like this. + cursor->SetStateBool("scaleto43", true); + cursor->StateChanged(gameLocal.time); // DG end savefile->ReadInt( oldMouseX ); savefile->ReadInt( oldMouseY ); diff --git a/neo/game/Player.cpp b/neo/game/Player.cpp index 312fce2e7..d0d6a92aa 100644 --- a/neo/game/Player.cpp +++ b/neo/game/Player.cpp @@ -1455,6 +1455,13 @@ void idPlayer::Spawn( void ) { cursor = uiManager->FindGui( temp, true, gameLocal.isMultiplayer, gameLocal.isMultiplayer ); } if ( cursor ) { + // DG: make it scale to 4:3 so crosshair looks properly round + // yes, like so many scaling-related things this is a bit hacky + // and note that this is special cased in StateChanged and you + // can *not* generally set windowDef properties like this. + cursor->SetStateBool("scaleto43", true); + cursor->StateChanged(gameLocal.time); // DG end + cursor->Activate( true, gameLocal.time ); } @@ -2018,6 +2025,13 @@ void idPlayer::Restore( idRestoreGame *savefile ) { savefile->ReadObject( reinterpret_cast( focusVehicle ) ); savefile->ReadUserInterface( cursor ); + // DG: make it scale to 4:3 so crosshair looks properly round + // yes, like so many scaling-related things this is a bit hacky + // and note that this is special cased in StateChanged and you + // can *not* generally set windowDef properties like this. + cursor->SetStateBool("scaleto43", true); + cursor->StateChanged(gameLocal.time); // DG end + savefile->ReadInt( oldMouseX ); savefile->ReadInt( oldMouseY ); diff --git a/neo/renderer/Framebuffer.h b/neo/renderer/Framebuffer.h index a0e6fd317..a6b14e598 100644 --- a/neo/renderer/Framebuffer.h +++ b/neo/renderer/Framebuffer.h @@ -49,6 +49,10 @@ class fhFramebuffer { } void Resize(int width, int height, int samples, pixelFormat_t colorFormat, pixelFormat_t depthFormat); + int GetID() { // JW : for clearing framebuffer effects + return num; + } + idImage* GetColorAttachment() { return colorAttachment; } diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 008c9ba09..c18a3a03e 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -219,6 +219,9 @@ idCVar r_materialOverride( "r_materialOverride", "", CVAR_RENDERER, "overrides a idCVar r_debugRenderToTexture( "r_debugRenderToTexture", "0", CVAR_RENDERER | CVAR_INTEGER, "" ); +// DG: let users disable the "scale menus to 4:3" hack +idCVar r_scaleMenusTo43("r_scaleMenusTo43", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Scale menus, fullscreen videos and PDA to 4:3 aspect ratio"); + idCVar r_softParticles( "r_softParticles", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "enabled soft particles"); idCVar r_defaultParticleSoftness( "r_defaultParticleSoftness", "0.35", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_FLOAT, ""); diff --git a/neo/renderer/tr_backend.cpp b/neo/renderer/tr_backend.cpp index 3abfde67b..532e67ac5 100644 --- a/neo/renderer/tr_backend.cpp +++ b/neo/renderer/tr_backend.cpp @@ -925,6 +925,16 @@ static void RB_CopyRender( const void *data, bool copyFromDefaultFramebuffer ) { fhFramebuffer framebuffer("tmp", cmd->imageWidth, cmd->imageHeight, image, nullptr ); fhFramebuffer::BlitColor( src, cmd->x, cmd->y, cmd->imageWidth, cmd->imageHeight, &framebuffer ); framebuffer.Purge(); + if ( image == fhFramebuffer::currentRenderFramebuffer->GetColorAttachment() ) // JW : need to do below to clear the mirror hell effect in Mars City 2 so it doesn't leave artifacts in subsequent uses of reflection, imp fireball heat distortion etc. + { + fhFramebuffer *currentDrawBuffer = fhFramebuffer::GetCurrentDrawBuffer(); + fhFramebuffer::currentRenderFramebuffer->Bind(); + glBindFramebuffer( GL_DRAW_FRAMEBUFFER, fhFramebuffer::currentRenderFramebuffer->GetID() ); + int samples = fhFramebuffer::currentRenderFramebuffer->GetSamples(); + auto target = samples > 1 ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + glFramebufferTexture2D( GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, fhFramebuffer::currentRenderFramebuffer->GetColorAttachment()->texnum, 0 ); // just reattaching it why + currentDrawBuffer->Bind(); + } } } diff --git a/neo/ui/DeviceContext.cpp b/neo/ui/DeviceContext.cpp index c2739e061..5d3a626bb 100644 --- a/neo/ui/DeviceContext.cpp +++ b/neo/ui/DeviceContext.cpp @@ -135,6 +135,10 @@ void idDeviceContext::Init() { mat.Identity(); origin.Zero(); initialized = true; + + // DG: this is used for the "make sure menus are rendered as 4:3" hack + fixScaleForMenu.Set(1, 1); + fixOffsetForMenu.Set(0, 0); } void idDeviceContext::Shutdown() { @@ -254,8 +258,67 @@ bool idDeviceContext::ClippedCoords(float *x, float *y, float *w, float *h, floa return (*w == 0 || *h == 0) ? true : false; } +// DG: this is used for the "make sure menus are rendered as 4:3" hack +void idDeviceContext::SetMenuScaleFix(bool enable) { + if(enable) { + float w = renderSystem->GetScreenWidth(); + float h = renderSystem->GetScreenHeight(); + float aspectRatio = w/h; + static const float virtualAspectRatio = float(VIRTUAL_WIDTH)/float(VIRTUAL_HEIGHT); // 4:3 + if(aspectRatio > 1.4f) { + // widescreen (4:3 is 1.333 3:2 is 1.5, 16:10 is 1.6, 16:9 is 1.7778) + // => we need to scale and offset X + // All the coordinates here assume 640x480 (VIRTUAL_WIDTH x VIRTUAL_HEIGHT) + // screensize, so to fit a 4:3 menu into 640x480 stretched to a widescreen, + // we need do decrease the width to something smaller than 640 and center + // the result with an offset + float scaleX = virtualAspectRatio/aspectRatio; + float offsetX = (1.0f-scaleX)*(VIRTUAL_WIDTH*0.5f); // (640 - scale*640)/2 + fixScaleForMenu.Set(scaleX, 1); + fixOffsetForMenu.Set(offsetX, 0); + } else if(aspectRatio < 1.24f) { + // portrait-mode, "thinner" than 5:4 (which is 1.25) + // => we need to scale and offset Y + // it's analogue to the other case, but inverted and with height and Y + float scaleY = aspectRatio/virtualAspectRatio; + float offsetY = (1.0f - scaleY)*(VIRTUAL_HEIGHT*0.5f); // (480 - scale*480)/2 + fixScaleForMenu.Set(1, scaleY); + fixOffsetForMenu.Set(0, offsetY); + } + } else { + fixScaleForMenu.Set(1, 1); + fixOffsetForMenu.Set(0, 0); + } +} void idDeviceContext::AdjustCoords(float *x, float *y, float *w, float *h) { + + if (x) { + *x *= xScale; + + *x *= fixScaleForMenu.x; // DG: for "render menus as 4:3" hack + *x += fixOffsetForMenu.x; + } + if (y) { + *y *= yScale; + + *y *= fixScaleForMenu.y; // DG: for "render menus as 4:3" hack + *y += fixOffsetForMenu.y; + } + if (w) { + *w *= xScale; + + *w *= fixScaleForMenu.x; // DG: for "render menus as 4:3" hack + } + if (h) { + *h *= yScale; + + *h *= fixScaleForMenu.y; // DG: for "render menus as 4:3" hack + } +} + +// DG: same as AdjustCoords, but ignore fixupMenus because for the cursor that must be handled seperately +void idDeviceContext::AdjustCursorCoords(float *x, float *y, float *w, float *h) { if (x) { *x *= xScale; } @@ -581,6 +644,26 @@ void idDeviceContext::DrawFilledRect( float x, float y, float w, float h, const DrawStretchPic( x, y, w, h, 0, 0, 0, 0, whiteImage); } +// JW : use to draw pillarboxing background for menus in 4:3 mode. +// uses AdjustCursorCoords instead of AdjustCoords. That method ignores fixupMenus because for the cursor that must be handled seperately +// which also allows us to draw a background that covers the whole screen, causing black pillarboxing outside the 4:3 menu +// instead of just leaving whatever was drawn there before, as happens with r_useFramebuffer 1 (but not r_useFramebuffer 0) +void idDeviceContext::DrawFilledRectNo43(float x, float y, float w, float h, const idVec4 &color) { + + if (color.w == 0.0f) { + return; + } + + renderSystem->SetColor(color); + + if (ClippedCoords(&x, &y, &w, &h, NULL, NULL, NULL, NULL)) { + return; + } + + AdjustCursorCoords(&x, &y, &w, &h); + DrawStretchPic(x, y, w, h, 0, 0, 0, 0, whiteImage); +} + void idDeviceContext::DrawRect( float x, float y, float w, float h, float size, const idVec4 &color) { @@ -637,8 +720,16 @@ void idDeviceContext::DrawCursor(float *x, float *y, float size) { } renderSystem->SetColor(colorWhite); - AdjustCoords(x, y, &size, &size); - DrawStretchPic( *x, *y, size, size, 0, 0, 1, 1, cursorImages[cursor]); + + // DG: I use this instead of plain AdjustCursorCoords and the following lines + // to scale menus and other fullscreen GUIs to 4:3 aspect ratio + AdjustCursorCoords(x, y, &size, &size); + float sizeW = size * fixScaleForMenu.x; + float sizeH = size * fixScaleForMenu.y; + float fixedX = *x * fixScaleForMenu.x + fixOffsetForMenu.x; + float fixedY = *y * fixScaleForMenu.y + fixOffsetForMenu.y; + + DrawStretchPic(fixedX, fixedY, sizeW, sizeH, 0, 0, 1, 1, cursorImages[cursor]); } /* ======================================================================================================================= diff --git a/neo/ui/DeviceContext.h b/neo/ui/DeviceContext.h index cd7172b79..97ca54cf5 100644 --- a/neo/ui/DeviceContext.h +++ b/neo/ui/DeviceContext.h @@ -54,6 +54,7 @@ class idDeviceContext { void DrawMaterial(float x, float y, float w, float h, const idMaterial *mat, const idVec4 &color, float scalex = 1.0, float scaley = 1.0); void DrawRect(float x, float y, float width, float height, float size, const idVec4 &color); void DrawFilledRect(float x, float y, float width, float height, const idVec4 &color); + void DrawFilledRectNo43(float x, float y, float width, float height, const idVec4 &color); // JW : for drawing rectangles unaffected by the 4:3 menu hack (to draw pillarboxes behind menus with r_useFramebuffer 1) int DrawText(const char *text, float textScale, int textAlign, idVec4 color, idRectangle rectDraw, bool wrap, int cursor = -1, bool calcOnly = false, idList *breaks = NULL, int limit = 0 ); void DrawMaterialRect( float x, float y, float w, float h, float size, const idMaterial *mat, const idVec4 &color); void DrawStretchPic(float x, float y, float w, float h, float s0, float t0, float s1, float t1, const idMaterial *mat); @@ -80,6 +81,7 @@ class idDeviceContext { void SetCursor(int n); void AdjustCoords(float *x, float *y, float *w, float *h); + void AdjustCursorCoords(float *x, float *y, float *w, float *h); // DG: added for "render menus as 4:3" hack bool ClippedCoords(float *x, float *y, float *w, float *h); bool ClippedCoords(float *x, float *y, float *w, float *h, float *s1, float *t1, float *s2, float *t2); @@ -96,6 +98,12 @@ class idDeviceContext { void DrawEditCursor(float x, float y, float scale); + // DG: this is used for the "make sure menus are rendered as 4:3" hack + void SetMenuScaleFix(bool enable); + bool IsMenuScaleFixActive() const { + return fixOffsetForMenu.x != 0.0f || fixOffsetForMenu.y != 0.0f; + } + enum { CURSOR_ARROW, CURSOR_HAND, @@ -163,6 +171,10 @@ class idDeviceContext { bool initialized; bool mbcs; + + // DG: this is used for the "make sure menus are rendered as 4:3" hack + idVec2 fixScaleForMenu; + idVec2 fixOffsetForMenu; }; #endif /* !__DEVICECONTEXT_H__ */ diff --git a/neo/ui/RenderWindow.cpp b/neo/ui/RenderWindow.cpp index 87537f75f..462d6772e 100644 --- a/neo/ui/RenderWindow.cpp +++ b/neo/ui/RenderWindow.cpp @@ -139,7 +139,7 @@ void idRenderWindow::Render( int time ) { -void idRenderWindow::Draw(int time, float x, float y) { +void idRenderWindow::Draw(int time, float x_, float y_) { PreRender(); Render(time); @@ -153,10 +153,20 @@ void idRenderWindow::Draw(int time, float x, float y) { refdef.shaderParms[2] = 1; refdef.shaderParms[3] = 1; - refdef.x = drawRect.x; - refdef.y = drawRect.y; - refdef.width = drawRect.w; - refdef.height = drawRect.h; + // DG: for scaling menus to 4:3 (like that spinning mars globe in the main menu) + float x = drawRect.x; + float y = drawRect.y; + float w = drawRect.w; + float h = drawRect.h; + if(dc->IsMenuScaleFixActive()) { + dc->AdjustCoords(&x, &y, &w, &h); + } + + refdef.x = x; + refdef.y = y; + refdef.width = w; + refdef.height = h; + // DG end refdef.fov_x = 90; refdef.fov_y = 2 * atan((float)drawRect.h / drawRect.w) * idMath::M_RAD2DEG; diff --git a/neo/ui/UserInterface.cpp b/neo/ui/UserInterface.cpp index 7bf0981fb..2fab21488 100644 --- a/neo/ui/UserInterface.cpp +++ b/neo/ui/UserInterface.cpp @@ -35,6 +35,7 @@ If you have questions concerning this license or the applicable additional terms #include "UserInterfaceLocal.h" extern idCVar r_skipGuiShaders; // 1 = don't render any gui elements on surfaces +extern idCVar r_scaleMenusTo43; // DG: for the "scale menus to 4:3" hack idUserInterfaceManagerLocal uiManagerLocal; idUserInterfaceManager * uiManager = &uiManagerLocal; @@ -343,8 +344,42 @@ const char *idUserInterfaceLocal::HandleEvent( const sysEvent_t *event, int _tim } if ( event->evType == SE_MOUSE ) { - cursorX += event->evValue; - cursorY += event->evValue2; + if (!desktop || (desktop->GetFlags() & WIN_MENUGUI)) { + // DG: this is a fullscreen GUI, scale the mousedelta added to cursorX/Y + // by 640/w, because the GUI pretends that everything is 640x480 + // even if the actual resolution is higher => mouse moved too fast + float w = renderSystem->GetScreenWidth(); + float h = renderSystem->GetScreenHeight(); + if (w <= 0.0f || h <= 0.0f) { + w = VIRTUAL_WIDTH; + h = VIRTUAL_HEIGHT; + } + + if (r_scaleMenusTo43.GetBool()) { + // in case we're scaling menus to 4:3, we need to take that into account + // when scaling the mouse events. + // no, we can't just call uiManagerLocal.dc.GetFixScaleForMenu() or sth like that, + // because when we're here dc.SetMenuScaleFix(true) is not active and it'd just return (1, 1)! + float aspectRatio = w / h; + static const float virtualAspectRatio = float(VIRTUAL_WIDTH) / float(VIRTUAL_HEIGHT); // 4:3 + if (aspectRatio > 1.4f) { + // widescreen (4:3 is 1.333 3:2 is 1.5, 16:10 is 1.6, 16:9 is 1.7778) + // => we need to modify cursorX scaling, by modifying w + w *= virtualAspectRatio / aspectRatio; + } else if (aspectRatio < 1.24f) { + // portrait-mode, "thinner" than 5:4 (which is 1.25) + // => we need to scale cursorY via h + h *= aspectRatio / virtualAspectRatio; + } + } + + cursorX += event->evValue * (float(VIRTUAL_WIDTH) / w); + cursorY += event->evValue2 * (float(VIRTUAL_HEIGHT) / h); + } else { + // not a fullscreen GUI but some ingame thing - no scaling needed + cursorX += event->evValue; + cursorY += event->evValue2; + } if (cursorX < 0) { cursorX = 0; @@ -428,6 +463,19 @@ float idUserInterfaceLocal::GetStateFloat( const char *varName, const char* defa void idUserInterfaceLocal::StateChanged( int _time, bool redraw ) { time = _time; if (desktop) { + // DG: little hack: allow game DLLs to do + // ui->SetStateBool("scaleto43", true); + // ui->StateChanged(gameLocal.time); + // so we can force cursors.gui (crosshair) to be scaled, for example + bool scaleTo43 = false; + if(state.GetBool("scaleto43", "0", scaleTo43)) { + if(scaleTo43) + desktop->SetFlag(WIN_SCALETO43); + else + desktop->ClearFlag(WIN_SCALETO43); + } + // DG end + desktop->StateChanged( redraw ); } if ( state.GetBool( "noninteractive" ) ) { diff --git a/neo/ui/Window.cpp b/neo/ui/Window.cpp index 77c557e49..6d82501ee 100644 --- a/neo/ui/Window.cpp +++ b/neo/ui/Window.cpp @@ -57,6 +57,8 @@ idCVar idWindow::gui_debug( "gui_debug", "0", CVAR_GUI | CVAR_BOOL, "" ); idCVar idWindow::gui_edit( "gui_edit", "0", CVAR_GUI | CVAR_BOOL, "" ); extern idCVar r_skipGuiShaders; // 1 = don't render any gui elements on surfaces +extern idCVar r_scaleMenusTo43; // DG : should menus render in 4:3 +extern idCVar r_useFramebuffer; // JW : if this var is set, need to do a little extra for pillarboxing to work in 4:3 mode // made RegisterVars a member of idWindow const idRegEntry idWindow::RegisterVars[] = { @@ -136,6 +138,7 @@ void idWindow::CommonInit() { foreColor = idVec4(1, 1, 1, 1); hoverColor = idVec4(1, 1, 1, 1); matColor = idVec4(1, 1, 1, 1); + pillarboxColor = idVec4(0, 0, 0, 1); // JW : color to draw behind 4:3 menus when playing in widescreen mode borderColor.Zero(); background = NULL; backGroundName = ""; @@ -1208,6 +1211,23 @@ void idWindow::Redraw(float x, float y) { return; } + // DG: allow scaling menus to 4:3 + bool fixupFor43 = false; + if ( flags & WIN_DESKTOP ) { + // only scale desktop windows (will automatically scale its sub-windows) + // that EITHER have the scaleto43 flag set OR are fullscreen menus and r_scaleMenusTo43 is 1 + if( (flags & WIN_SCALETO43) || + ((flags & WIN_MENUGUI) && r_scaleMenusTo43.GetBool()) ) + { + // JW: draw black background for menus so pillarboxing works when r_useFramebuffer is 1 + if ((flags & WIN_MENUGUI) && r_scaleMenusTo43.GetBool() && r_useFramebuffer.GetBool()) { + dc->DrawFilledRectNo43(drawRect.x, drawRect.y, drawRect.w, drawRect.h, pillarboxColor); + } + fixupFor43 = true; + dc->SetMenuScaleFix(true); + } + } + if ( flags & WIN_SHOWTIME ) { dc->DrawText(va(" %0.1f seconds\n%s", (float)(time - timeLine) / 1000, gui->State().GetString("name")), 0.35f, 0, dc->colorWhite, idRectangle(100, 0, 80, 80), false); } @@ -1220,6 +1240,9 @@ void idWindow::Redraw(float x, float y) { } if (!visible) { + if (fixupFor43) { // DG: gotta reset that before returning this function + dc->SetMenuScaleFix(false); + } return; } @@ -1288,6 +1311,10 @@ void idWindow::Redraw(float x, float y) { dc->EnableClipping(true); } + if (fixupFor43) { // DG: gotta reset that before returning this function + dc->SetMenuScaleFix(false); + } + drawRect.Offset(-x, -y); clientRect.Offset(-x, -y); textRect.Offset(-x, -y); @@ -1926,6 +1953,15 @@ bool idWindow::ParseInternalVar(const char *_name, idParser *src) { } return true; } + // DG: added this window flag for Windows that should be scaled to 4:3 + // (with "empty" bars left/right or above/below) + if (idStr::Icmp(_name, "scaleto43") == 0) { + if ( src->ParseBool() ) { + flags |= WIN_SCALETO43; + } + return true; + } + // DG end if (idStr::Icmp(_name, "forceaspectwidth") == 0) { forceAspectWidth = src->ParseFloat(); return true; diff --git a/neo/ui/Window.h b/neo/ui/Window.h index 73c6b3631..aa736dcc0 100644 --- a/neo/ui/Window.h +++ b/neo/ui/Window.h @@ -64,6 +64,8 @@ const int WIN_WANTENTER = 0x01000000; const int WIN_DESKTOP = 0x10000000; +const int WIN_SCALETO43 = 0x20000000; // DG: for the "scaleto43" window flag (=> scale window to 4:3 with "empty" bars left/right or above/below) + const char CAPTION_HEIGHT[] = "16.0"; const char SCROLLER_SIZE[] = "16.0"; const int SCROLLBAR_SIZE = 16; @@ -402,6 +404,7 @@ class idWindow { idWinVec4 foreColor; idWinVec4 hoverColor; idWinVec4 borderColor; + idWinVec4 pillarboxColor; // JW : color to draw behind 4:3 menus when playing in widescreen mode idWinFloat textScale; idWinFloat rotate; idWinStr text;