#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers /***************************************************************************** * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * OpenRCT2 is the work of many authors, a full list can be found in contributors.md * For more information, visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * A full copy of the GNU General Public License can be found in licence.txt *****************************************************************************/ #pragma endregion #ifdef DISABLE_OPENGL #include "../../IDrawingEngine.h" IDrawingEngine * DrawingEngineFactory::CreateOpenGL() { return nullptr; } #else #include #include #include #include "GLSLTypes.h" #include "OpenGLAPI.h" #include "OpenGLFramebuffer.h" #include "CopyFramebufferShader.h" #include "DrawImageShader.h" #include "DrawImageMaskedShader.h" #include "DrawLineShader.h" #include "FillRectShader.h" #include "SwapFramebuffer.h" #include "TextureCache.h" #include "../../../core/Console.hpp" #include "../../../core/Exception.hpp" #include "../../../core/Math.hpp" #include "../../../core/Memory.hpp" #include "../../IDrawingContext.h" #include "../../IDrawingEngine.h" #include "../../Rain.h" extern "C" { #include "../../../config.h" #include "../../../interface/screenshot.h" #include "../../../interface/window.h" #include "../../../intro.h" #include "../../drawing.h" } static const vec3f TransparentColourTable[144 - 44] = { { 0.7f, 0.8f, 0.8f }, // 44 { 0.7f, 0.8f, 0.8f }, { 0.3f, 0.4f, 0.4f }, { 0.2f, 0.3f, 0.3f }, { 0.1f, 0.2f, 0.2f }, { 0.4f, 0.5f, 0.5f }, { 0.3f, 0.4f, 0.4f }, { 0.4f, 0.5f, 0.5f }, { 0.4f, 0.5f, 0.5f }, { 0.3f, 0.4f, 0.4f }, { 0.6f, 0.7f, 0.7f }, { 0.3f, 0.5f, 0.9f }, { 0.1f, 0.3f, 0.8f }, { 0.5f, 0.7f, 0.9f }, { 0.6f, 0.2f, 0.2f }, { 0.5f, 0.1f, 0.1f }, { 0.8f, 0.4f, 0.4f }, { 0.3f, 0.5f, 0.4f }, { 0.2f, 0.4f, 0.2f }, { 0.5f, 0.7f, 0.5f }, { 0.5f, 0.5f, 0.7f }, { 0.3f, 0.3f, 0.5f }, { 0.6f, 0.6f, 0.8f }, { 0.5f, 0.5f, 0.2f }, { 0.4f, 0.4f, 0.1f }, { 0.7f, 0.7f, 0.4f }, { 0.7f, 0.5f, 0.3f }, { 0.6f, 0.4f, 0.2f }, { 0.8f, 0.7f, 0.4f }, { 0.8f, 0.7f, 0.1f }, { 0.7f, 0.4f, 0.0f }, { 1.0f, 0.9f, 0.2f }, { 0.4f, 0.6f, 0.2f }, { 0.3f, 0.4f, 0.2f }, { 0.5f, 0.7f, 0.3f }, { 0.5f, 0.6f, 0.4f }, { 0.4f, 0.4f, 0.3f }, { 0.7f, 0.8f, 0.5f }, { 0.3f, 0.7f, 0.2f }, { 0.2f, 0.6f, 0.0f }, { 0.4f, 0.8f, 0.3f }, { 0.8f, 0.5f, 0.4f }, { 0.7f, 0.4f, 0.3f }, { 0.9f, 0.7f, 0.5f }, { 0.5f, 0.3f, 0.7f }, { 0.4f, 0.2f, 0.6f }, { 0.7f, 0.5f, 0.8f }, { 0.9f, 0.0f, 0.0f }, { 0.7f, 0.0f, 0.0f }, { 1.0f, 0.3f, 0.3f }, { 1.0f, 0.4f, 0.1f }, { 0.9f, 0.3f, 0.0f }, { 1.0f, 0.6f, 0.3f }, { 0.2f, 0.6f, 0.6f }, { 0.0f, 0.4f, 0.4f }, { 0.4f, 0.7f, 0.7f }, { 0.9f, 0.2f, 0.6f }, { 0.6f, 0.1f, 0.4f }, { 1.0f, 0.5f, 0.7f }, { 0.6f, 0.5f, 0.4f }, { 0.4f, 0.3f, 0.2f }, { 0.7f, 0.7f, 0.6f }, { 0.9f, 0.6f, 0.6f }, { 0.8f, 0.5f, 0.5f }, { 1.0f, 0.7f, 0.7f }, { 0.7f, 0.8f, 0.8f }, { 0.5f, 0.6f, 0.6f }, { 0.9f, 1.0f, 1.0f }, { 0.2f, 0.3f, 0.3f }, { 0.4f, 0.5f, 0.5f }, { 0.7f, 0.8f, 0.8f }, { 0.2f, 0.3f, 0.5f }, { 0.5f, 0.5f, 0.7f }, { 0.5f, 0.3f, 0.7f }, { 0.1f, 0.3f, 0.7f }, { 0.3f, 0.5f, 0.9f }, { 0.6f, 0.8f, 1.0f }, { 0.2f, 0.6f, 0.6f }, { 0.5f, 0.8f, 0.8f }, { 0.1f, 0.5f, 0.0f }, { 0.3f, 0.5f, 0.4f }, { 0.4f, 0.6f, 0.2f }, { 0.3f, 0.7f, 0.2f }, { 0.5f, 0.6f, 0.4f }, { 0.5f, 0.5f, 0.2f }, { 1.0f, 0.9f, 0.2f }, { 0.8f, 0.7f, 0.1f }, { 0.6f, 0.3f, 0.0f }, { 1.0f, 0.4f, 0.1f }, { 0.7f, 0.3f, 0.0f }, { 0.7f, 0.5f, 0.3f }, { 0.5f, 0.3f, 0.1f }, { 0.5f, 0.4f, 0.3f }, { 0.8f, 0.5f, 0.4f }, { 0.6f, 0.2f, 0.2f }, { 0.6f, 0.0f, 0.0f }, { 0.9f, 0.0f, 0.0f }, { 0.6f, 0.1f, 0.3f }, { 0.9f, 0.2f, 0.6f }, { 0.9f, 0.6f, 0.6f }, }; class OpenGLDrawingEngine; class OpenGLDrawingContext : public IDrawingContext { private: OpenGLDrawingEngine * _engine; rct_drawpixelinfo * _dpi; DrawImageShader * _drawImageShader = nullptr; DrawImageMaskedShader * _drawImageMaskedShader = nullptr; DrawLineShader * _drawLineShader = nullptr; FillRectShader * _fillRectShader = nullptr; TextureCache * _textureCache = nullptr; sint32 _offsetX; sint32 _offsetY; sint32 _clipLeft; sint32 _clipTop; sint32 _clipRight; sint32 _clipBottom; public: explicit OpenGLDrawingContext(OpenGLDrawingEngine * engine); ~OpenGLDrawingContext() override; IDrawingEngine * GetEngine() override; TextureCache * GetTextureCache() const { return _textureCache; } void Initialise(); void Resize(sint32 width, sint32 height); void Clear(uint32 colour) override; void FillRect(uint32 colour, sint32 x, sint32 y, sint32 w, sint32 h) override; void DrawLine(uint32 colour, sint32 x1, sint32 y1, sint32 x2, sint32 y2) override; void DrawSprite(uint32 image, sint32 x, sint32 y, uint32 tertiaryColour) override; void DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskImage, uint32 colourImage) override; void DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour) override; void DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * palette) override; void SetDPI(rct_drawpixelinfo * dpi); }; class OpenGLDrawingEngine : public IDrawingEngine { private: SDL_Window * _window = nullptr; SDL_GLContext _context; uint32 _width = 0; uint32 _height = 0; uint32 _pitch = 0; size_t _bitsSize = 0; uint8 * _bits = nullptr; rct_drawpixelinfo _bitsDPI = { 0 }; OpenGLDrawingContext * _drawingContext; CopyFramebufferShader * _copyFramebufferShader = nullptr; OpenGLFramebuffer * _screenFramebuffer = nullptr; SwapFramebuffer * _swapFramebuffer = nullptr; public: SDL_Color Palette[256]; vec4f GLPalette[256]; OpenGLDrawingEngine() { _drawingContext = new OpenGLDrawingContext(this); } ~OpenGLDrawingEngine() override { delete _copyFramebufferShader; delete _drawingContext; delete [] _bits; SDL_GL_DeleteContext(_context); } void Initialise(SDL_Window * window) override { _window = window; SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); _context = SDL_GL_CreateContext(_window); SDL_GL_MakeCurrent(_window, _context); if (!OpenGLAPI::Initialise()) { throw Exception("Unable to initialise OpenGL."); } _drawingContext->Initialise(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Do not draw the unseen side of the primitives glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); _copyFramebufferShader = new CopyFramebufferShader(); } void Resize(uint32 width, uint32 height) override { ConfigureBits(width, height, width); ConfigureCanvas(); _drawingContext->Resize(width, height); } void SetPalette(SDL_Color * palette) override { for (int i = 0; i < 256; i++) { SDL_Color colour = palette[i]; colour.a = i == 0 ? 0 : 255; Palette[i] = colour; GLPalette[i] = { colour.r / 255.0f, colour.g / 255.0f, colour.b / 255.0f, colour.a / 255.0f }; } _drawingContext->GetTextureCache() ->SetPalette(Palette); } void Invalidate(sint32 left, sint32 top, sint32 right, sint32 bottom) override { } void Draw() override { assert(_screenFramebuffer != nullptr); assert(_swapFramebuffer != nullptr); _swapFramebuffer->Bind(); if (gIntroState != INTRO_STATE_NONE) { intro_draw(&_bitsDPI); } else { window_update_all_viewports(); window_draw_all(&_bitsDPI, 0, 0, _width, _height); window_update_all(); gfx_draw_pickedup_peep(&_bitsDPI); _swapFramebuffer->SwapCopy(); rct2_draw(&_bitsDPI); } // Scale up to window _screenFramebuffer->Bind(); sint32 width = _screenFramebuffer->GetWidth(); sint32 height = _screenFramebuffer->GetHeight(); _copyFramebufferShader->Use(); _copyFramebufferShader->SetTexture(_swapFramebuffer->GetTargetFramebuffer() ->GetTexture()); _copyFramebufferShader->Draw(); Display(); } sint32 Screenshot() override { const OpenGLFramebuffer * framebuffer = _swapFramebuffer->GetTargetFramebuffer(); framebuffer->Bind(); void * pixels = framebuffer->GetPixels(); int result = screenshot_dump_png_32bpp(_width, _height, pixels); Memory::Free(pixels); return result; } void CopyRect(sint32 x, sint32 y, sint32 width, sint32 height, sint32 dx, sint32 dy) override { // Not applicable for this engine } IDrawingContext * GetDrawingContext(rct_drawpixelinfo * dpi) override { _drawingContext->SetDPI(dpi); return _drawingContext; } rct_drawpixelinfo * GetDrawingPixelInfo() override { return &_bitsDPI; } DRAWING_ENGINE_FLAGS GetFlags() override { return DEF_NONE; } void InvalidateImage(uint32 image) override { _drawingContext->GetTextureCache() ->InvalidateImage(image); } rct_drawpixelinfo * GetDPI() { return &_bitsDPI; } GLuint SwapCopyReturningSourceTexture() { _swapFramebuffer->SwapCopy(); return _swapFramebuffer->GetSourceTexture(); } private: void ConfigureBits(uint32 width, uint32 height, uint32 pitch) { size_t newBitsSize = pitch * height; uint8 * newBits = new uint8[newBitsSize]; if (_bits == nullptr) { Memory::Set(newBits, 0, newBitsSize); } else { if (_pitch == pitch) { Memory::Copy(newBits, _bits, Math::Min(_bitsSize, newBitsSize)); } else { uint8 * src = _bits; uint8 * dst = newBits; uint32 minWidth = Math::Min(_width, width); uint32 minHeight = Math::Min(_height, height); for (uint32 y = 0; y < minHeight; y++) { Memory::Copy(dst, src, minWidth); if (pitch - minWidth > 0) { Memory::Set(dst + minWidth, 0, pitch - minWidth); } src += _pitch; dst += pitch; } } delete [] _bits; } _bits = newBits; _bitsSize = newBitsSize; _width = width; _height = height; _pitch = pitch; rct_drawpixelinfo * dpi = &_bitsDPI; dpi->bits = _bits; dpi->x = 0; dpi->y = 0; dpi->width = width; dpi->height = height; dpi->pitch = _pitch - width; } void ConfigureCanvas() { // Re-create screen framebuffer delete _screenFramebuffer; _screenFramebuffer = new OpenGLFramebuffer(_window); // Re-create canvas framebuffer delete _swapFramebuffer; _swapFramebuffer = new SwapFramebuffer(_width, _height); _copyFramebufferShader->Use(); _copyFramebufferShader->SetScreenSize(_width, _height); _copyFramebufferShader->SetBounds(0, 0, _width, _height); _copyFramebufferShader->SetTextureCoordinates(0, 1, 1, 0); } void Display() { SDL_GL_SwapWindow(_window); } }; IDrawingEngine * DrawingEngineFactory::CreateOpenGL() { return new OpenGLDrawingEngine(); } OpenGLDrawingContext::OpenGLDrawingContext(OpenGLDrawingEngine * engine) { _engine = engine; _textureCache = new TextureCache(); } OpenGLDrawingContext::~OpenGLDrawingContext() { delete _drawImageShader; delete _drawImageMaskedShader; delete _drawLineShader; delete _fillRectShader; delete _textureCache; } IDrawingEngine * OpenGLDrawingContext::GetEngine() { return _engine; } void OpenGLDrawingContext::Initialise() { _drawImageShader = new DrawImageShader(); _drawImageMaskedShader = new DrawImageMaskedShader(); _drawLineShader = new DrawLineShader(); _fillRectShader = new FillRectShader(); } void OpenGLDrawingContext::Resize(sint32 width, sint32 height) { _drawImageShader->Use(); _drawImageShader->SetScreenSize(width, height); _drawImageMaskedShader->Use(); _drawImageMaskedShader->SetScreenSize(width, height); _drawLineShader->Use(); _drawLineShader->SetScreenSize(width, height); _fillRectShader->Use(); _fillRectShader->SetScreenSize(width, height); } void OpenGLDrawingContext::Clear(uint32 colour) { FillRect(colour, _clipLeft - _offsetX, _clipTop - _offsetY, _clipRight, _clipBottom); } void OpenGLDrawingContext::FillRect(uint32 colour, sint32 left, sint32 top, sint32 right, sint32 bottom) { left += _offsetX; top += _offsetY; right += _offsetX; bottom += _offsetY; vec4f paletteColour[2]; paletteColour[0] = _engine->GLPalette[(colour >> 0) & 0xFF]; paletteColour[1] = paletteColour[0]; if (colour & 0x1000000) { paletteColour[1].a = 0; _fillRectShader->Use(); _fillRectShader->SetFlags(0); } else if (colour & 0x2000000) { uint8 tableIndex = colour & 0xFF; if (tableIndex < 44) return; if (tableIndex >= 144) return; tableIndex -= 44; vec3f transformColour = TransparentColourTable[tableIndex]; paletteColour[0].r = transformColour.r; paletteColour[0].g = transformColour.g; paletteColour[0].b = transformColour.b; paletteColour[0].a = 1; paletteColour[1] = paletteColour[0]; GLuint srcTexture = _engine->SwapCopyReturningSourceTexture(); _fillRectShader->Use(); _fillRectShader->SetFlags(1); _fillRectShader->SetSourceFramebuffer(srcTexture); } else { _fillRectShader->Use(); _fillRectShader->SetFlags(0); } _fillRectShader->SetColour(0, paletteColour[0]); _fillRectShader->SetColour(1, paletteColour[1]); _fillRectShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); _fillRectShader->Draw(left, top, right + 1, bottom + 1); } void OpenGLDrawingContext::DrawLine(uint32 colour, sint32 x1, sint32 y1, sint32 x2, sint32 y2) { x1 += _offsetX; y1 += _offsetY; x2 += _offsetX; y2 += _offsetY; vec4f paletteColour = _engine->GLPalette[colour & 0xFF]; _drawLineShader->Use(); _drawLineShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); _drawLineShader->SetColour(paletteColour); _drawLineShader->Draw(x1, y1, x2, y2); } void OpenGLDrawingContext::DrawSprite(uint32 image, sint32 x, sint32 y, uint32 tertiaryColour) { int g1Id = image & 0x7FFFF; rct_g1_element * g1Element = gfx_get_g1_element(g1Id); if (_dpi->zoom_level != 0) { if (g1Element->flags & (1 << 4)) { rct_drawpixelinfo zoomedDPI; zoomedDPI.bits = _dpi->bits; zoomedDPI.x = _dpi->x >> 1; zoomedDPI.y = _dpi->y >> 1; zoomedDPI.height = _dpi->height >> 1; zoomedDPI.width = _dpi->width >> 1; zoomedDPI.pitch = _dpi->pitch; zoomedDPI.zoom_level = _dpi->zoom_level - 1; SetDPI(&zoomedDPI); DrawSprite((image << 28) | (g1Id - g1Element->zoomed_offset), x >> 1, y >> 1, tertiaryColour); return; } if (g1Element->flags & (1 << 5)) { return; } } GLuint texture = _textureCache->GetOrLoadImageTexture(image); sint32 drawOffsetX = g1Element->x_offset; sint32 drawOffsetY = g1Element->y_offset; sint32 drawWidth = (uint16)g1Element->width >> _dpi->zoom_level; sint32 drawHeight = (uint16)g1Element->height >> _dpi->zoom_level; sint32 left = x + drawOffsetX; sint32 top = y + drawOffsetY; sint32 right = left + drawWidth; sint32 bottom = top + drawHeight; if (left > right) { std::swap(left, right); } if (top > bottom) { std::swap(top, bottom); } left += _offsetX; top += _offsetY; right += _offsetX; bottom += _offsetY; _drawImageShader->Use(); _drawImageShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); _drawImageShader->SetTexture(texture); _drawImageShader->Draw(left, top, right, bottom); } void OpenGLDrawingContext::DrawSpriteRawMasked(sint32 x, sint32 y, uint32 maskImage, uint32 colourImage) { rct_g1_element * g1ElementMask = gfx_get_g1_element(maskImage & 0x7FFFF); rct_g1_element * g1ElementColour = gfx_get_g1_element(colourImage & 0x7FFFF); GLuint textureMask = _textureCache->GetOrLoadImageTexture(maskImage); GLuint textureColour = _textureCache->GetOrLoadImageTexture(colourImage); sint32 drawOffsetX = g1ElementMask->x_offset; sint32 drawOffsetY = g1ElementMask->y_offset; sint32 drawWidth = Math::Min(g1ElementMask->width, g1ElementColour->width); sint32 drawHeight = Math::Min(g1ElementMask->height, g1ElementColour->height); sint32 left = x + drawOffsetX; sint32 top = y + drawOffsetY; sint32 right = left + drawWidth; sint32 bottom = top + drawHeight; if (left > right) { std::swap(left, right); } if (top > bottom) { std::swap(top, bottom); } left += _offsetX; top += _offsetY; right += _offsetX; bottom += _offsetY; _drawImageMaskedShader->Use(); _drawImageMaskedShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); _drawImageMaskedShader->SetTextureMask(textureMask); _drawImageMaskedShader->SetTextureColour(textureColour); _drawImageMaskedShader->Draw(left, top, right, bottom); } void OpenGLDrawingContext::DrawSpriteSolid(uint32 image, sint32 x, sint32 y, uint8 colour) { vec4f paletteColour = _engine->GLPalette[colour & 0xFF]; int g1Id = image & 0x7FFFF; rct_g1_element * g1Element = gfx_get_g1_element(g1Id); GLuint texture = _textureCache->GetOrLoadImageTexture(image); sint32 drawOffsetX = g1Element->x_offset; sint32 drawOffsetY = g1Element->y_offset; sint32 drawWidth = (uint16)g1Element->width; sint32 drawHeight = (uint16)g1Element->height; sint32 left = x + drawOffsetX; sint32 top = y + drawOffsetY; sint32 right = left + drawWidth; sint32 bottom = top + drawHeight; if (left > right) { std::swap(left, right); } if (top > bottom) { std::swap(top, bottom); } left += _offsetX; top += _offsetY; right += _offsetX; bottom += _offsetY; _drawImageShader->Use(); _drawImageShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); _drawImageShader->SetTexture(texture); _drawImageShader->SetFlags(1); _drawImageShader->SetColour(paletteColour); _drawImageShader->Draw(left, top, right, bottom); _drawImageShader->SetFlags(0); } void OpenGLDrawingContext::DrawGlyph(uint32 image, sint32 x, sint32 y, uint8 * palette) { int g1Id = image & 0x7FFFF; rct_g1_element * g1Element = gfx_get_g1_element(g1Id); GLuint texture = _textureCache->GetOrLoadGlyphTexture(image, palette); sint32 drawOffsetX = g1Element->x_offset; sint32 drawOffsetY = g1Element->y_offset; sint32 drawWidth = (uint16)g1Element->width; sint32 drawHeight = (uint16)g1Element->height; sint32 left = x + drawOffsetX; sint32 top = y + drawOffsetY; sint32 right = left + drawWidth; sint32 bottom = top + drawHeight; if (left > right) { std::swap(left, right); } if (top > bottom) { std::swap(top, bottom); } left += _offsetX; top += _offsetY; right += _offsetX; bottom += _offsetY; _drawImageShader->Use(); _drawImageShader->SetClip(_clipLeft, _clipTop, _clipRight, _clipBottom); _drawImageShader->SetTexture(texture); _drawImageShader->Draw(left, top, right, bottom); _drawImageShader->SetFlags(0); } void OpenGLDrawingContext::SetDPI(rct_drawpixelinfo * dpi) { rct_drawpixelinfo * screenDPI = _engine->GetDPI(); size_t bitsSize = (size_t)screenDPI->height * (size_t)(screenDPI->width + screenDPI->pitch); size_t bitsOffset = (size_t)(dpi->bits - screenDPI->bits); assert(bitsOffset < bitsSize); _clipLeft = bitsOffset % (screenDPI->width + screenDPI->pitch); _clipTop = bitsOffset / (screenDPI->width + screenDPI->pitch); _clipRight = _clipLeft + dpi->width; _clipBottom = _clipTop + dpi->height; _offsetX = _clipLeft - dpi->x; _offsetY = _clipTop - dpi->y; _dpi = dpi; } #endif /* DISABLE_OPENGL */