diff --git a/src/gl/renderer/gl_2ddrawer.cpp b/src/gl/renderer/gl_2ddrawer.cpp index 7c896d62c..8eb7b6c78 100644 --- a/src/gl/renderer/gl_2ddrawer.cpp +++ b/src/gl/renderer/gl_2ddrawer.cpp @@ -155,14 +155,10 @@ void F2DDrawer::AddTexture(FTexture *img, DrawParms &parms) color.a = (BYTE)(parms.Alpha * 255); // scissor test doesn't use the current viewport for the coordinates, so use real screen coordinates - int btm = (SCREENHEIGHT - screen->GetHeight()) / 2; - btm = SCREENHEIGHT - btm; - - int space = (static_cast(screen)->GetTrueHeight() - screen->GetHeight()) / 2; - dg.mScissor[0] = parms.lclip; - dg.mScissor[1] = btm - parms.dclip + space; - dg.mScissor[2] = parms.rclip - parms.lclip; - dg.mScissor[3] = parms.dclip - parms.uclip; + dg.mScissor[0] = GLRenderer->ScreenToWindowX(parms.lclip); + dg.mScissor[1] = GLRenderer->ScreenToWindowY(parms.dclip); + dg.mScissor[2] = GLRenderer->ScreenToWindowX(parms.rclip) - GLRenderer->ScreenToWindowX(parms.lclip); + dg.mScissor[3] = GLRenderer->ScreenToWindowY(parms.dclip) - GLRenderer->ScreenToWindowY(parms.uclip); FSimpleVertex *ptr = &mVertices[dg.mVertIndex]; ptr->Set(x, y, 0, u1, v1, color); ptr++; @@ -438,7 +434,8 @@ void F2DDrawer::Flush() glDrawArrays(GL_TRIANGLE_STRIP, dt->mVertIndex + 4, 4); } - glScissor(0, 0, screen->GetWidth(), screen->GetHeight()); + const auto &viewport = GLRenderer->mScreenViewport; + glScissor(viewport.left, viewport.top, viewport.width, viewport.height); glDisable(GL_SCISSOR_TEST); gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); gl_RenderState.SetTextureMode(TM_MODULATE); diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index dd4cc71aa..2d62a8d02 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -182,7 +182,7 @@ void FGLRenderer::BloomScene() // Add bloom back to scene texture: mBuffers->BindCurrentFB(); - glViewport(mOutputViewport.left, mOutputViewport.top, mOutputViewport.width, mOutputViewport.height); + glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_ONE, GL_ONE); @@ -243,7 +243,7 @@ void FGLRenderer::LensDistortScene() 0.0f }; - float aspect = mOutputViewport.width / mOutputViewport.height; + float aspect = mSceneViewport.width / mSceneViewport.height; // Scale factor to keep sampling within the input texture float r2 = aspect * aspect * 0.25 + 0.25f; @@ -283,60 +283,21 @@ void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) if (FGLRenderBuffers::IsEnabled()) { FGLPostProcessState savedState; - mBuffers->BindOutputFB(); - int x, y, width, height; + GL_IRECT box; if (bounds) { - x = bounds->left; - y = bounds->top; - width = bounds->width; - height = bounds->height; + box = *bounds; } else { - // Calculate letterbox - int clientWidth = framebuffer->GetClientWidth(); - int clientHeight = framebuffer->GetClientHeight(); - float scaleX = clientWidth / (float)mScreenViewport.width; - float scaleY = clientHeight / (float)mScreenViewport.height; - float scale = MIN(scaleX, scaleY); - width = (int)round(mScreenViewport.width * scale); - height = (int)round(mScreenViewport.height * scale); - x = (clientWidth - width) / 2; - y = (clientHeight - height) / 2; - - // Black bars around the box: - glViewport(0, 0, clientWidth, clientHeight); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glEnable(GL_SCISSOR_TEST); - if (y > 0) - { - glScissor(0, 0, clientWidth, y); - glClear(GL_COLOR_BUFFER_BIT); - } - if (clientHeight - y - height > 0) - { - glScissor(0, y + height, clientWidth, clientHeight - y - height); - glClear(GL_COLOR_BUFFER_BIT); - } - if (x > 0) - { - glScissor(0, y, x, height); - glClear(GL_COLOR_BUFFER_BIT); - } - if (clientWidth - x - width > 0) - { - glScissor(x + width, y, clientWidth - x - width, height); - glClear(GL_COLOR_BUFFER_BIT); - } + ClearBorders(); + box = mOutputLetterbox; } - glDisable(GL_SCISSOR_TEST); // Present what was rendered: - glViewport(x, y, width, height); - glDisable(GL_BLEND); + glViewport(box.left, box.top, box.width, box.height); mPresentShader->Bind(); mPresentShader->InputTexture.Set(0); @@ -358,4 +319,48 @@ void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); RenderScreenQuad(); } + else if (!bounds) + { + FGLPostProcessState savedState; + ClearBorders(); + } +} + +//----------------------------------------------------------------------------- +// +// Fills the black bars around the screen letterbox +// +//----------------------------------------------------------------------------- + +void FGLRenderer::ClearBorders() +{ + const auto &box = mOutputLetterbox; + + int clientWidth = framebuffer->GetClientWidth(); + int clientHeight = framebuffer->GetClientHeight(); + + glViewport(0, 0, clientWidth, clientHeight); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glEnable(GL_SCISSOR_TEST); + if (box.top > 0) + { + glScissor(0, 0, clientWidth, box.top); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientHeight - box.top - box.height > 0) + { + glScissor(0, box.top + box.height, clientWidth, clientHeight - box.top - box.height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (box.left > 0) + { + glScissor(0, box.top, box.left, box.height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientWidth - box.left - box.width > 0) + { + glScissor(box.left + box.width, box.top, clientWidth - box.left - box.width, box.height); + glClear(GL_COLOR_BUFFER_BIT); + } + glDisable(GL_SCISSOR_TEST); } diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index b00396e21..7a4dbab55 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -79,6 +79,8 @@ EXTERN_CVAR(Int, screenblocks) +CVAR(Bool, gl_scale_viewport, true, 0); + //=========================================================================== // // Renderer interface @@ -190,16 +192,14 @@ void FGLRenderer::SetOutputViewport(GL_IRECT *bounds) { if (bounds) { - mOutputViewport = *bounds; - mOutputViewportLB = *bounds; + mSceneViewport = *bounds; mScreenViewport = *bounds; + mOutputLetterbox = *bounds; return; } - int height, width; - // Special handling so the view with a visible status bar displays properly - + int height, width; if (screenblocks >= 10) { height = framebuffer->GetHeight(); @@ -211,29 +211,64 @@ void FGLRenderer::SetOutputViewport(GL_IRECT *bounds) width = (screenblocks*framebuffer->GetWidth() / 10); } - int trueheight = framebuffer->GetTrueHeight(); // ugh... - int bars = (trueheight - framebuffer->GetHeight()) / 2; - - int vw = viewwidth; - int vh = viewheight; + // Back buffer letterbox for the final output + int clientWidth = framebuffer->GetClientWidth(); + int clientHeight = framebuffer->GetClientHeight(); + int screenWidth = framebuffer->GetWidth(); + int screenHeight = framebuffer->GetHeight(); + float scale = MIN(clientWidth / (float)screenWidth, clientHeight / (float)screenHeight); + mOutputLetterbox.width = (int)round(screenWidth * scale); + mOutputLetterbox.height = (int)round(screenHeight * scale); + mOutputLetterbox.left = (clientWidth - mOutputLetterbox.width) / 2; + mOutputLetterbox.top = (clientHeight - mOutputLetterbox.height) / 2; // The entire renderable area, including the 2D HUD mScreenViewport.left = 0; mScreenViewport.top = 0; - mScreenViewport.width = framebuffer->GetWidth(); - mScreenViewport.height = framebuffer->GetHeight(); + mScreenViewport.width = screenWidth; + mScreenViewport.height = screenHeight; - // Letterboxed viewport for the main scene - mOutputViewportLB.left = viewwindowx; - mOutputViewportLB.top = trueheight - bars - (height + viewwindowy - ((height - vh) / 2)); - mOutputViewportLB.width = vw; - mOutputViewportLB.height = height; + // Viewport for the 3D scene + mSceneViewport.left = viewwindowx; + mSceneViewport.top = screenHeight - (height + viewwindowy - ((height - viewheight) / 2)); + mSceneViewport.width = viewwidth; + mSceneViewport.height = height; - // Entire canvas for player sprites - mOutputViewport.left = 0; - mOutputViewport.top = (trueheight - framebuffer->GetHeight()) / 2; - mOutputViewport.width = framebuffer->GetWidth(); - mOutputViewport.height = framebuffer->GetHeight(); + // Scale viewports to fit letterbox + if (gl_scale_viewport || !FGLRenderBuffers::IsEnabled()) + { + mScreenViewport.width = mOutputLetterbox.width; + mScreenViewport.height = mOutputLetterbox.height; + mSceneViewport.left = (int)round(mSceneViewport.left * scale); + mSceneViewport.top = (int)round(mSceneViewport.top * scale); + mSceneViewport.width = (int)round(mSceneViewport.width * scale); + mSceneViewport.height = (int)round(mSceneViewport.height * scale); + + // Without render buffers we have to render directly to the letterbox + if (!FGLRenderBuffers::IsEnabled()) + { + mScreenViewport.left += mOutputLetterbox.left; + mScreenViewport.top += mOutputLetterbox.top; + mSceneViewport.left += mOutputLetterbox.left; + mSceneViewport.top += mOutputLetterbox.top; + } + } +} + +//=========================================================================== +// +// Calculates the OpenGL window coordinates for a zdoom screen position +// +//=========================================================================== + +int FGLRenderer::ScreenToWindowX(int x) +{ + return mScreenViewport.left + (int)round(x * mScreenViewport.width / (float)framebuffer->GetWidth()); +} + +int FGLRenderer::ScreenToWindowY(int y) +{ + return mScreenViewport.top + mScreenViewport.height - (int)round(y * mScreenViewport.height / (float)framebuffer->GetHeight()); } //=========================================================================== @@ -258,6 +293,7 @@ void FGLRenderer::Begin2D() mBuffers->BindCurrentFB(); } glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + glScissor(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); gl_RenderState.EnableFog(false); gl_RenderState.Set2DMode(true); @@ -363,32 +399,3 @@ unsigned char *FGLRenderer::GetTextureBuffer(FTexture *tex, int &w, int &h) } return NULL; } - -//=========================================================================== -// -// -// -//=========================================================================== - -void FGLRenderer::ClearBorders() -{ - OpenGLFrameBuffer *glscreen = static_cast(screen); - - // Letterbox time! Draw black top and bottom borders. - int width = glscreen->GetWidth(); - int height = glscreen->GetHeight(); - int trueHeight = glscreen->GetTrueHeight(); - - int borderHeight = (trueHeight - height) / 2; - - glViewport(0, 0, width, trueHeight); - glClearColor(0, 0, 0, 1); - glEnable(GL_SCISSOR_TEST); - glScissor(0, 0, width, borderHeight); - glClear(GL_COLOR_BUFFER_BIT); - glScissor(0, trueHeight-borderHeight, width, borderHeight); - glClear(GL_COLOR_BUFFER_BIT); - glDisable(GL_SCISSOR_TEST); - glViewport(0, (trueHeight - height) / 2, width, height); -} - diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 1a8883b3f..1ce241da3 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -111,17 +111,20 @@ public: F2DDrawer *m2DDrawer; GL_IRECT mScreenViewport; - GL_IRECT mOutputViewportLB; - GL_IRECT mOutputViewport; + GL_IRECT mSceneViewport; + GL_IRECT mOutputLetterbox; bool mDrawingScene2D = false; float mCameraExposure = 1.0f; FGLRenderer(OpenGLFrameBuffer *fb); ~FGLRenderer() ; + void SetOutputViewport(GL_IRECT *bounds); + int ScreenToWindowX(int x); + int ScreenToWindowY(int y); + angle_t FrustumAngle(); void SetViewArea(); - void SetOutputViewport(GL_IRECT *bounds); void Set3DViewport(bool mainview); void Reset3DViewport(); sector_t *RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 44118087d..4ba181656 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -157,7 +157,7 @@ void FGLRenderer::SetViewArea() void FGLRenderer::Reset3DViewport() { - glViewport(mOutputViewport.left, mOutputViewport.top, mOutputViewport.width, mOutputViewport.height); + glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); } //----------------------------------------------------------------------------- @@ -170,11 +170,11 @@ void FGLRenderer::Set3DViewport(bool mainview) { if (mainview && FGLRenderBuffers::IsEnabled()) { - mBuffers->Setup(mOutputViewport.width, mOutputViewport.height); + mBuffers->Setup(mScreenViewport.width, mScreenViewport.height); mBuffers->BindSceneFB(); } - const auto &bounds = mOutputViewportLB; + const auto &bounds = mSceneViewport; glViewport(bounds.left, bounds.top, bounds.width, bounds.height); glScissor(bounds.left, bounds.top, bounds.width, bounds.height); diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 495df07e8..d06b917a6 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -160,10 +160,6 @@ void OpenGLFrameBuffer::InitializeState() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - //int trueH = GetTrueHeight(); - //int h = GetHeight(); - //glViewport(0, (trueH - h)/2, GetWidth(), GetHeight()); - GLRenderer->Initialize(GetWidth(), GetHeight()); GLRenderer->SetOutputViewport(nullptr); Begin2D(false); @@ -191,13 +187,6 @@ void OpenGLFrameBuffer::Update() DrawRateStuff(); GLRenderer->Flush(); - if (GetTrueHeight() != GetHeight()) - { - if (GLRenderer != NULL) - GLRenderer->ClearBorders(); - - Begin2D(false); - } if (gl_draw_sync || !swapped) { Swap(); @@ -484,15 +473,41 @@ void OpenGLFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, int void OpenGLFrameBuffer::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type) { + const auto &viewport = GLRenderer->mScreenViewport; + + // Grab what is in the back buffer. + // We cannot rely on SCREENWIDTH/HEIGHT here because the output may have been scaled. + TArray pixels; + pixels.Resize(viewport.width * viewport.height); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(viewport.left, viewport.top, viewport.width, viewport.height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + + // Copy to screenshot buffer: int w = SCREENWIDTH; int h = SCREENHEIGHT; ReleaseScreenshotBuffer(); ScreenshotBuffer = new BYTE[w * h * 3]; - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(0,(GetTrueHeight() - GetHeight()) / 2,w,h,GL_RGB,GL_UNSIGNED_BYTE,ScreenshotBuffer); - glPixelStorei(GL_PACK_ALIGNMENT, 4); + float rcpWidth = 1.0f / viewport.width; + float rcpHeight = 1.0f / viewport.height; + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + float u = (x + 0.5f) * rcpWidth; + float v = (y + 0.5f) * rcpHeight; + int sx = u * viewport.width; + int sy = v * viewport.height; + int sindex = (sx + sy * w) * 3; + int dindex = (x + y * w) * 3; + ScreenshotBuffer[dindex] = pixels[sindex]; + ScreenshotBuffer[dindex + 1] = pixels[sindex + 1]; + ScreenshotBuffer[dindex + 2] = pixels[sindex + 2]; + } + } + pitch = -w*3; color_type = SS_RGB; buffer = ScreenshotBuffer + w * 3 * (h - 1); diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 59adc7e9e..c6c09b551 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -151,8 +151,9 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) return false; } - wipestartscreen = new FHardwareTexture(Width, Height, true); - wipestartscreen->CreateTexture(NULL, Width, Height, 0, false, 0); + const auto &viewport = GLRenderer->mScreenViewport; + wipestartscreen = new FHardwareTexture(viewport.width, viewport.height, true); + wipestartscreen->CreateTexture(NULL, viewport.width, viewport.height, 0, false, 0); GLRenderer->mSamplerManager->Bind(0, CLAMP_NOFILTER, -1); GLRenderer->mSamplerManager->Bind(1, CLAMP_NONE, -1); glFinish(); @@ -161,14 +162,14 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) if (FGLRenderBuffers::IsEnabled()) { GLRenderer->mBuffers->BindCurrentFB(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height); } else { GLint readbuffer = 0; glGetIntegerv(GL_READ_BUFFER, &readbuffer); glReadBuffer(GL_FRONT); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height); glReadBuffer(readbuffer); } @@ -189,8 +190,10 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) void OpenGLFrameBuffer::WipeEndScreen() { GLRenderer->m2DDrawer->Flush(); - wipeendscreen = new FHardwareTexture(Width, Height, true); - wipeendscreen->CreateTexture(NULL, Width, Height, 0, false, 0); + + const auto &viewport = GLRenderer->mScreenViewport; + wipeendscreen = new FHardwareTexture(viewport.width, viewport.height, true); + wipeendscreen->CreateTexture(NULL, viewport.width, viewport.height, 0, false, 0); GLRenderer->mSamplerManager->Bind(0, CLAMP_NOFILTER, -1); glFinish(); wipeendscreen->Bind(0, false, false); @@ -198,7 +201,7 @@ void OpenGLFrameBuffer::WipeEndScreen() if (FGLRenderBuffers::IsEnabled()) GLRenderer->mBuffers->BindCurrentFB(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, viewport.left, viewport.top, viewport.width, viewport.height); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); diff --git a/src/posix/cocoa/sdlglvideo.h b/src/posix/cocoa/sdlglvideo.h index cab846a15..fcbf23f2a 100644 --- a/src/posix/cocoa/sdlglvideo.h +++ b/src/posix/cocoa/sdlglvideo.h @@ -65,8 +65,6 @@ public: int GetClientWidth(); int GetClientHeight(); - int GetTrueHeight() { return GetHeight(); } - protected: int m_lock; bool m_isUpdatePending; diff --git a/src/posix/sdl/sdlglvideo.h b/src/posix/sdl/sdlglvideo.h index 0b6a46c12..d8ce9005d 100644 --- a/src/posix/sdl/sdlglvideo.h +++ b/src/posix/sdl/sdlglvideo.h @@ -61,9 +61,6 @@ public: int GetClientWidth(); int GetClientHeight(); -//[C] - int GetTrueHeight() { return GetHeight();} - protected: bool CanUpdate(); void SetGammaTable(WORD *tbl);