ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

openglsdl-graphics.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/scummsys.h"
00024 
00025 #if defined(SDL_BACKEND)
00026 
00027 #include "backends/graphics/openglsdl/openglsdl-graphics.h"
00028 
00029 #include "backends/events/sdl/resvm-sdl-events.h"
00030 #include "common/config-manager.h"
00031 #include "common/file.h"
00032 #include "engines/engine.h"
00033 #include "graphics/pixelbuffer.h"
00034 #include "graphics/opengl/context.h"
00035 #include "graphics/opengl/framebuffer.h"
00036 #include "graphics/opengl/surfacerenderer.h"
00037 #include "graphics/opengl/system_headers.h"
00038 #include "graphics/opengl/texture.h"
00039 #include "graphics/opengl/tiledsurface.h"
00040 
00041 #ifdef USE_PNG
00042 #include "image/png.h"
00043 #else
00044 #include "image/bmp.h"
00045 #endif
00046 
00047 OpenGLSdlGraphicsManager::OpenGLSdlGraphicsManager(SdlEventSource *eventSource, SdlWindow *window, const Capabilities &capabilities)
00048     : ResVmSdlGraphicsManager(eventSource, window),
00049 #if SDL_VERSION_ATLEAST(2, 0, 0)
00050     _glContext(nullptr),
00051 #endif
00052     _capabilities(capabilities),
00053     _overlayVisible(false),
00054     _overlayScreen(nullptr),
00055     _overlayBackground(nullptr),
00056     _gameRect(),
00057     _fullscreen(false),
00058     _lockAspectRatio(true),
00059     _frameBuffer(nullptr),
00060     _surfaceRenderer(nullptr),
00061     _engineRequestedWidth(0),
00062     _engineRequestedHeight(0) {
00063     ConfMan.registerDefault("antialiasing", 0);
00064     ConfMan.registerDefault("aspect_ratio", true);
00065 
00066     _sideTextures[0] = _sideTextures[1] = nullptr;
00067 
00068     // Don't start at zero so that the value is never the same as the surface graphics manager
00069     _screenChangeCount = 1 << (sizeof(int) * 8 - 2);
00070 }
00071 
00072 OpenGLSdlGraphicsManager::~OpenGLSdlGraphicsManager() {
00073     closeOverlay();
00074 #if SDL_VERSION_ATLEAST(2, 0, 0)
00075     deinitializeRenderer();
00076 #endif
00077 }
00078 
00079 bool OpenGLSdlGraphicsManager::hasFeature(OSystem::Feature f) const {
00080     return
00081         (f == OSystem::kFeatureFullscreenMode) ||
00082         (f == OSystem::kFeatureOpenGL) ||
00083 #if SDL_VERSION_ATLEAST(2, 0, 0)
00084         (f == OSystem::kFeatureFullscreenToggleKeepsContext) ||
00085 #endif
00086         (f == OSystem::kFeatureVSync) ||
00087         (f == OSystem::kFeatureAspectRatioCorrection) ||
00088         (f == OSystem::kFeatureOverlaySupportsAlpha && _overlayFormat.aBits() > 3);
00089 }
00090 
00091 bool OpenGLSdlGraphicsManager::getFeatureState(OSystem::Feature f) const {
00092     switch (f) {
00093         case OSystem::kFeatureVSync:
00094             return isVSyncEnabled();
00095         case OSystem::kFeatureFullscreenMode:
00096             return _fullscreen;
00097         case OSystem::kFeatureAspectRatioCorrection:
00098             return _lockAspectRatio;
00099         default:
00100             return false;
00101     }
00102 }
00103 
00104 void OpenGLSdlGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
00105     switch (f) {
00106         case OSystem::kFeatureFullscreenMode:
00107             if (_fullscreen != enable) {
00108                 _fullscreen = enable;
00109                 createOrUpdateScreen();
00110             }
00111             break;
00112         case OSystem::kFeatureAspectRatioCorrection:
00113             _lockAspectRatio = enable;
00114             break;
00115         default:
00116             break;
00117     }
00118 }
00119 
00120 void OpenGLSdlGraphicsManager::setupScreen(uint gameWidth, uint gameHeight, bool fullscreen, bool accel3d) {
00121     assert(accel3d);
00122     closeOverlay();
00123 
00124     _engineRequestedWidth = gameWidth;
00125     _engineRequestedHeight = gameHeight;
00126     _antialiasing = ConfMan.getInt("antialiasing");
00127     _fullscreen = fullscreen;
00128     _lockAspectRatio = ConfMan.getBool("aspect_ratio");
00129     _vsync = ConfMan.getBool("vsync");
00130 
00131 #if SDL_VERSION_ATLEAST(2, 0, 0)
00132     bool needsWindowReset = false;
00133     if (_window->getSDLWindow()) {
00134         // The anti-aliasing setting cannot be changed without recreating the window.
00135         // So check if the window needs to be recreated.
00136 
00137         int currentSamples = 0;
00138         SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &currentSamples);
00139 
00140         // When rendering to a framebuffer, MSAA is enabled on that framebuffer, not on the screen
00141         int targetSamples = shouldRenderToFramebuffer() ? 0 : _antialiasing;
00142 
00143         if (currentSamples != targetSamples) {
00144             needsWindowReset = true;
00145         }
00146     }
00147 
00148     // Clear the GL context when going from / to the launcher
00149     SDL_GL_DeleteContext(_glContext);
00150     _glContext = nullptr;
00151 
00152     if (needsWindowReset) {
00153         _window->destroyWindow();
00154     }
00155 #endif
00156 
00157     createOrUpdateScreen();
00158 
00159     int glflag;
00160     const GLubyte *str;
00161 
00162     str = glGetString(GL_VENDOR);
00163     debug("INFO: OpenGL Vendor: %s", str);
00164     str = glGetString(GL_RENDERER);
00165     debug("INFO: OpenGL Renderer: %s", str);
00166     str = glGetString(GL_VERSION);
00167     debug("INFO: OpenGL Version: %s", str);
00168     SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &glflag);
00169     debug("INFO: OpenGL Red bits: %d", glflag);
00170     SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &glflag);
00171     debug("INFO: OpenGL Green bits: %d", glflag);
00172     SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &glflag);
00173     debug("INFO: OpenGL Blue bits: %d", glflag);
00174     SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &glflag);
00175     debug("INFO: OpenGL Alpha bits: %d", glflag);
00176     SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &glflag);
00177     debug("INFO: OpenGL Z buffer depth bits: %d", glflag);
00178     SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &glflag);
00179     debug("INFO: OpenGL Double Buffer: %d", glflag);
00180     SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &glflag);
00181     debug("INFO: OpenGL Stencil buffer bits: %d", glflag);
00182 #ifdef USE_GLEW
00183     debug("INFO: GLEW Version: %s", glewGetString(GLEW_VERSION));
00184 #endif
00185 #ifdef USE_OPENGL_SHADERS
00186     debug("INFO: GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
00187 #endif
00188 }
00189 
00190 void OpenGLSdlGraphicsManager::createOrUpdateScreen() {
00191     closeOverlay();
00192 
00193     // If the game can't adapt to any resolution, render it to a framebuffer
00194     // so it can be scaled to fill the available space.
00195     bool engineSupportsArbitraryResolutions = !g_engine || g_engine->hasFeature(Engine::kSupportsArbitraryResolutions);
00196     bool renderToFrameBuffer = shouldRenderToFramebuffer();
00197 
00198     // Choose the effective window size or fullscreen mode
00199     uint effectiveWidth;
00200     uint effectiveHeight;
00201     if (_fullscreen && (engineSupportsArbitraryResolutions || renderToFrameBuffer)) {
00202         Common::Rect fullscreenResolution = getPreferredFullscreenResolution();
00203         effectiveWidth = fullscreenResolution.width();
00204         effectiveHeight = fullscreenResolution.height();
00205     } else {
00206         effectiveWidth = _engineRequestedWidth;
00207         effectiveHeight = _engineRequestedHeight;
00208     }
00209 
00210     if (!createOrUpdateGLContext(_engineRequestedWidth, _engineRequestedHeight,
00211                                  effectiveWidth, effectiveHeight,
00212                                  renderToFrameBuffer, engineSupportsArbitraryResolutions)) {
00213         warning("SDL Error: %s", SDL_GetError());
00214         g_system->quit();
00215     }
00216 
00217 #ifdef USE_GLEW
00218     GLenum err = glewInit();
00219 #ifdef GLEW_ERROR_NO_GLX_DISPLAY
00220     if (err == GLEW_ERROR_NO_GLX_DISPLAY) {
00221         // Wayland: https://github.com/nigels-com/glew/issues/172
00222     } else
00223 #endif
00224     if (err != GLEW_OK) {
00225         warning("Error: %s", glewGetErrorString(err));
00226         g_system->quit();
00227     }
00228 #endif
00229 
00230 #if SDL_VERSION_ATLEAST(2, 0, 1)
00231     int obtainedWidth = 0, obtainedHeight = 0;
00232     SDL_GL_GetDrawableSize(_window->getSDLWindow(), &obtainedWidth, &obtainedHeight);
00233 #else
00234     int obtainedWidth = effectiveWidth;
00235     int obtainedHeight = effectiveHeight;
00236 #endif
00237 
00238     // Compute the rectangle where to draw the game inside the effective screen
00239     _gameRect = computeGameRect(renderToFrameBuffer, _engineRequestedWidth, _engineRequestedHeight,
00240                                 obtainedWidth, obtainedHeight);
00241 
00242     initializeOpenGLContext();
00243     _surfaceRenderer = OpenGL::createBestSurfaceRenderer();
00244 
00245     _overlayFormat = OpenGL::Texture::getRGBAPixelFormat();
00246     _overlayScreen = new OpenGL::TiledSurface(obtainedWidth, obtainedHeight, _overlayFormat);
00247 
00248     _screenFormat = _overlayFormat;
00249 
00250     _screenChangeCount++;
00251 
00252     dynamic_cast<ResVmSdlEventSource *>(_eventSource)->resetKeyboardEmulation(obtainedWidth - 1, obtainedHeight - 1);
00253 
00254 #if !defined(AMIGAOS)
00255     if (renderToFrameBuffer) {
00256         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00257         _frameBuffer = createFramebuffer(_engineRequestedWidth, _engineRequestedHeight);
00258         _frameBuffer->attach();
00259     }
00260 #endif
00261 }
00262 
00263 Math::Rect2d OpenGLSdlGraphicsManager::computeGameRect(bool renderToFrameBuffer, uint gameWidth, uint gameHeight,
00264                                                       uint screenWidth, uint screenHeight) {
00265     if (renderToFrameBuffer) {
00266         if (_lockAspectRatio) {
00267             // The game is scaled to fit the screen, keeping the same aspect ratio
00268             float scale = MIN(screenHeight / float(gameHeight), screenWidth / float(gameWidth));
00269             float scaledW = scale * (gameWidth / float(screenWidth));
00270             float scaledH = scale * (gameHeight / float(screenHeight));
00271             return Math::Rect2d(
00272                     Math::Vector2d(0.5 - (0.5 * scaledW), 0.5 - (0.5 * scaledH)),
00273                     Math::Vector2d(0.5 + (0.5 * scaledW), 0.5 + (0.5 * scaledH))
00274             );
00275         } else {
00276             // The game occupies the whole screen
00277             return Math::Rect2d(Math::Vector2d(0, 0), Math::Vector2d(1, 1));
00278         }
00279     } else {
00280         return Math::Rect2d(Math::Vector2d(0, 0), Math::Vector2d(1, 1));
00281     }
00282 }
00283 
00284 void OpenGLSdlGraphicsManager::notifyResize(const int width, const int height) {
00285 #if SDL_VERSION_ATLEAST(2, 0, 0)
00286     // Get the updated size directly from SDL, in case there are multiple
00287     // resize events in the message queue.
00288     int newWidth = 0, newHeight = 0;
00289     SDL_GL_GetDrawableSize(_window->getSDLWindow(), &newWidth, &newHeight);
00290 
00291     if (newWidth == _overlayScreen->getWidth() && newHeight == _overlayScreen->getHeight()) {
00292         return; // nothing to do
00293     }
00294 
00295     // Compute the rectangle where to draw the game inside the effective screen
00296     _gameRect = computeGameRect(_frameBuffer != nullptr,
00297                                 _engineRequestedWidth, _engineRequestedHeight,
00298                                 newWidth, newHeight);
00299 
00300     // Update the overlay
00301     delete _overlayScreen;
00302     _overlayScreen = new OpenGL::TiledSurface(newWidth, newHeight, _overlayFormat);
00303 
00304     // Clear the overlay background so it is not displayed distorted while resizing
00305     delete _overlayBackground;
00306     _overlayBackground = nullptr;
00307 
00308     _screenChangeCount++;
00309 
00310     dynamic_cast<ResVmSdlEventSource *>(_eventSource)->resetKeyboardEmulation(newWidth - 1, newHeight- 1);
00311 #endif
00312 }
00313 
00314 Graphics::PixelBuffer OpenGLSdlGraphicsManager::getScreenPixelBuffer() {
00315     error("Direct screen buffer access is not allowed when using OpenGL");
00316 }
00317 
00318 void OpenGLSdlGraphicsManager::initializeOpenGLContext() const {
00319     OpenGL::ContextType type;
00320 
00321 #ifdef USE_GLES2
00322     type = OpenGL::kContextGLES2;
00323 #else
00324     type = OpenGL::kContextGL;
00325 #endif
00326 
00327     OpenGLContext.initialize(type);
00328 
00329 #if SDL_VERSION_ATLEAST(2, 0, 0)
00330     if (SDL_GL_SetSwapInterval(_vsync ? 1 : 0)) {
00331         warning("Unable to %s VSync: %s", _vsync ? "enable" : "disable", SDL_GetError());
00332     }
00333 #endif
00334 }
00335 
00336 OpenGLSdlGraphicsManager::OpenGLPixelFormat::OpenGLPixelFormat(uint screenBytesPerPixel, uint red, uint blue, uint green, uint alpha, int samples) :
00337         bytesPerPixel(screenBytesPerPixel),
00338         redSize(red),
00339         blueSize(blue),
00340         greenSize(green),
00341         alphaSize(alpha),
00342         multisampleSamples(samples) {
00343 
00344 }
00345 
00346 bool OpenGLSdlGraphicsManager::createOrUpdateGLContext(uint gameWidth, uint gameHeight,
00347                                                        uint effectiveWidth, uint effectiveHeight,
00348                                                        bool renderToFramebuffer,
00349                                                        bool engineSupportsArbitraryResolutions) {
00350     // Build a list of OpenGL pixel formats usable by ResidualVM
00351     Common::Array<OpenGLPixelFormat> pixelFormats;
00352     if (_antialiasing > 0 && !renderToFramebuffer) {
00353         // Don't enable screen level multisampling when rendering to a framebuffer
00354         pixelFormats.push_back(OpenGLPixelFormat(32, 8, 8, 8, 8, _antialiasing));
00355         pixelFormats.push_back(OpenGLPixelFormat(16, 5, 5, 5, 1, _antialiasing));
00356         pixelFormats.push_back(OpenGLPixelFormat(16, 5, 6, 5, 0, _antialiasing));
00357     }
00358     pixelFormats.push_back(OpenGLPixelFormat(32, 8, 8, 8, 8, 0));
00359     pixelFormats.push_back(OpenGLPixelFormat(16, 5, 5, 5, 1, 0));
00360     pixelFormats.push_back(OpenGLPixelFormat(16, 5, 6, 5, 0, 0));
00361 
00362     // Unfortunatly, SDL does not provide a list of valid pixel formats
00363     // for the current OpenGL implementation and hardware.
00364     // SDL may not be able to create a screen with the preferred pixel format.
00365     // Try all the pixel formats in the list until SDL returns a valid screen.
00366     Common::Array<OpenGLPixelFormat>::const_iterator it = pixelFormats.begin();
00367     for (; it != pixelFormats.end(); it++) {
00368         SDL_GL_SetAttribute(SDL_GL_RED_SIZE, it->redSize);
00369         SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, it->greenSize);
00370         SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, it->blueSize);
00371         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, it->alphaSize);
00372         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
00373         SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
00374         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
00375         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, it->multisampleSamples > 0);
00376         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, it->multisampleSamples);
00377 #if !SDL_VERSION_ATLEAST(2, 0, 0)
00378         SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, _vsync ? 1 : 0);
00379 #endif
00380 #if SDL_VERSION_ATLEAST(2, 0, 0)
00381 #ifdef USE_GLES2
00382         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
00383         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
00384         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
00385 #else
00386         // AmigaOS4's OpenGL implementation is close to 1.3. Until that changes we need
00387         // to use 1.3 as version or residualvm will cease working at all on that platform.
00388         // Profile Mask has to be 0 as well.
00389         // This will be revised and removed once AmigaOS4 supports 2.x or OpenGLES2.
00390         #ifdef __amigaos4__
00391             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
00392             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
00393         #else
00394             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
00395             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
00396         #endif
00397 #endif
00398 #endif
00399 
00400 #if SDL_VERSION_ATLEAST(2, 0, 0)
00401         uint32 sdlflags = SDL_WINDOW_OPENGL;
00402 
00403         if (renderToFramebuffer || engineSupportsArbitraryResolutions) {
00404             sdlflags |= SDL_WINDOW_RESIZABLE;
00405         }
00406 
00407         if (_fullscreen) {
00408             // On Linux/X11, when toggling to fullscreen, the window manager saves
00409             // the window size to be able to restore it when going back to windowed mode.
00410             // If the user configured ResidualVM to start in fullscreen mode, we first
00411             // create a window and then toggle it to fullscreen to give the window manager
00412             // a chance to save the window size. That way if the user switches back
00413             // to windowed mode, the window manager has a window size to apply instead
00414             // of leaving the window at the fullscreen resolution size.
00415             if (!_window->getSDLWindow()) {
00416                 _window->createOrUpdateWindow(gameWidth, gameHeight, sdlflags);
00417             }
00418 
00419             sdlflags |= SDL_WINDOW_FULLSCREEN;
00420         }
00421 
00422         if (_window->createOrUpdateWindow(effectiveWidth, effectiveHeight, sdlflags)) {
00423             // Get the current GL context from SDL in case the previous one
00424             // was destroyed because the window was recreated.
00425             _glContext = SDL_GL_GetCurrentContext();
00426             if (!_glContext) {
00427                 _glContext = SDL_GL_CreateContext(_window->getSDLWindow());
00428                 if (_glContext) {
00429                     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00430                 }
00431             }
00432 
00433             if (_glContext) {
00434                 assert(SDL_GL_GetCurrentWindow() == _window->getSDLWindow());
00435                 break;
00436             }
00437         }
00438 
00439         _window->destroyWindow();
00440 #else
00441         uint32 sdlflags = SDL_OPENGL;
00442         if (_fullscreen)
00443             sdlflags |= SDL_FULLSCREEN;
00444 
00445         SDL_Surface *screen = SDL_SetVideoMode(effectiveWidth, effectiveHeight, it->bytesPerPixel, sdlflags);
00446         if (screen) {
00447             break;
00448         }
00449 #endif
00450     }
00451 
00452     // Display a warning if the effective pixel format is not the preferred one
00453     if (it != pixelFormats.begin() && it != pixelFormats.end()) {
00454         bool wantsAA = pixelFormats.front().multisampleSamples > 0;
00455         bool gotAA = it->multisampleSamples > 0;
00456 
00457         warning("Couldn't create a %d-bit visual%s, using to %d-bit%s instead",
00458                 pixelFormats.front().bytesPerPixel,
00459                 wantsAA && !gotAA ? " with AA" : "",
00460                 it->bytesPerPixel,
00461                 wantsAA && !gotAA ? " without AA" : "");
00462     }
00463 
00464     return it != pixelFormats.end();
00465 }
00466 
00467 bool OpenGLSdlGraphicsManager::shouldRenderToFramebuffer() const {
00468     bool engineSupportsArbitraryResolutions = !g_engine || g_engine->hasFeature(Engine::kSupportsArbitraryResolutions);
00469     return !engineSupportsArbitraryResolutions && _capabilities.openGLFrameBuffer;
00470 }
00471 
00472 bool OpenGLSdlGraphicsManager::isVSyncEnabled() const {
00473 #if SDL_VERSION_ATLEAST(2, 0, 0)
00474     return SDL_GL_GetSwapInterval() != 0;
00475 #else
00476     int swapControl = 0;
00477     SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &swapControl);
00478     return swapControl != 0;
00479 #endif
00480 }
00481 
00482 void OpenGLSdlGraphicsManager::drawOverlay() {
00483     glViewport(0, 0, _overlayScreen->getWidth(), _overlayScreen->getHeight());
00484     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00485 
00486     _surfaceRenderer->prepareState();
00487 
00488     if (_overlayBackground) {
00489         _overlayBackground->draw(_surfaceRenderer);
00490     }
00491 
00492     _surfaceRenderer->enableAlphaBlending(true);
00493     _surfaceRenderer->setFlipY(true);
00494     _overlayScreen->draw(_surfaceRenderer);
00495 
00496     _surfaceRenderer->restorePreviousState();
00497 }
00498 
00499 void OpenGLSdlGraphicsManager::drawSideTextures() {
00500     if (_fullscreen && _lockAspectRatio) {
00501         _surfaceRenderer->setFlipY(true);
00502 
00503         const Math::Vector2d nudge(1.0 / float(_overlayScreen->getWidth()), 0);
00504         if (_sideTextures[0] != nullptr) {
00505             float left = _gameRect.getBottomLeft().getX() - (float(_overlayScreen->getHeight()) / float(_sideTextures[0]->getHeight())) * _sideTextures[0]->getWidth() / float(_overlayScreen->getWidth());
00506             Math::Rect2d leftRect(Math::Vector2d(left, 0.0), _gameRect.getBottomLeft() + nudge);
00507             _surfaceRenderer->render(_sideTextures[0], leftRect);
00508         }
00509 
00510         if (_sideTextures[1] != nullptr) {
00511             float right = _gameRect.getTopRight().getX() + (float(_overlayScreen->getHeight()) / float(_sideTextures[1]->getHeight())) * _sideTextures[1]->getWidth() / float(_overlayScreen->getWidth());
00512             Math::Rect2d rightRect(_gameRect.getTopRight() - nudge, Math::Vector2d(right, 1.0));
00513             _surfaceRenderer->render(_sideTextures[1], rightRect);
00514         }
00515 
00516         _surfaceRenderer->setFlipY(false);
00517     }
00518 }
00519 
00520 #ifndef AMIGAOS
00521 OpenGL::FrameBuffer *OpenGLSdlGraphicsManager::createFramebuffer(uint width, uint height) {
00522 #if !defined(USE_GLES2)
00523     if (_antialiasing && OpenGLContext.framebufferObjectMultisampleSupported) {
00524         return new OpenGL::MultiSampleFrameBuffer(width, height, _antialiasing);
00525     } else
00526 #endif
00527     {
00528         return new OpenGL::FrameBuffer(width, height);
00529     }
00530 }
00531 #endif // AMIGAOS
00532 
00533 void OpenGLSdlGraphicsManager::updateScreen() {
00534     if (_frameBuffer) {
00535         _frameBuffer->detach();
00536         glViewport(0, 0, _overlayScreen->getWidth(), _overlayScreen->getHeight());
00537         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
00538         _surfaceRenderer->prepareState();
00539         drawSideTextures();
00540         _surfaceRenderer->render(_frameBuffer, _gameRect);
00541         _surfaceRenderer->restorePreviousState();
00542     }
00543 
00544     if (_overlayVisible) {
00545         _overlayScreen->update();
00546 
00547         if (_overlayBackground) {
00548             _overlayBackground->update();
00549         }
00550 
00551         drawOverlay();
00552     }
00553 
00554 #if SDL_VERSION_ATLEAST(2, 0, 0)
00555     SDL_GL_SwapWindow(_window->getSDLWindow());
00556 #else
00557     SDL_GL_SwapBuffers();
00558 #endif
00559 
00560     if (_frameBuffer) {
00561         _frameBuffer->attach();
00562     }
00563 }
00564 
00565 int16 OpenGLSdlGraphicsManager::getHeight() const {
00566     // ResidualVM specific
00567     if (_frameBuffer)
00568         return _frameBuffer->getHeight();
00569     else
00570         return _overlayScreen->getHeight();
00571 }
00572 
00573 int16 OpenGLSdlGraphicsManager::getWidth() const {
00574     // ResidualVM specific
00575     if (_frameBuffer)
00576         return _frameBuffer->getWidth();
00577     else
00578         return _overlayScreen->getWidth();
00579 }
00580 
00581 #pragma mark -
00582 #pragma mark --- Overlays ---
00583 #pragma mark -
00584 
00585 void OpenGLSdlGraphicsManager::suggestSideTextures(Graphics::Surface *left, Graphics::Surface *right) {
00586     delete _sideTextures[0];
00587     _sideTextures[0] = nullptr;
00588     delete _sideTextures[1];
00589     _sideTextures[1] = nullptr;
00590     if (left) {
00591         _sideTextures[0] = new OpenGL::Texture(*left);
00592     }
00593     if (right) {
00594         _sideTextures[1] = new OpenGL::Texture(*right);
00595     }
00596 }
00597 
00598 void OpenGLSdlGraphicsManager::showOverlay() {
00599     if (_overlayVisible) {
00600         return;
00601     }
00602     _overlayVisible = true;
00603 
00604     delete _overlayBackground;
00605     _overlayBackground = nullptr;
00606 
00607     if (g_engine) {
00608         if (_frameBuffer)
00609             _frameBuffer->detach();
00610         // If there is a game running capture the screen, so that it can be shown "below" the overlay.
00611         _overlayBackground = new OpenGL::TiledSurface(_overlayScreen->getWidth(), _overlayScreen->getHeight(), _overlayFormat);
00612         Graphics::Surface *background = _overlayBackground->getBackingSurface();
00613         glReadPixels(0, 0, background->w, background->h, GL_RGBA, GL_UNSIGNED_BYTE, background->getPixels());
00614         if (_frameBuffer)
00615             _frameBuffer->attach();
00616     }
00617 }
00618 
00619 void OpenGLSdlGraphicsManager::hideOverlay() {
00620     if (!_overlayVisible) {
00621         return;
00622     }
00623     _overlayVisible = false;
00624 
00625     delete _overlayBackground;
00626     _overlayBackground = nullptr;
00627 }
00628 
00629 void OpenGLSdlGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
00630     _overlayScreen->copyRectToSurface(buf, pitch, x, y, w, h);
00631 }
00632 
00633 void OpenGLSdlGraphicsManager::clearOverlay() {
00634     _overlayScreen->fill(0);
00635 }
00636 
00637 void OpenGLSdlGraphicsManager::grabOverlay(void *buf, int pitch) const {
00638     const Graphics::Surface *overlayData = _overlayScreen->getBackingSurface();
00639 
00640     const byte *src = (const byte *)overlayData->getPixels();
00641     byte *dst = (byte *)buf;
00642 
00643     for (uint h = overlayData->h; h > 0; --h) {
00644         memcpy(dst, src, overlayData->w * overlayData->format.bytesPerPixel);
00645         dst += pitch;
00646         src += overlayData->pitch;
00647     }
00648 }
00649 
00650 void OpenGLSdlGraphicsManager::closeOverlay() {
00651     delete _sideTextures[0];
00652     delete _sideTextures[1];
00653     _sideTextures[0] = _sideTextures[1] = nullptr;
00654 
00655     if (_overlayScreen) {
00656         delete _overlayScreen;
00657         _overlayScreen = nullptr;
00658     }
00659 
00660     delete _surfaceRenderer;
00661     _surfaceRenderer = nullptr;
00662 
00663     delete _frameBuffer;
00664     _frameBuffer = nullptr;
00665 
00666     OpenGL::Context::destroy();
00667 }
00668 
00669 int16 OpenGLSdlGraphicsManager::getOverlayHeight() const {
00670     return _overlayScreen->getHeight();
00671 }
00672 
00673 int16 OpenGLSdlGraphicsManager::getOverlayWidth() const {
00674     return _overlayScreen->getWidth();
00675 }
00676 
00677 void OpenGLSdlGraphicsManager::warpMouse(int x, int y) {
00678     //ResidualVM specific
00679     if (_frameBuffer) {
00680         // Scale from game coordinates to screen coordinates
00681         x = (x * _gameRect.getWidth() * _overlayScreen->getWidth()) / _frameBuffer->getWidth();
00682         y = (y * _gameRect.getHeight() * _overlayScreen->getHeight()) / _frameBuffer->getHeight();
00683 
00684         x += _gameRect.getTopLeft().getX() * _overlayScreen->getWidth();
00685         y += _gameRect.getTopLeft().getY() * _overlayScreen->getHeight();
00686     }
00687 
00688     _window->warpMouseInWindow(x, y);
00689 }
00690 
00691 void OpenGLSdlGraphicsManager::transformMouseCoordinates(Common::Point &point) {
00692     if (_overlayVisible || !_frameBuffer)
00693         return;
00694 
00695     // Scale from screen coordinates to game coordinates
00696     point.x -= _gameRect.getTopLeft().getX() * _overlayScreen->getWidth();
00697     point.y -= _gameRect.getTopLeft().getY() * _overlayScreen->getHeight();
00698 
00699     point.x = (point.x * _frameBuffer->getWidth())  / (_gameRect.getWidth() * _overlayScreen->getWidth());
00700     point.y = (point.y * _frameBuffer->getHeight()) / (_gameRect.getHeight() * _overlayScreen->getHeight());
00701 
00702     // Make sure we only supply valid coordinates.
00703     point.x = CLIP<int16>(point.x, 0, _frameBuffer->getWidth() - 1);
00704     point.y = CLIP<int16>(point.y, 0, _frameBuffer->getHeight() - 1);
00705 }
00706 
00707 #if SDL_VERSION_ATLEAST(2, 0, 0)
00708 void OpenGLSdlGraphicsManager::deinitializeRenderer() {
00709     SDL_GL_DeleteContext(_glContext);
00710     _glContext = nullptr;
00711 }
00712 #endif // SDL_VERSION_ATLEAST(2, 0, 0)
00713 
00714 bool OpenGLSdlGraphicsManager::saveScreenshot(const Common::String &filename) const {
00715     // Largely based on the implementation from ScummVM
00716     uint width = _overlayScreen->getWidth();
00717     uint height = _overlayScreen->getHeight();
00718 
00719     uint linePaddingSize = width % 4;
00720     uint lineSize = width * 3 + linePaddingSize;
00721 
00722     Common::DumpFile out;
00723     if (!out.open(filename)) {
00724         return false;
00725     }
00726 
00727     Common::Array<uint8> pixels;
00728     pixels.resize(lineSize * height);
00729 
00730     if (_frameBuffer) {
00731         _frameBuffer->detach();
00732     }
00733     glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels.front());
00734     if (_frameBuffer) {
00735         _frameBuffer->attach();
00736     }
00737 
00738 #ifdef SCUMM_LITTLE_ENDIAN
00739     const Graphics::PixelFormat format(3, 8, 8, 8, 0, 0, 8, 16, 0);
00740 #else
00741     const Graphics::PixelFormat format(3, 8, 8, 8, 0, 16, 8, 0, 0);
00742 #endif
00743     Graphics::Surface data;
00744     data.init(width, height, lineSize, &pixels.front(), format);
00745 #ifdef USE_PNG
00746     return Image::writePNG(out, data, true);
00747 #else
00748     return Image::writeBMP(out, data, true);
00749 #endif
00750 }
00751 
00752 #endif


Generated on Sat May 23 2020 05:00:28 for ResidualVM by doxygen 1.7.1
curved edge   curved edge