From e0702eb4f9adae6fb357278f0d74f41a7d0442c1 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Fri, 26 Jun 2026 21:44:38 +0200 Subject: [PATCH 01/12] Replace SDL_Renderer with bare OpenGL --- CGame.cpp | 27 ++++++++++++++++++++++----- CGame.h | 6 +++--- CGame_Init.cpp | 48 +++++++++++++++++++++++++++++++++++++++--------- CMakeLists.txt | 22 +++++++++++++++++++++- 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/CGame.cpp b/CGame.cpp index 3e1e4f7..73f6c1f 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -13,6 +13,7 @@ #include "s25util/file_handle.h" #include #include +#include #include #include #include @@ -43,6 +44,11 @@ CGame::CGame(Extent GameResolution_, bool fullscreen_) CGame::~CGame() { + if(glContext_) + { + SDL_GL_DeleteContext(glContext_); + glContext_ = nullptr; + } global::s2 = nullptr; } @@ -69,12 +75,23 @@ int CGame::Execute() return 0; } -void CGame::RenderPresent() const +void CGame::RenderPresent() { - SDL_UpdateTexture(displayTexture_.get(), nullptr, Surf_Display->pixels, Surf_Display->w * sizeof(Uint32)); - SDL_RenderClear(renderer_.get()); - SDL_RenderCopy(renderer_.get(), displayTexture_.get(), nullptr, nullptr); - SDL_RenderPresent(renderer_.get()); + glBindTexture(GL_TEXTURE_2D, displayTex_); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Surf_Display->w, Surf_Display->h, GL_BGRA, GL_UNSIGNED_BYTE, + Surf_Display->pixels); + glClear(GL_COLOR_BUFFER_BIT); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2i(0, 0); + glTexCoord2f(1, 0); + glVertex2i(GameResolution.x, 0); + glTexCoord2f(1, 1); + glVertex2i(GameResolution.x, GameResolution.y); + glTexCoord2f(0, 1); + glVertex2i(0, GameResolution.y); + glEnd(); + SDL_GL_SwapWindow(window_.get()); } CMenu* CGame::RegisterMenu(std::unique_ptr Menu) diff --git a/CGame.h b/CGame.h index 26b32d4..6a1eed5 100644 --- a/CGame.h +++ b/CGame.h @@ -27,8 +27,8 @@ class CGame bool Running; bool showLoadScreen; SdlSurface Surf_Display; - SdlTexture displayTexture_; - SdlRenderer renderer_; + SDL_GLContext glContext_ = nullptr; + unsigned int displayTex_ = 0; SdlWindow window_; private: @@ -88,7 +88,7 @@ class CGame void Render(); - void RenderPresent() const; + void RenderPresent(); CMenu* RegisterMenu(std::unique_ptr Menu); bool UnregisterMenu(CMenu* Menu); diff --git a/CGame_Init.cpp b/CGame_Init.cpp index f367224..2bccd21 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -12,25 +12,45 @@ #include "callbacks.h" #include "globals.h" #include "lua/GameDataLoader.h" +#include #include #include bool CGame::ReCreateWindow() { suppressResizeEvents_ = 3; - displayTexture_.reset(); - renderer_.reset(); + if(displayTex_) + { + glDeleteTextures(1, &displayTex_); + displayTex_ = 0; + } + if(glContext_) + { + SDL_GL_DeleteContext(glContext_); + glContext_ = nullptr; + } window_.reset(); window_.reset(SDL_CreateWindow("Return to the Roots Map editor [BETA]", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, GameResolution.x, GameResolution.y, - fullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE)); + (fullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE) | SDL_WINDOW_OPENGL)); if(!window_) return false; - renderer_.reset(SDL_CreateRenderer(window_.get(), -1, 0)); - if(!renderer_) + + glContext_ = SDL_GL_CreateContext(window_.get()); + if(!glContext_ || !gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) return false; + + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glClearColor(0, 0, 0, 1); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + RecreateDisplayResources(); - if(!displayTexture_ || !Surf_Display) + if(!displayTex_ || !Surf_Display) return false; SetAppIcon(); @@ -39,10 +59,20 @@ bool CGame::ReCreateWindow() void CGame::RecreateDisplayResources() { - displayTexture_.reset(); - displayTexture_ = makeSdlTexture(renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, GameResolution.x, - GameResolution.y); + if(displayTex_) + { + glDeleteTextures(1, &displayTex_); + displayTex_ = 0; + } Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true); + + glGenTextures(1, &displayTex_); + glBindTexture(GL_TEXTURE_2D, displayTex_); + 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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GameResolution.x, GameResolution.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); } void CGame::UpdateDisplaySize(const Extent& newSize) diff --git a/CMakeLists.txt b/CMakeLists.txt index a6e3788..0faeeac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,26 @@ project(s25edit) find_package(Boost 1.64 REQUIRED) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../libutil/cmake") + +# glad: OpenGL loader (reused from s25client) +if(NOT TARGET glad) + set(RTTR_OGL_MAJOR 2) + set(RTTR_OGL_MINOR 0) + set(RTTR_OGL_ES 0) + set(RTTR_OGL_COMPAT 1) + set(RTTR_OGL_GL4ES 0) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../glad/openglCfg.hpp.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/openglCfg.hpp @ONLY) + + set(GLAD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../glad/OpenGL2.0Compat/src/glad.c) + set(GLAD_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/../glad/OpenGL2.0Compat/include) + add_library(glad STATIC ${GLAD_SRC}) + target_include_directories(glad PUBLIC ${GLAD_INCLUDE} ${CMAKE_CURRENT_BINARY_DIR}/include) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(glad PUBLIC ${CMAKE_DL_LIBS}) + endif() +endif() + add_subdirectory(SGE) option(RTTR_EDITOR_ADMINMODE "In admin mode there are some key combos to open debugger, resource viewer and so on" OFF) @@ -38,7 +58,7 @@ ELSE() ENDIF() add_executable(s25edit ${MAIN_SOURCES} ${CIO_SOURCES} ${icon_RC}) -target_link_libraries(s25edit PRIVATE SGE rttrConfig s25Common gamedata siedler2 endian::static Boost::nowide PUBLIC Boost::disable_autolinking Boost::program_options) +target_link_libraries(s25edit PRIVATE SGE rttrConfig s25Common gamedata siedler2 endian::static glad Boost::nowide PUBLIC Boost::disable_autolinking Boost::program_options) target_include_directories(s25edit PRIVATE include) target_compile_features(s25edit PRIVATE cxx_std_17) From 728a8215657ed75112a4f5e23c865e8ebcd5ee63 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Fri, 26 Jun 2026 22:17:20 +0200 Subject: [PATCH 02/12] Draw cursor, splash and menu bg with GL --- CGame.cpp | 15 ++++- CGame_Init.cpp | 6 +- CGame_Render.cpp | 26 ++++++++- CIO/CControlContainer.h | 2 +- CIO/CFile.cpp | 2 + CIO/CMenu.cpp | 5 +- CSurface.cpp | 122 ++++++++++++++++++++++++++++++++++++++++ CSurface.h | 2 + 8 files changed, 172 insertions(+), 8 deletions(-) diff --git a/CGame.cpp b/CGame.cpp index 73f6c1f..86f7274 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -80,7 +80,9 @@ void CGame::RenderPresent() glBindTexture(GL_TEXTURE_2D, displayTex_); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Surf_Display->w, Surf_Display->h, GL_BGRA, GL_UNSIGNED_BYTE, Surf_Display->pixels); - glClear(GL_COLOR_BUFFER_BIT); + // Screen was cleared at the start of Render(); GL-drawn backgrounds + // are already in the framebuffer. The Surf_Display texture (with + // alpha=0 for transparent areas) is blended on top via GL_BLEND. glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(0, 0); @@ -91,6 +93,17 @@ void CGame::RenderPresent() glTexCoord2f(0, 1); glVertex2i(0, GameResolution.y); glEnd(); + + // Draw cursor on top of everything (cached GL texture with colorkey) + { + SDL_Surface* cursorSurf = Cursor.clicked ? + (Cursor.button.right ? global::bmpArray[CROSS].surface.get() : + global::bmpArray[CURSOR_CLICKED].surface.get()) : + global::bmpArray[CURSOR].surface.get(); + if(cursorSurf) + CSurface::DrawGL(cursorSurf, Cursor.pos.x, Cursor.pos.y); + } + SDL_GL_SwapWindow(window_.get()); } diff --git a/CGame_Init.cpp b/CGame_Init.cpp index 2bccd21..00efd3a 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -41,8 +41,11 @@ bool CGame::ReCreateWindow() return false; glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glClearColor(0, 0, 0, 1); + glViewport(0, 0, GameResolution.x, GameResolution.y); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); @@ -126,8 +129,9 @@ bool CGame::Init() // std::cout << "\nShow loading screen..."; showLoadScreen = true; + glClear(GL_COLOR_BUFFER_BIT); CSurface::DrawStretched(Surf_Display, global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface); - RenderPresent(); + SDL_GL_SwapWindow(window_.get()); GameDataLoader gdLoader(global::worldDesc); if(!gdLoader.Load()) diff --git a/CGame_Render.cpp b/CGame_Render.cpp index 3fc316c..3bb280f 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -10,6 +10,7 @@ #include "CMap.h" #include "CSurface.h" #include "globals.h" +#include #ifdef _WIN32 # include "s25editResource.h" # ifndef WIN32_LEAN_AND_MEAN @@ -39,6 +40,21 @@ void CGame::SetAppIcon() void CGame::Render() { suppressResizeEvents_ = 0; + + // Ensure viewport and ortho projection match current window size (handles resize) + glViewport(0, 0, GameResolution.x, GameResolution.y); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Clear the framebuffer and the software overlay surface. + // GL draws (backgrounds) and the final Surf_Display overlay are + // composited via blending. + glClear(GL_COLOR_BUFFER_BIT); + SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); + if(Extent(Surf_Display->w, Surf_Display->h) != GameResolution || fullscreen != ((SDL_GetWindowFlags(window_.get()) & SDL_WINDOW_FULLSCREEN) != 0)) { @@ -49,7 +65,7 @@ void CGame::Render() if(showLoadScreen) { CSurface::DrawStretched(Surf_Display, global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface); - RenderPresent(); + SDL_GL_SwapWindow(window_.get()); return; } @@ -82,7 +98,13 @@ void CGame::Render() for(auto& Menu : Menus) { if(Menu->isActive()) + { + // Draw menu background via OpenGL + auto& bgBmp = global::bmpArray[Menu->getBackground()]; + CSurface::DrawStretched(Surf_Display, bgBmp.surface); + // Draw UI overlay on top CSurface::Draw(Surf_Display, Menu->getSurface(), 0, 0); + } } // render windows ordered by priority @@ -103,7 +125,7 @@ void CGame::Render() } } - // render mouse cursor + // render mouse cursor (drawn in RenderPresent via GL to stay on top of all overlays) if(Cursor.clicked) { if(Cursor.button.right) diff --git a/CIO/CControlContainer.h b/CIO/CControlContainer.h index dacc91f..d3661de 100644 --- a/CIO/CControlContainer.h +++ b/CIO/CControlContainer.h @@ -49,9 +49,9 @@ class CControlContainer void renderElements(); auto& getTextFields() { return textfields; } const auto& getTextFields() const { return textfields; } - int getBackground() const { return pic_background; } public: + int getBackground() const { return pic_background; } CControlContainer(int pic_background); CControlContainer(int pic_background, Extent borderBeginSize, Extent borderEndSize); ~CControlContainer() noexcept; diff --git a/CIO/CFile.cpp b/CIO/CFile.cpp index 023d89f..078d1d0 100644 --- a/CIO/CFile.cpp +++ b/CIO/CFile.cpp @@ -291,6 +291,7 @@ bool CFile::read_bbm(FILE* fp) CHECK_READ(libendian::read(&(color.r), 1, fp)); CHECK_READ(libendian::read(&(color.g), 1, fp)); CHECK_READ(libendian::read(&(color.b), 1, fp)); + color.a = 255; } palArray++; @@ -1250,6 +1251,7 @@ bool CFile::read_bob05(FILE* fp) CHECK_READ(libendian::read(&(color.r), 1, fp)); CHECK_READ(libendian::read(&(color.g), 1, fp)); CHECK_READ(libendian::read(&(color.b), 1, fp)); + color.a = 255; } palArray++; diff --git a/CIO/CMenu.cpp b/CIO/CMenu.cpp index fc8da5e..c77564d 100644 --- a/CIO/CMenu.cpp +++ b/CIO/CMenu.cpp @@ -5,7 +5,6 @@ #include "CMenu.h" #include "../CGame.h" -#include "../CSurface.h" #include "../globals.h" CMenu::CMenu(int pic_background) : CControlContainer(pic_background) {} @@ -22,12 +21,12 @@ bool CMenu::render() // if we need a new surface if(!surface) { - surface = makeRGBSurface(global::s2->getRes().x, global::s2->getRes().y); + surface = makeRGBSurface(global::s2->getRes().x, global::s2->getRes().y, true); if(!surface) return false; } - CSurface::DrawStretched(surface, global::bmpArray[getBackground()].surface); + // Background is drawn via OpenGL in CGame::Render(); only draw UI elements here. renderElements(); return true; } diff --git a/CSurface.cpp b/CSurface.cpp index 18cc1ce..79343d0 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -12,9 +12,11 @@ #include "globals.h" #include "gameData/EdgeDesc.h" #include "gameData/TerrainDesc.h" +#include #include #include #include +#include // Disable SGE's internal surface locking once at startup; terrain drawing is // the only remaining consumer of SGE functions and the caller already handles @@ -189,6 +191,64 @@ void CSurface::DrawStretched(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src) if(!Surf_Dest || !Surf_Src) return; + // When drawing to the display surface, use OpenGL directly (no SDL blit). + // This is the path used by the loading screen and splash. + if(global::s2 && Surf_Dest == global::s2->getDisplaySurface()) + { + // Cache GL textures for static background images (created once, reused each frame) + static std::unordered_map s_bgTexCache; + + auto it = s_bgTexCache.find(Surf_Src); + if(it == s_bgTexCache.end()) + { + // Convert to match destination format (BGRA8888 on little-endian) + SDL_Surface* converted = SDL_ConvertSurface(Surf_Src, Surf_Dest->format, 0); + if(!converted) + return; + + // LBM palette entries often have alpha=0; force full opacity + if(Surf_Dest->format->Amask) + { + SDL_LockSurface(converted); + for(int y = 0; y < converted->h; y++) + { + auto* row = (Uint32*)((Uint8*)converted->pixels + y * converted->pitch); + for(int x = 0; x < converted->w; x++) + row[x] |= Surf_Dest->format->Amask; + } + SDL_UnlockSurface(converted); + } + + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, converted->w, converted->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, + converted->pixels); + SDL_FreeSurface(converted); + + it = s_bgTexCache.emplace(Surf_Src, tex).first; + } + + // Draw stretched full-screen using cached texture (screen already cleared by caller) + glBindTexture(GL_TEXTURE_2D, it->second); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2i(0, 0); + glTexCoord2f(1, 0); + glVertex2i(Surf_Dest->w, 0); + glTexCoord2f(1, 1); + glVertex2i(Surf_Dest->w, Surf_Dest->h); + glTexCoord2f(0, 1); + glVertex2i(0, Surf_Dest->h); + glEnd(); + return; + } + + // Fallback: SDL blit for intermediate compositing surfaces (e.g. menus). // Convert to 32-bit RGB to handle 8-bit paletted sources and drop any // implicit alpha from LBM palette entries (which have a=0). SDL_Surface* converted = SDL_ConvertSurfaceFormat(Surf_Src, SDL_PIXELFORMAT_RGB888, 0); @@ -205,6 +265,68 @@ void CSurface::DrawStretched(SdlSurface& Surf_Dest, SdlSurface& Surf_Src) DrawStretched(Surf_Dest.get(), Surf_Src.get()); } +void CSurface::DrawGL(SDL_Surface* surface, int x, int y) +{ + if(!surface || !surface->format->palette) + return; + + static std::unordered_map s_texCache; + + auto it = s_texCache.find(surface); + if(it == s_texCache.end()) + { + const int cw = surface->w, ch = surface->h; + std::vector pixels(static_cast(cw) * ch); + + SDL_Palette* pal = surface->format->palette; + Uint32 ck; + const bool hasCK = (SDL_GetColorKey(surface, &ck) == 0); + const Uint8 ckIdx = hasCK ? static_cast(ck & 0xFF) : 0; + + SDL_LockSurface(surface); + for(int row = 0; row < ch; row++) + { + const auto* src = (const Uint8*)surface->pixels + row * surface->pitch; + for(int col = 0; col < cw; col++) + { + const Uint8 idx = src[col]; + if(hasCK && idx == ckIdx) + pixels[row * cw + col] = 0; + else + { + const SDL_Color& c = pal->colors[idx]; + pixels[row * cw + col] = (0xFFu << 24) | (Uint32(c.r) << 16) | (Uint32(c.g) << 8) | Uint32(c.b); + } + } + } + SDL_UnlockSurface(surface); + + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + 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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cw, ch, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels.data()); + + it = s_texCache.emplace(surface, tex).first; + } + + glBindTexture(GL_TEXTURE_2D, it->second); + const int cw = surface->w, ch = surface->h; + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2i(x, y); + glTexCoord2f(1, 0); + glVertex2i(x + cw, y); + glTexCoord2f(1, 1); + glVertex2i(x + cw, y + ch); + glTexCoord2f(0, 1); + glVertex2i(x, y + ch); + glEnd(); +} + // this is the example function from the SDL-documentation to draw pixels void CSurface::DrawPixel_Color(SDL_Surface* screen, Position pos, Uint32 color) { diff --git a/CSurface.h b/CSurface.h index 6b32021..b05ea47 100644 --- a/CSurface.h +++ b/CSurface.h @@ -48,6 +48,8 @@ class CSurface // stretches Surf_Src to fill entire Surf_Dest static void DrawStretched(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src); static void DrawStretched(SdlSurface& Surf_Dest, SdlSurface& Surf_Src); + // draws an 8-bit paletted surface (with colorkey) as a cached GL textured quad + static void DrawGL(SDL_Surface* surface, int x, int y); static void DrawPixel_Color(SDL_Surface* screen, Position pos, Uint32 color); static void DrawPixel_RGB(SDL_Surface* screen, Position pos, Uint8 R, Uint8 G, Uint8 B); static void DrawPixel_RGB(SdlSurface& screen, Position pos, Uint8 R, Uint8 G, Uint8 B) From f0eba1916598b1faef439b134b8980ad1825896c Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Sat, 27 Jun 2026 13:10:01 +0200 Subject: [PATCH 03/12] setGLViewport() --- CGame.cpp | 1 + CGame.h | 1 + CGame_Init.cpp | 17 +++++++++++------ CGame_Render.cpp | 7 +------ 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CGame.cpp b/CGame.cpp index 86f7274..a39b31c 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -7,6 +7,7 @@ #include "CIO/CMenu.h" #include "CIO/CWindow.h" #include "CMap.h" +#include "CSurface.h" #include "RttrConfig.h" #include "files.h" #include "globals.h" diff --git a/CGame.h b/CGame.h index 6a1eed5..0f84ace 100644 --- a/CGame.h +++ b/CGame.h @@ -68,6 +68,7 @@ class CGame void SetAppIcon(); void RecreateDisplayResources(); + void setGLViewport(); public: void LoadSettings(); diff --git a/CGame_Init.cpp b/CGame_Init.cpp index 00efd3a..412fba9 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -45,12 +45,7 @@ bool CGame::ReCreateWindow() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glClearColor(0, 0, 0, 1); - glViewport(0, 0, GameResolution.x, GameResolution.y); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + setGLViewport(); RecreateDisplayResources(); if(!displayTex_ || !Surf_Display) @@ -78,6 +73,16 @@ void CGame::RecreateDisplayResources() glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GameResolution.x, GameResolution.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); } +void CGame::setGLViewport() +{ + glViewport(0, 0, GameResolution.x, GameResolution.y); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + void CGame::UpdateDisplaySize(const Extent& newSize) { GameResolution = newSize; diff --git a/CGame_Render.cpp b/CGame_Render.cpp index 3bb280f..e0d6af7 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -42,12 +42,7 @@ void CGame::Render() suppressResizeEvents_ = 0; // Ensure viewport and ortho projection match current window size (handles resize) - glViewport(0, 0, GameResolution.x, GameResolution.y); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + setGLViewport(); // Clear the framebuffer and the software overlay surface. // GL draws (backgrounds) and the final Surf_Display overlay are From 1e5ffa39843794332e0a229e991b00af8f902910 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Sat, 27 Jun 2026 13:52:53 +0200 Subject: [PATCH 04/12] class GlTexture --- CGame.cpp | 11 +--- CGame.h | 9 +++ CGame_Init.cpp | 13 +++- CGame_Render.cpp | 17 ++---- CSurface.cpp | 143 -------------------------------------------- CSurface.h | 6 +- GlTexture.cpp | 151 +++++++++++++++++++++++++++++++++++++++++++++++ GlTexture.h | 40 +++++++++++++ 8 files changed, 220 insertions(+), 170 deletions(-) create mode 100644 GlTexture.cpp create mode 100644 GlTexture.h diff --git a/CGame.cpp b/CGame.cpp index a39b31c..54d19a5 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -7,7 +7,6 @@ #include "CIO/CMenu.h" #include "CIO/CWindow.h" #include "CMap.h" -#include "CSurface.h" #include "RttrConfig.h" #include "files.h" #include "globals.h" @@ -95,14 +94,10 @@ void CGame::RenderPresent() glVertex2i(0, GameResolution.y); glEnd(); - // Draw cursor on top of everything (cached GL texture with colorkey) + // Draw cursor on top of everything { - SDL_Surface* cursorSurf = Cursor.clicked ? - (Cursor.button.right ? global::bmpArray[CROSS].surface.get() : - global::bmpArray[CURSOR_CLICKED].surface.get()) : - global::bmpArray[CURSOR].surface.get(); - if(cursorSurf) - CSurface::DrawGL(cursorSurf, Cursor.pos.x, Cursor.pos.y); + const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_; + cursorImg.Draw(Cursor.pos.x, Cursor.pos.y); } SDL_GL_SwapWindow(window_.get()); diff --git a/CGame.h b/CGame.h index 0f84ace..8b41ca7 100644 --- a/CGame.h +++ b/CGame.h @@ -6,6 +6,7 @@ #pragma once #include "CIO/CFont.h" +#include "GlTexture.h" #include "SdlSurface.h" #include #include @@ -45,6 +46,14 @@ class CGame Uint32 lastFrameTime = 0; unsigned suppressResizeEvents_ = 0; + // GL textures for backgrounds and cursor + GlTexture splashBg_; + GlTexture menuBgMain_; + GlTexture menuBgSub_; + GlTexture cursor_; + GlTexture cursorClicked_; + GlTexture cross_; + // structure for mouse cursor struct { diff --git a/CGame_Init.cpp b/CGame_Init.cpp index 412fba9..e988805 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -8,7 +8,6 @@ #include "CIO/CMenu.h" #include "CIO/CWindow.h" #include "CMap.h" -#include "CSurface.h" #include "callbacks.h" #include "globals.h" #include "lua/GameDataLoader.h" @@ -132,10 +131,13 @@ bool CGame::Init() } } + // Create GL texture for splash background + splashBg_.load(global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface.get(), true); + // std::cout << "\nShow loading screen..."; showLoadScreen = true; glClear(GL_COLOR_BUFFER_BIT); - CSurface::DrawStretched(Surf_Display, global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface); + splashBg_.DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y)); SDL_GL_SwapWindow(window_.get()); GameDataLoader gdLoader(global::worldDesc); @@ -291,5 +293,12 @@ bool CGame::Init() // create the mainmenu callback::mainmenu(INITIALIZING_CALL); + // Create GL textures for cursor and menu background images + cursor_.load(global::bmpArray[CURSOR].surface.get()); + cursorClicked_.load(global::bmpArray[CURSOR_CLICKED].surface.get()); + cross_.load(global::bmpArray[CROSS].surface.get()); + menuBgMain_.load(global::bmpArray[SPLASHSCREEN_MAINMENU].surface.get(), true); + menuBgSub_.load(global::bmpArray[SPLASHSCREEN_SUBMENU3].surface.get(), true); + return true; } diff --git a/CGame_Render.cpp b/CGame_Render.cpp index e0d6af7..fde3a98 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -59,7 +59,7 @@ void CGame::Render() // if the S2 loading screen is shown, render only this until user clicks a mouse button if(showLoadScreen) { - CSurface::DrawStretched(Surf_Display, global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface); + splashBg_.DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y)); SDL_GL_SwapWindow(window_.get()); return; } @@ -95,8 +95,9 @@ void CGame::Render() if(Menu->isActive()) { // Draw menu background via OpenGL - auto& bgBmp = global::bmpArray[Menu->getBackground()]; - CSurface::DrawStretched(Surf_Display, bgBmp.surface); + int bgIdx = Menu->getBackground(); + (bgIdx == SPLASHSCREEN_MAINMENU ? menuBgMain_ : menuBgSub_) + .DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y)); // Draw UI overlay on top CSurface::Draw(Surf_Display, Menu->getSurface(), 0, 0); } @@ -120,15 +121,7 @@ void CGame::Render() } } - // render mouse cursor (drawn in RenderPresent via GL to stay on top of all overlays) - if(Cursor.clicked) - { - if(Cursor.button.right) - CSurface::Draw(Surf_Display, global::bmpArray[CROSS].surface, Cursor.pos); - else - CSurface::Draw(Surf_Display, global::bmpArray[CURSOR_CLICKED].surface, Cursor.pos); - } else - CSurface::Draw(Surf_Display, global::bmpArray[CURSOR].surface, Cursor.pos); + // Cursor is drawn in RenderPresent via GL (after the Surf_Display overlay) #ifdef _ADMINMODE FrameCounter++; diff --git a/CSurface.cpp b/CSurface.cpp index 79343d0..d032a13 100644 --- a/CSurface.cpp +++ b/CSurface.cpp @@ -12,11 +12,9 @@ #include "globals.h" #include "gameData/EdgeDesc.h" #include "gameData/TerrainDesc.h" -#include #include #include #include -#include // Disable SGE's internal surface locking once at startup; terrain drawing is // the only remaining consumer of SGE functions and the caller already handles @@ -186,147 +184,6 @@ bool CSurface::Draw(SDL_Surface* Surf_Dest, SdlSurface& Surf_Src, Position dest, return Draw(Surf_Dest, Surf_Src.get(), dest, srcOffset, srcSize); } -void CSurface::DrawStretched(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src) -{ - if(!Surf_Dest || !Surf_Src) - return; - - // When drawing to the display surface, use OpenGL directly (no SDL blit). - // This is the path used by the loading screen and splash. - if(global::s2 && Surf_Dest == global::s2->getDisplaySurface()) - { - // Cache GL textures for static background images (created once, reused each frame) - static std::unordered_map s_bgTexCache; - - auto it = s_bgTexCache.find(Surf_Src); - if(it == s_bgTexCache.end()) - { - // Convert to match destination format (BGRA8888 on little-endian) - SDL_Surface* converted = SDL_ConvertSurface(Surf_Src, Surf_Dest->format, 0); - if(!converted) - return; - - // LBM palette entries often have alpha=0; force full opacity - if(Surf_Dest->format->Amask) - { - SDL_LockSurface(converted); - for(int y = 0; y < converted->h; y++) - { - auto* row = (Uint32*)((Uint8*)converted->pixels + y * converted->pitch); - for(int x = 0; x < converted->w; x++) - row[x] |= Surf_Dest->format->Amask; - } - SDL_UnlockSurface(converted); - } - - GLuint tex; - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, converted->w, converted->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, - converted->pixels); - SDL_FreeSurface(converted); - - it = s_bgTexCache.emplace(Surf_Src, tex).first; - } - - // Draw stretched full-screen using cached texture (screen already cleared by caller) - glBindTexture(GL_TEXTURE_2D, it->second); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2i(0, 0); - glTexCoord2f(1, 0); - glVertex2i(Surf_Dest->w, 0); - glTexCoord2f(1, 1); - glVertex2i(Surf_Dest->w, Surf_Dest->h); - glTexCoord2f(0, 1); - glVertex2i(0, Surf_Dest->h); - glEnd(); - return; - } - - // Fallback: SDL blit for intermediate compositing surfaces (e.g. menus). - // Convert to 32-bit RGB to handle 8-bit paletted sources and drop any - // implicit alpha from LBM palette entries (which have a=0). - SDL_Surface* converted = SDL_ConvertSurfaceFormat(Surf_Src, SDL_PIXELFORMAT_RGB888, 0); - if(!converted) - return; - - // Stretch full source to fill full destination - SDL_BlitScaled(converted, nullptr, Surf_Dest, nullptr); - SDL_FreeSurface(converted); -} - -void CSurface::DrawStretched(SdlSurface& Surf_Dest, SdlSurface& Surf_Src) -{ - DrawStretched(Surf_Dest.get(), Surf_Src.get()); -} - -void CSurface::DrawGL(SDL_Surface* surface, int x, int y) -{ - if(!surface || !surface->format->palette) - return; - - static std::unordered_map s_texCache; - - auto it = s_texCache.find(surface); - if(it == s_texCache.end()) - { - const int cw = surface->w, ch = surface->h; - std::vector pixels(static_cast(cw) * ch); - - SDL_Palette* pal = surface->format->palette; - Uint32 ck; - const bool hasCK = (SDL_GetColorKey(surface, &ck) == 0); - const Uint8 ckIdx = hasCK ? static_cast(ck & 0xFF) : 0; - - SDL_LockSurface(surface); - for(int row = 0; row < ch; row++) - { - const auto* src = (const Uint8*)surface->pixels + row * surface->pitch; - for(int col = 0; col < cw; col++) - { - const Uint8 idx = src[col]; - if(hasCK && idx == ckIdx) - pixels[row * cw + col] = 0; - else - { - const SDL_Color& c = pal->colors[idx]; - pixels[row * cw + col] = (0xFFu << 24) | (Uint32(c.r) << 16) | (Uint32(c.g) << 8) | Uint32(c.b); - } - } - } - SDL_UnlockSurface(surface); - - GLuint tex; - glGenTextures(1, &tex); - glBindTexture(GL_TEXTURE_2D, tex); - 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); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cw, ch, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels.data()); - - it = s_texCache.emplace(surface, tex).first; - } - - glBindTexture(GL_TEXTURE_2D, it->second); - const int cw = surface->w, ch = surface->h; - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2i(x, y); - glTexCoord2f(1, 0); - glVertex2i(x + cw, y); - glTexCoord2f(1, 1); - glVertex2i(x + cw, y + ch); - glTexCoord2f(0, 1); - glVertex2i(x, y + ch); - glEnd(); -} - // this is the example function from the SDL-documentation to draw pixels void CSurface::DrawPixel_Color(SDL_Surface* screen, Position pos, Uint32 color) { diff --git a/CSurface.h b/CSurface.h index b05ea47..b7ddeb9 100644 --- a/CSurface.h +++ b/CSurface.h @@ -45,11 +45,7 @@ class CSurface { return Draw(Surf_Dest.get(), Surf_Src.get(), X, Y, X2, Y2, W, H); } - // stretches Surf_Src to fill entire Surf_Dest - static void DrawStretched(SDL_Surface* Surf_Dest, SDL_Surface* Surf_Src); - static void DrawStretched(SdlSurface& Surf_Dest, SdlSurface& Surf_Src); - // draws an 8-bit paletted surface (with colorkey) as a cached GL textured quad - static void DrawGL(SDL_Surface* surface, int x, int y); + static void DrawPixel_Color(SDL_Surface* screen, Position pos, Uint32 color); static void DrawPixel_RGB(SDL_Surface* screen, Position pos, Uint8 R, Uint8 G, Uint8 B); static void DrawPixel_RGB(SdlSurface& screen, Position pos, Uint8 R, Uint8 G, Uint8 B) diff --git a/GlTexture.cpp b/GlTexture.cpp new file mode 100644 index 0000000..ad41ceb --- /dev/null +++ b/GlTexture.cpp @@ -0,0 +1,151 @@ +// Copyright (C) 2009 - 2021 Marc Vester (XaserLE) +// Copyright (C) 2009 - 2021 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "GlTexture.h" +#include +#include + +GlTexture::~GlTexture() +{ + if(texture_) + glDeleteTextures(1, &texture_); +} + +GlTexture::GlTexture(GlTexture&& other) noexcept + : texture_(other.texture_), width_(other.width_), height_(other.height_) +{ + other.texture_ = 0; + other.width_ = 0; + other.height_ = 0; +} + +GlTexture& GlTexture::operator=(GlTexture&& other) noexcept +{ + if(this != &other) + { + if(texture_) + glDeleteTextures(1, &texture_); + texture_ = other.texture_; + width_ = other.width_; + height_ = other.height_; + other.texture_ = 0; + other.width_ = 0; + other.height_ = 0; + } + return *this; +} + +void GlTexture::createTexture(const void* bgraPixels, int w, int h, bool linear) +{ + if(texture_) + glDeleteTextures(1, &texture_); + texture_ = 0; + width_ = height_ = 0; + + glGenTextures(1, &texture_); + glBindTexture(GL_TEXTURE_2D, texture_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, bgraPixels); + width_ = w; + height_ = h; +} + +bool GlTexture::load(SDL_Surface* surface, bool linear) +{ + if(!surface) + return false; + + // For 8-bit paletted surfaces, honour colorkey if set. + if(surface->format->palette) + { + const int w = surface->w, h = surface->h; + std::vector pixels(static_cast(w) * h); + + SDL_Palette* pal = surface->format->palette; + Uint32 ck; + const bool hasCK = (SDL_GetColorKey(surface, &ck) == 0); + const Uint8 ckIdx = hasCK ? static_cast(ck & 0xFF) : 0; + + SDL_LockSurface(surface); + for(int row = 0; row < h; row++) + { + const auto* src = (const Uint8*)surface->pixels + row * surface->pitch; + for(int col = 0; col < w; col++) + { + const Uint8 idx = src[col]; + if(hasCK && idx == ckIdx) + pixels[row * w + col] = 0; // transparent + else + { + const SDL_Color& c = pal->colors[idx]; + // BGRA layout: A<<24 | R<<16 | G<<8 | B (little-endian GL_BGRA) + pixels[row * w + col] = (0xFFu << 24) | (Uint32(c.r) << 16) | (Uint32(c.g) << 8) | Uint32(c.b); + } + } + } + SDL_UnlockSurface(surface); + + createTexture(pixels.data(), w, h, linear); + return true; + } + + // 32-bit surface: convert to destination format (BGRA), force full opacity + SDL_Surface* converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0); + if(!converted) + return false; + + // Force alpha to opaque (LBM palette entries often have alpha=0) + SDL_LockSurface(converted); + for(int y = 0; y < converted->h; y++) + { + auto* row = (Uint32*)((Uint8*)converted->pixels + y * converted->pitch); + for(int x = 0; x < converted->w; x++) + row[x] |= 0xFF000000u; // set alpha bits + } + SDL_UnlockSurface(converted); + + createTexture(converted->pixels, converted->w, converted->h, linear); + SDL_FreeSurface(converted); + return true; +} + +void GlTexture::DrawFull(const Rect& destRect) const +{ + if(!texture_) + return; + + glBindTexture(GL_TEXTURE_2D, texture_); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2i(destRect.left, destRect.top); + glTexCoord2f(1, 0); + glVertex2i(destRect.right, destRect.top); + glTexCoord2f(1, 1); + glVertex2i(destRect.right, destRect.bottom); + glTexCoord2f(0, 1); + glVertex2i(destRect.left, destRect.bottom); + glEnd(); +} + +void GlTexture::Draw(int x, int y) const +{ + if(!texture_) + return; + + glBindTexture(GL_TEXTURE_2D, texture_); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2i(x, y); + glTexCoord2f(1, 0); + glVertex2i(x + width_, y); + glTexCoord2f(1, 1); + glVertex2i(x + width_, y + height_); + glTexCoord2f(0, 1); + glVertex2i(x, y + height_); + glEnd(); +} diff --git a/GlTexture.h b/GlTexture.h new file mode 100644 index 0000000..584a45a --- /dev/null +++ b/GlTexture.h @@ -0,0 +1,40 @@ +// Copyright (C) 2009 - 2021 Marc Vester (XaserLE) +// Copyright (C) 2009 - 2021 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "Rect.h" +#include + +/// Wraps a GL texture with RAII and provides draw methods. +class GlTexture +{ +public: + GlTexture() = default; + ~GlTexture(); + + GlTexture(GlTexture&&) noexcept; + GlTexture& operator=(GlTexture&&) noexcept; + + // No copy + GlTexture(const GlTexture&) = delete; + GlTexture& operator=(const GlTexture&) = delete; + + /// Load from a 32-bit or 8-bit paletted SDL surface. + /// For 8-bit surfaces, optional colorkey is respected (keyed pixels become transparent). + bool load(SDL_Surface* surface, bool linear = false); + + /// Draw the texture stretched to fill the given rect. + void DrawFull(const Rect& destRect) const; + + /// Draw the texture at native size at the given position. + void Draw(int x, int y) const; + +private: + unsigned int texture_ = 0; + int width_ = 0, height_ = 0; + + void createTexture(const void* bgraPixels, int w, int h, bool linear); +}; From 934e919a0f2ce13a2c2e5444bcfeb9df0d6906a3 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Wed, 1 Jul 2026 15:20:00 +0200 Subject: [PATCH 05/12] Review feedback --- CGame.cpp | 23 ++--------- CGame.h | 16 ++++---- CGame_Init.cpp | 28 +++----------- CGame_Render.cpp | 14 +------ CIO/CControlContainer.h | 2 +- CIO/CMenu.cpp | 17 ++++++++- CIO/CMenu.h | 4 ++ CMakeLists.txt | 18 --------- CSurface.h | 1 - GlTexture.h | 40 ------------------- GlTexture.cpp => Texture.cpp | 74 +++++++++++++++++++----------------- Texture.h | 50 ++++++++++++++++++++++++ 12 files changed, 127 insertions(+), 160 deletions(-) delete mode 100644 GlTexture.h rename GlTexture.cpp => Texture.cpp (64%) create mode 100644 Texture.h diff --git a/CGame.cpp b/CGame.cpp index 54d19a5..c5e60a8 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -77,27 +77,12 @@ int CGame::Execute() void CGame::RenderPresent() { - glBindTexture(GL_TEXTURE_2D, displayTex_); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Surf_Display->w, Surf_Display->h, GL_BGRA, GL_UNSIGNED_BYTE, - Surf_Display->pixels); - // Screen was cleared at the start of Render(); GL-drawn backgrounds - // are already in the framebuffer. The Surf_Display texture (with - // alpha=0 for transparent areas) is blended on top via GL_BLEND. - glBegin(GL_QUADS); - glTexCoord2f(0, 0); - glVertex2i(0, 0); - glTexCoord2f(1, 0); - glVertex2i(GameResolution.x, 0); - glTexCoord2f(1, 1); - glVertex2i(GameResolution.x, GameResolution.y); - glTexCoord2f(0, 1); - glVertex2i(0, GameResolution.y); - glEnd(); - - // Draw cursor on top of everything + displayTexture_.upload(Surf_Display->pixels); + displayTexture_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); + { const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_; - cursorImg.Draw(Cursor.pos.x, Cursor.pos.y); + cursorImg.Draw(Cursor.pos); } SDL_GL_SwapWindow(window_.get()); diff --git a/CGame.h b/CGame.h index 8b41ca7..68172e3 100644 --- a/CGame.h +++ b/CGame.h @@ -6,8 +6,8 @@ #pragma once #include "CIO/CFont.h" -#include "GlTexture.h" #include "SdlSurface.h" +#include "Texture.h" #include #include #include @@ -28,8 +28,8 @@ class CGame bool Running; bool showLoadScreen; SdlSurface Surf_Display; + Texture displayTexture_; SDL_GLContext glContext_ = nullptr; - unsigned int displayTex_ = 0; SdlWindow window_; private: @@ -46,13 +46,11 @@ class CGame Uint32 lastFrameTime = 0; unsigned suppressResizeEvents_ = 0; - // GL textures for backgrounds and cursor - GlTexture splashBg_; - GlTexture menuBgMain_; - GlTexture menuBgSub_; - GlTexture cursor_; - GlTexture cursorClicked_; - GlTexture cross_; + // GL textures for splash screen and cursor + Texture splashBg_; + Texture cursor_; + Texture cursorClicked_; + Texture cross_; // structure for mouse cursor struct diff --git a/CGame_Init.cpp b/CGame_Init.cpp index e988805..3738beb 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -18,11 +18,7 @@ bool CGame::ReCreateWindow() { suppressResizeEvents_ = 3; - if(displayTex_) - { - glDeleteTextures(1, &displayTex_); - displayTex_ = 0; - } + displayTexture_ = Texture(); if(glContext_) { SDL_GL_DeleteContext(glContext_); @@ -47,7 +43,7 @@ bool CGame::ReCreateWindow() setGLViewport(); RecreateDisplayResources(); - if(!displayTex_ || !Surf_Display) + if(!displayTexture_.isValid() || !Surf_Display) return false; SetAppIcon(); @@ -56,20 +52,8 @@ bool CGame::ReCreateWindow() void CGame::RecreateDisplayResources() { - if(displayTex_) - { - glDeleteTextures(1, &displayTex_); - displayTex_ = 0; - } Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true); - - glGenTextures(1, &displayTex_); - glBindTexture(GL_TEXTURE_2D, displayTex_); - 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); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GameResolution.x, GameResolution.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, nullptr); + displayTexture_.load(GameResolution); } void CGame::setGLViewport() @@ -137,7 +121,7 @@ bool CGame::Init() // std::cout << "\nShow loading screen..."; showLoadScreen = true; glClear(GL_COLOR_BUFFER_BIT); - splashBg_.DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y)); + splashBg_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); SDL_GL_SwapWindow(window_.get()); GameDataLoader gdLoader(global::worldDesc); @@ -293,12 +277,10 @@ bool CGame::Init() // create the mainmenu callback::mainmenu(INITIALIZING_CALL); - // Create GL textures for cursor and menu background images + // Create GL textures for cursor cursor_.load(global::bmpArray[CURSOR].surface.get()); cursorClicked_.load(global::bmpArray[CURSOR_CLICKED].surface.get()); cross_.load(global::bmpArray[CROSS].surface.get()); - menuBgMain_.load(global::bmpArray[SPLASHSCREEN_MAINMENU].surface.get(), true); - menuBgSub_.load(global::bmpArray[SPLASHSCREEN_SUBMENU3].surface.get(), true); return true; } diff --git a/CGame_Render.cpp b/CGame_Render.cpp index fde3a98..6297f8e 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -41,12 +41,7 @@ void CGame::Render() { suppressResizeEvents_ = 0; - // Ensure viewport and ortho projection match current window size (handles resize) setGLViewport(); - - // Clear the framebuffer and the software overlay surface. - // GL draws (backgrounds) and the final Surf_Display overlay are - // composited via blending. glClear(GL_COLOR_BUFFER_BIT); SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); @@ -59,7 +54,7 @@ void CGame::Render() // if the S2 loading screen is shown, render only this until user clicks a mouse button if(showLoadScreen) { - splashBg_.DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y)); + splashBg_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); SDL_GL_SwapWindow(window_.get()); return; } @@ -94,11 +89,6 @@ void CGame::Render() { if(Menu->isActive()) { - // Draw menu background via OpenGL - int bgIdx = Menu->getBackground(); - (bgIdx == SPLASHSCREEN_MAINMENU ? menuBgMain_ : menuBgSub_) - .DrawFull(Rect(0, 0, GameResolution.x, GameResolution.y)); - // Draw UI overlay on top CSurface::Draw(Surf_Display, Menu->getSurface(), 0, 0); } } @@ -121,7 +111,7 @@ void CGame::Render() } } - // Cursor is drawn in RenderPresent via GL (after the Surf_Display overlay) + // Cursor is drawn in RenderPresent via GL #ifdef _ADMINMODE FrameCounter++; diff --git a/CIO/CControlContainer.h b/CIO/CControlContainer.h index d3661de..dacc91f 100644 --- a/CIO/CControlContainer.h +++ b/CIO/CControlContainer.h @@ -49,9 +49,9 @@ class CControlContainer void renderElements(); auto& getTextFields() { return textfields; } const auto& getTextFields() const { return textfields; } + int getBackground() const { return pic_background; } public: - int getBackground() const { return pic_background; } CControlContainer(int pic_background); CControlContainer(int pic_background, Extent borderBeginSize, Extent borderEndSize); ~CControlContainer() noexcept; diff --git a/CIO/CMenu.cpp b/CIO/CMenu.cpp index c77564d..969ba19 100644 --- a/CIO/CMenu.cpp +++ b/CIO/CMenu.cpp @@ -5,6 +5,7 @@ #include "CMenu.h" #include "../CGame.h" +#include "../Texture.h" #include "../globals.h" CMenu::CMenu(int pic_background) : CControlContainer(pic_background) {} @@ -14,7 +15,20 @@ bool CMenu::render() if(getBackground() < 0) return false; - // if we don't need to render, all is up to date, return true + // Lazy-load background texture from the palette-converted bitmap + if(!bgTexture_) + { + const int picIdx = getBackground(); + if(picIdx >= 0 && picIdx < static_cast(global::bmpArray.size()) && global::bmpArray[picIdx].surface) + { + bgTexture_ = std::make_unique(); + bgTexture_->load(global::bmpArray[picIdx].surface.get(), true); + } + } + + if(bgTexture_) + bgTexture_->Draw(Rect(0, 0, global::s2->getRes().x, global::s2->getRes().y)); + if(!needRender) return true; needRender = false; @@ -26,7 +40,6 @@ bool CMenu::render() return false; } - // Background is drawn via OpenGL in CGame::Render(); only draw UI elements here. renderElements(); return true; } diff --git a/CIO/CMenu.h b/CIO/CMenu.h index b03832a..59d8400 100644 --- a/CIO/CMenu.h +++ b/CIO/CMenu.h @@ -6,11 +6,15 @@ #pragma once #include "CControlContainer.h" +#include + +class Texture; class CMenu final : public CControlContainer { // if active is false, the menu will not be render within the game loop bool active = true; + mutable std::unique_ptr bgTexture_; bool render() final; diff --git a/CMakeLists.txt b/CMakeLists.txt index 0faeeac..8d0010f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,24 +11,6 @@ find_package(Boost 1.64 REQUIRED) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../libutil/cmake") -# glad: OpenGL loader (reused from s25client) -if(NOT TARGET glad) - set(RTTR_OGL_MAJOR 2) - set(RTTR_OGL_MINOR 0) - set(RTTR_OGL_ES 0) - set(RTTR_OGL_COMPAT 1) - set(RTTR_OGL_GL4ES 0) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../glad/openglCfg.hpp.cmake ${CMAKE_CURRENT_BINARY_DIR}/include/openglCfg.hpp @ONLY) - - set(GLAD_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../glad/OpenGL2.0Compat/src/glad.c) - set(GLAD_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/../glad/OpenGL2.0Compat/include) - add_library(glad STATIC ${GLAD_SRC}) - target_include_directories(glad PUBLIC ${GLAD_INCLUDE} ${CMAKE_CURRENT_BINARY_DIR}/include) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(glad PUBLIC ${CMAKE_DL_LIBS}) - endif() -endif() - add_subdirectory(SGE) option(RTTR_EDITOR_ADMINMODE "In admin mode there are some key combos to open debugger, resource viewer and so on" OFF) diff --git a/CSurface.h b/CSurface.h index b7ddeb9..3677579 100644 --- a/CSurface.h +++ b/CSurface.h @@ -45,7 +45,6 @@ class CSurface { return Draw(Surf_Dest.get(), Surf_Src.get(), X, Y, X2, Y2, W, H); } - static void DrawPixel_Color(SDL_Surface* screen, Position pos, Uint32 color); static void DrawPixel_RGB(SDL_Surface* screen, Position pos, Uint8 R, Uint8 G, Uint8 B); static void DrawPixel_RGB(SdlSurface& screen, Position pos, Uint8 R, Uint8 G, Uint8 B) diff --git a/GlTexture.h b/GlTexture.h deleted file mode 100644 index 584a45a..0000000 --- a/GlTexture.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2009 - 2021 Marc Vester (XaserLE) -// Copyright (C) 2009 - 2021 Settlers Freaks -// -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "Rect.h" -#include - -/// Wraps a GL texture with RAII and provides draw methods. -class GlTexture -{ -public: - GlTexture() = default; - ~GlTexture(); - - GlTexture(GlTexture&&) noexcept; - GlTexture& operator=(GlTexture&&) noexcept; - - // No copy - GlTexture(const GlTexture&) = delete; - GlTexture& operator=(const GlTexture&) = delete; - - /// Load from a 32-bit or 8-bit paletted SDL surface. - /// For 8-bit surfaces, optional colorkey is respected (keyed pixels become transparent). - bool load(SDL_Surface* surface, bool linear = false); - - /// Draw the texture stretched to fill the given rect. - void DrawFull(const Rect& destRect) const; - - /// Draw the texture at native size at the given position. - void Draw(int x, int y) const; - -private: - unsigned int texture_ = 0; - int width_ = 0, height_ = 0; - - void createTexture(const void* bgraPixels, int w, int h, bool linear); -}; diff --git a/GlTexture.cpp b/Texture.cpp similarity index 64% rename from GlTexture.cpp rename to Texture.cpp index ad41ceb..274ab81 100644 --- a/GlTexture.cpp +++ b/Texture.cpp @@ -1,61 +1,65 @@ -// Copyright (C) 2009 - 2021 Marc Vester (XaserLE) -// Copyright (C) 2009 - 2021 Settlers Freaks +// Copyright (C) 2026 - 2026 Settlers Freaks // // SPDX-License-Identifier: GPL-3.0-or-later -#include "GlTexture.h" +#include "Texture.h" #include +#include #include -GlTexture::~GlTexture() +Texture::~Texture() { if(texture_) glDeleteTextures(1, &texture_); } -GlTexture::GlTexture(GlTexture&& other) noexcept - : texture_(other.texture_), width_(other.width_), height_(other.height_) -{ - other.texture_ = 0; - other.width_ = 0; - other.height_ = 0; -} +Texture::Texture(Texture&& other) noexcept + : texture_(std::exchange(other.texture_, 0)), size_(std::exchange(other.size_, {0, 0})) +{} -GlTexture& GlTexture::operator=(GlTexture&& other) noexcept +Texture& Texture::operator=(Texture&& other) noexcept { if(this != &other) { if(texture_) glDeleteTextures(1, &texture_); - texture_ = other.texture_; - width_ = other.width_; - height_ = other.height_; - other.texture_ = 0; - other.width_ = 0; - other.height_ = 0; + texture_ = std::exchange(other.texture_, 0); + size_ = std::exchange(other.size_, {0, 0}); } return *this; } -void GlTexture::createTexture(const void* bgraPixels, int w, int h, bool linear) +void Texture::load(const void* bgraPixels, Extent size, bool filterLinear) { if(texture_) glDeleteTextures(1, &texture_); texture_ = 0; - width_ = height_ = 0; + size_ = {0, 0}; glGenTextures(1, &texture_); glBindTexture(GL_TEXTURE_2D, texture_); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterLinear ? GL_LINEAR : GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterLinear ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, bgraPixels); - width_ = w; - height_ = h; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.x, size.y, 0, GL_BGRA, GL_UNSIGNED_BYTE, bgraPixels); + size_ = size; +} + +void Texture::load(Extent size, bool filterLinear) +{ + load(nullptr, size, filterLinear); +} + +void Texture::upload(const void* bgraPixels) +{ + if(!texture_) + return; + glBindTexture(GL_TEXTURE_2D, texture_); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size_.x, size_.y, GL_BGRA, GL_UNSIGNED_BYTE, bgraPixels); } -bool GlTexture::load(SDL_Surface* surface, bool linear) +bool Texture::load(SDL_Surface* surface, bool filterLinear) { if(!surface) return false; @@ -68,7 +72,7 @@ bool GlTexture::load(SDL_Surface* surface, bool linear) SDL_Palette* pal = surface->format->palette; Uint32 ck; - const bool hasCK = (SDL_GetColorKey(surface, &ck) == 0); + const bool hasCK = SDL_GetColorKey(surface, &ck) == 0; const Uint8 ckIdx = hasCK ? static_cast(ck & 0xFF) : 0; SDL_LockSurface(surface); @@ -90,7 +94,7 @@ bool GlTexture::load(SDL_Surface* surface, bool linear) } SDL_UnlockSurface(surface); - createTexture(pixels.data(), w, h, linear); + load(pixels.data(), Extent(w, h), filterLinear); return true; } @@ -109,12 +113,12 @@ bool GlTexture::load(SDL_Surface* surface, bool linear) } SDL_UnlockSurface(converted); - createTexture(converted->pixels, converted->w, converted->h, linear); + load(converted->pixels, Extent(converted->w, converted->h), filterLinear); SDL_FreeSurface(converted); return true; } -void GlTexture::DrawFull(const Rect& destRect) const +void Texture::Draw(const Rect& destRect) const { if(!texture_) return; @@ -132,7 +136,7 @@ void GlTexture::DrawFull(const Rect& destRect) const glEnd(); } -void GlTexture::Draw(int x, int y) const +void Texture::Draw(Position pos) const { if(!texture_) return; @@ -140,12 +144,12 @@ void GlTexture::Draw(int x, int y) const glBindTexture(GL_TEXTURE_2D, texture_); glBegin(GL_QUADS); glTexCoord2f(0, 0); - glVertex2i(x, y); + glVertex2i(pos.x, pos.y); glTexCoord2f(1, 0); - glVertex2i(x + width_, y); + glVertex2i(pos.x + size_.x, pos.y); glTexCoord2f(1, 1); - glVertex2i(x + width_, y + height_); + glVertex2i(pos.x + size_.x, pos.y + size_.y); glTexCoord2f(0, 1); - glVertex2i(x, y + height_); + glVertex2i(pos.x, pos.y + size_.y); glEnd(); } diff --git a/Texture.h b/Texture.h new file mode 100644 index 0000000..ed8a99f --- /dev/null +++ b/Texture.h @@ -0,0 +1,50 @@ +// Copyright (C) 2026 - 2026 Settlers Freaks +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "Rect.h" +#include +#include + +/// Wraps a GL texture with RAII and provides draw methods. +class Texture +{ +public: + Texture() = default; + ~Texture(); + + Texture(Texture&&) noexcept; + Texture& operator=(Texture&&) noexcept; + + // No copy + Texture(const Texture&) = delete; + Texture& operator=(const Texture&) = delete; + + /// Load from a 32-bit or 8-bit paletted SDL surface. + /// For 8-bit surfaces, optional colorkey is respected (keyed pixels become transparent). + bool load(SDL_Surface* surface, bool filterLinear = false); + + /// Create an empty texture of the given size (for use as a render-target). + void load(Extent size, bool filterLinear = false); + + /// Upload new pixel data to an existing texture (glTexSubImage2D). + void upload(const void* bgraPixels); + + /// Draw the texture stretched to fill the given rect. + void Draw(const Rect& destRect) const; + + /// Draw the texture at native size at the given position. + void Draw(Position pos) const; + + /// Returns true if the texture has been created. + bool isValid() const { return texture_ != 0; } + +private: + unsigned int texture_ = 0; + Extent size_; + + /// Internal: create or recreate texture from raw BGRA pixel data. + void load(const void* bgraPixels, Extent size, bool filterLinear); +}; From 413682bfa302d3b9283d3b09027684ab6a986553 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Wed, 1 Jul 2026 18:36:14 +0200 Subject: [PATCH 06/12] Review feedback and res switch/resize --- CGame.cpp | 6 ++---- CGame_Init.cpp | 21 ++++++++++++++++++++- CGame_Render.cpp | 5 ----- CIO/CMenu.cpp | 1 - CIO/CMenu.h | 2 ++ CMakeLists.txt | 2 -- Texture.cpp | 2 +- Texture.h | 2 +- 8 files changed, 26 insertions(+), 15 deletions(-) diff --git a/CGame.cpp b/CGame.cpp index c5e60a8..18f90ed 100644 --- a/CGame.cpp +++ b/CGame.cpp @@ -80,10 +80,8 @@ void CGame::RenderPresent() displayTexture_.upload(Surf_Display->pixels); displayTexture_.Draw(Rect(0, 0, GameResolution.x, GameResolution.y)); - { - const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_; - cursorImg.Draw(Cursor.pos); - } + const auto& cursorImg = Cursor.clicked ? (Cursor.button.right ? cross_ : cursorClicked_) : cursor_; + cursorImg.Draw(Cursor.pos); SDL_GL_SwapWindow(window_.get()); } diff --git a/CGame_Init.cpp b/CGame_Init.cpp index 3738beb..9f8ebb7 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -19,6 +19,14 @@ bool CGame::ReCreateWindow() { suppressResizeEvents_ = 3; displayTexture_ = Texture(); + cursor_ = Texture(); + cursorClicked_ = Texture(); + cross_ = Texture(); + splashBg_ = Texture(); + + for(auto& menu : Menus) + menu->resetBgTexture(); + if(glContext_) { SDL_GL_DeleteContext(glContext_); @@ -47,13 +55,23 @@ bool CGame::ReCreateWindow() return false; SetAppIcon(); + + auto loadGlTex = [&](unsigned idx, Texture& tex, bool linear = false) { + if(idx < global::bmpArray.size() && global::bmpArray[idx].surface) + tex.load(global::bmpArray[idx].surface.get(), linear); + }; + loadGlTex(CURSOR, cursor_); + loadGlTex(CURSOR_CLICKED, cursorClicked_); + loadGlTex(CROSS, cross_); + loadGlTex(SPLASHSCREEN_LOADING_S2SCREEN, splashBg_, true); + return true; } void CGame::RecreateDisplayResources() { Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true); - displayTexture_.load(GameResolution); + displayTexture_.createEmpty(GameResolution); } void CGame::setGLViewport() @@ -69,6 +87,7 @@ void CGame::setGLViewport() void CGame::UpdateDisplaySize(const Extent& newSize) { GameResolution = newSize; + setGLViewport(); RecreateDisplayResources(); for(auto& menu : Menus) menu->resetSurface(); diff --git a/CGame_Render.cpp b/CGame_Render.cpp index 6297f8e..cc67b01 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -41,7 +41,6 @@ void CGame::Render() { suppressResizeEvents_ = 0; - setGLViewport(); glClear(GL_COLOR_BUFFER_BIT); SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); @@ -88,9 +87,7 @@ void CGame::Render() for(auto& Menu : Menus) { if(Menu->isActive()) - { CSurface::Draw(Surf_Display, Menu->getSurface(), 0, 0); - } } // render windows ordered by priority @@ -111,8 +108,6 @@ void CGame::Render() } } - // Cursor is drawn in RenderPresent via GL - #ifdef _ADMINMODE FrameCounter++; #endif diff --git a/CIO/CMenu.cpp b/CIO/CMenu.cpp index 969ba19..af68a28 100644 --- a/CIO/CMenu.cpp +++ b/CIO/CMenu.cpp @@ -15,7 +15,6 @@ bool CMenu::render() if(getBackground() < 0) return false; - // Lazy-load background texture from the palette-converted bitmap if(!bgTexture_) { const int picIdx = getBackground(); diff --git a/CIO/CMenu.h b/CIO/CMenu.h index 59d8400..8c4a6c8 100644 --- a/CIO/CMenu.h +++ b/CIO/CMenu.h @@ -19,6 +19,8 @@ class CMenu final : public CControlContainer bool render() final; public: + void resetBgTexture() { bgTexture_.reset(); } + CMenu(int pic_background); void setActive() { active = true; }; void setInactive() { active = false; }; diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d0010f..524f7d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,6 @@ project(s25edit) find_package(Boost 1.64 REQUIRED) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../libutil/cmake") - add_subdirectory(SGE) option(RTTR_EDITOR_ADMINMODE "In admin mode there are some key combos to open debugger, resource viewer and so on" OFF) diff --git a/Texture.cpp b/Texture.cpp index 274ab81..5ff6a56 100644 --- a/Texture.cpp +++ b/Texture.cpp @@ -46,7 +46,7 @@ void Texture::load(const void* bgraPixels, Extent size, bool filterLinear) size_ = size; } -void Texture::load(Extent size, bool filterLinear) +void Texture::createEmpty(Extent size, bool filterLinear) { load(nullptr, size, filterLinear); } diff --git a/Texture.h b/Texture.h index ed8a99f..3444b2d 100644 --- a/Texture.h +++ b/Texture.h @@ -27,7 +27,7 @@ class Texture bool load(SDL_Surface* surface, bool filterLinear = false); /// Create an empty texture of the given size (for use as a render-target). - void load(Extent size, bool filterLinear = false); + void createEmpty(Extent size, bool filterLinear = false); /// Upload new pixel data to an existing texture (glTexSubImage2D). void upload(const void* bgraPixels); From 97c5a47ce35f26756fba15d70ff5a2bda8991a48 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Wed, 1 Jul 2026 19:52:36 +0200 Subject: [PATCH 07/12] Fix fullscreen res switching --- CGame.h | 2 +- CGame_Event.cpp | 16 ++++++++++------ CGame_Init.cpp | 36 +++++++++++++++++++++++++++++++----- CGame_Render.cpp | 8 ++------ 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/CGame.h b/CGame.h index 68172e3..c6cbd4f 100644 --- a/CGame.h +++ b/CGame.h @@ -44,7 +44,7 @@ class CGame CFont lastFps; Uint32 lastFrameTime = 0; - unsigned suppressResizeEvents_ = 0; + Extent lastSetResolution_ = Extent{0, 0}; ///< Last resolution we tried to apply (to avoid infinite resize loops) // GL textures for splash screen and cursor Texture splashBg_; diff --git a/CGame_Event.cpp b/CGame_Event.cpp index ed9570c..f2ca5cb 100644 --- a/CGame_Event.cpp +++ b/CGame_Event.cpp @@ -388,12 +388,16 @@ void CGame::EventHandling(SDL_Event* Event) { if(Event->window.event == SDL_WINDOWEVENT_RESIZED) { - if(suppressResizeEvents_ > 0) - { - suppressResizeEvents_--; - break; // Skip stale event from our own window recreation - } - UpdateDisplaySize(Extent(Event->window.data1, Event->window.data2)); + // In fullscreen the compositor (e.g. Wayland) may report the + // native desktop size even though we render at GameResolution. + // Keep the logical resolution unchanged and scale the output. + if(fullscreen) + break; + const Extent newSize(Event->window.data1, Event->window.data2); + // Ignore stale resize events generated by our own window recreation. + if(newSize == lastSetResolution_) + break; + UpdateDisplaySize(newSize); } break; } diff --git a/CGame_Init.cpp b/CGame_Init.cpp index 9f8ebb7..dc7cfcb 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -17,7 +17,6 @@ bool CGame::ReCreateWindow() { - suppressResizeEvents_ = 3; displayTexture_ = Texture(); cursor_ = Texture(); cursorClicked_ = Texture(); @@ -33,9 +32,14 @@ bool CGame::ReCreateWindow() glContext_ = nullptr; } window_.reset(); + + Uint32 windowFlags = SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL; + if(fullscreen) + windowFlags |= SDL_WINDOW_FULLSCREEN; + else + windowFlags |= SDL_WINDOW_RESIZABLE; window_.reset(SDL_CreateWindow("Return to the Roots Map editor [BETA]", SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, GameResolution.x, GameResolution.y, - (fullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE) | SDL_WINDOW_OPENGL)); + SDL_WINDOWPOS_CENTERED, GameResolution.x, GameResolution.y, windowFlags)); if(!window_) return false; @@ -48,12 +52,27 @@ bool CGame::ReCreateWindow() glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glClearColor(0, 0, 0, 1); - setGLViewport(); RecreateDisplayResources(); if(!displayTexture_.isValid() || !Surf_Display) return false; + if(fullscreen) + { + SDL_DisplayMode dm; + SDL_zero(dm); + dm.w = static_cast(GameResolution.x); + dm.h = static_cast(GameResolution.y); + dm.format = 0; // let SDL pick a supported format + dm.refresh_rate = 0; + if(SDL_SetWindowDisplayMode(window_.get(), &dm) != 0) + std::cerr << "SDL_SetWindowDisplayMode failed: " << SDL_GetError() << std::endl; + } + SDL_ShowWindow(window_.get()); + + setGLViewport(); + + lastSetResolution_ = GameResolution; SetAppIcon(); auto loadGlTex = [&](unsigned idx, Texture& tex, bool linear = false) { @@ -76,7 +95,13 @@ void CGame::RecreateDisplayResources() void CGame::setGLViewport() { - glViewport(0, 0, GameResolution.x, GameResolution.y); + if(!window_) + return; + int w = 0, h = 0; + SDL_GL_GetDrawableSize(window_.get(), &w, &h); + if(w == 0 || h == 0) + return; + glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, GameResolution.x, GameResolution.y, 0, -1, 1); @@ -87,6 +112,7 @@ void CGame::setGLViewport() void CGame::UpdateDisplaySize(const Extent& newSize) { GameResolution = newSize; + lastSetResolution_ = GameResolution; setGLViewport(); RecreateDisplayResources(); for(auto& menu : Menus) diff --git a/CGame_Render.cpp b/CGame_Render.cpp index cc67b01..c0facd8 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -39,16 +39,12 @@ void CGame::SetAppIcon() void CGame::Render() { - suppressResizeEvents_ = 0; - glClear(GL_COLOR_BUFFER_BIT); SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); - if(Extent(Surf_Display->w, Surf_Display->h) != GameResolution - || fullscreen != ((SDL_GetWindowFlags(window_.get()) & SDL_WINDOW_FULLSCREEN) != 0)) - { + const bool isFullscreen = (SDL_GetWindowFlags(window_.get()) & SDL_WINDOW_FULLSCREEN) != 0; + if(lastSetResolution_ != GameResolution || fullscreen != isFullscreen) ReCreateWindow(); - } // if the S2 loading screen is shown, render only this until user clicks a mouse button if(showLoadScreen) From 05a71d83ab9495e143141d83a22a1cb9b0e09272 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Thu, 2 Jul 2026 14:59:11 +0200 Subject: [PATCH 08/12] fullscreen res switching without ReCreateWindow() --- CGame.h | 7 ++-- CGame_Event.cpp | 10 ++--- CGame_Init.cpp | 102 ++++++++++++++++++++++++----------------------- CGame_Render.cpp | 4 +- 4 files changed, 63 insertions(+), 60 deletions(-) diff --git a/CGame.h b/CGame.h index c6cbd4f..4809551 100644 --- a/CGame.h +++ b/CGame.h @@ -44,7 +44,8 @@ class CGame CFont lastFps; Uint32 lastFrameTime = 0; - Extent lastSetResolution_ = Extent{0, 0}; ///< Last resolution we tried to apply (to avoid infinite resize loops) + Extent appliedResolution_ = Extent{0, 0}; ///< Last resolution we applied to the window/display + bool appliedFullscreen_ = false; ///< Last fullscreen state we applied // GL textures for splash screen and cursor Texture splashBg_; @@ -74,8 +75,9 @@ class CGame std::unique_ptr MapObj; void SetAppIcon(); - void RecreateDisplayResources(); void setGLViewport(); + bool CreateGLWindow(); + void ApplyWindowChanges(); public: void LoadSettings(); @@ -87,7 +89,6 @@ class CGame int Execute(); bool Init(); - bool ReCreateWindow(); void UpdateDisplaySize(const Extent& newSize); void EventHandling(SDL_Event* Event); diff --git a/CGame_Event.cpp b/CGame_Event.cpp index f2ca5cb..ab3c082 100644 --- a/CGame_Event.cpp +++ b/CGame_Event.cpp @@ -388,14 +388,14 @@ void CGame::EventHandling(SDL_Event* Event) { if(Event->window.event == SDL_WINDOWEVENT_RESIZED) { - // In fullscreen the compositor (e.g. Wayland) may report the - // native desktop size even though we render at GameResolution. - // Keep the logical resolution unchanged and scale the output. + // In fullscreen the compositor (e.g. Wayland) may report a size + // different from the one we requested. We already applied the + // resolution ourselves, so don't let the event override it. if(fullscreen) break; const Extent newSize(Event->window.data1, Event->window.data2); - // Ignore stale resize events generated by our own window recreation. - if(newSize == lastSetResolution_) + // Ignore events matching the resolution we already applied. + if(newSize == appliedResolution_) break; UpdateDisplaySize(newSize); } diff --git a/CGame_Init.cpp b/CGame_Init.cpp index dc7cfcb..f7ccf9a 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -13,33 +13,15 @@ #include "lua/GameDataLoader.h" #include #include -#include -bool CGame::ReCreateWindow() +bool CGame::CreateGLWindow() { - displayTexture_ = Texture(); - cursor_ = Texture(); - cursorClicked_ = Texture(); - cross_ = Texture(); - splashBg_ = Texture(); - - for(auto& menu : Menus) - menu->resetBgTexture(); - - if(glContext_) - { - SDL_GL_DeleteContext(glContext_); - glContext_ = nullptr; - } - window_.reset(); + if(window_) + return false; - Uint32 windowFlags = SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL; - if(fullscreen) - windowFlags |= SDL_WINDOW_FULLSCREEN; - else - windowFlags |= SDL_WINDOW_RESIZABLE; window_.reset(SDL_CreateWindow("Return to the Roots Map editor [BETA]", SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, GameResolution.x, GameResolution.y, windowFlags)); + SDL_WINDOWPOS_CENTERED, GameResolution.x, GameResolution.y, + SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE)); if(!window_) return false; @@ -53,10 +35,24 @@ bool CGame::ReCreateWindow() glDisable(GL_DEPTH_TEST); glClearColor(0, 0, 0, 1); - RecreateDisplayResources(); + SDL_ShowWindow(window_.get()); + + ApplyWindowChanges(); if(!displayTexture_.isValid() || !Surf_Display) return false; + SetAppIcon(); + + return true; +} + +void CGame::ApplyWindowChanges() +{ + if(!window_) + return; + if(GameResolution == appliedResolution_ && fullscreen == appliedFullscreen_) + return; + if(fullscreen) { SDL_DisplayMode dm; @@ -67,30 +63,31 @@ bool CGame::ReCreateWindow() dm.refresh_rate = 0; if(SDL_SetWindowDisplayMode(window_.get(), &dm) != 0) std::cerr << "SDL_SetWindowDisplayMode failed: " << SDL_GetError() << std::endl; - } - SDL_ShowWindow(window_.get()); - setGLViewport(); - - lastSetResolution_ = GameResolution; - SetAppIcon(); - - auto loadGlTex = [&](unsigned idx, Texture& tex, bool linear = false) { - if(idx < global::bmpArray.size() && global::bmpArray[idx].surface) - tex.load(global::bmpArray[idx].surface.get(), linear); - }; - loadGlTex(CURSOR, cursor_); - loadGlTex(CURSOR_CLICKED, cursorClicked_); - loadGlTex(CROSS, cross_); - loadGlTex(SPLASHSCREEN_LOADING_S2SCREEN, splashBg_, true); - - return true; -} + const Uint32 flags = SDL_GetWindowFlags(window_.get()); + if(!(flags & SDL_WINDOW_FULLSCREEN)) + { + if(SDL_SetWindowFullscreen(window_.get(), SDL_WINDOW_FULLSCREEN) != 0) + std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + } else if(GameResolution != appliedResolution_) + { + // Already fullscreen and the resolution changed. Toggle fullscreen off and + // back on so SDL/Wayland actually applies the new display mode. + if(SDL_SetWindowFullscreen(window_.get(), 0) != 0) + std::cerr << "SDL_SetWindowFullscreen(0) failed: " << SDL_GetError() << std::endl; + SDL_SetWindowSize(window_.get(), GameResolution.x, GameResolution.y); + if(SDL_SetWindowFullscreen(window_.get(), SDL_WINDOW_FULLSCREEN) != 0) + std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + } + } else + { + if(SDL_SetWindowFullscreen(window_.get(), 0) != 0) + std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + SDL_SetWindowSize(window_.get(), GameResolution.x, GameResolution.y); + SDL_SetWindowPosition(window_.get(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + } -void CGame::RecreateDisplayResources() -{ - Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true); - displayTexture_.createEmpty(GameResolution); + UpdateDisplaySize(GameResolution); } void CGame::setGLViewport() @@ -112,11 +109,18 @@ void CGame::setGLViewport() void CGame::UpdateDisplaySize(const Extent& newSize) { GameResolution = newSize; - lastSetResolution_ = GameResolution; + appliedResolution_ = GameResolution; + appliedFullscreen_ = fullscreen; + + Surf_Display = makeRGBSurface(GameResolution.x, GameResolution.y, true); + displayTexture_.createEmpty(GameResolution); + setGLViewport(); - RecreateDisplayResources(); for(auto& menu : Menus) + { + menu->resetBgTexture(); menu->resetSurface(); + } for(auto& wnd : Windows) wnd->resetSurface(); } @@ -128,7 +132,7 @@ bool CGame::Init() SDL_ShowCursor(SDL_DISABLE); std::cout << "Create Window..."; - if(!ReCreateWindow()) + if(!CreateGLWindow()) { std::cout << "failure"; return false; diff --git a/CGame_Render.cpp b/CGame_Render.cpp index c0facd8..50ff896 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -42,9 +42,7 @@ void CGame::Render() glClear(GL_COLOR_BUFFER_BIT); SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); - const bool isFullscreen = (SDL_GetWindowFlags(window_.get()) & SDL_WINDOW_FULLSCREEN) != 0; - if(lastSetResolution_ != GameResolution || fullscreen != isFullscreen) - ReCreateWindow(); + ApplyWindowChanges(); // if the S2 loading screen is shown, render only this until user clicks a mouse button if(showLoadScreen) From 646a83cbbc6722c2abdaf334258bca9276bd1eef Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Fri, 3 Jul 2026 00:20:58 +0200 Subject: [PATCH 09/12] Remove unncessecary GL prefixes --- CGame.h | 4 ++-- CGame_Init.cpp | 8 ++++---- Texture.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CGame.h b/CGame.h index 4809551..1080a02 100644 --- a/CGame.h +++ b/CGame.h @@ -47,7 +47,7 @@ class CGame Extent appliedResolution_ = Extent{0, 0}; ///< Last resolution we applied to the window/display bool appliedFullscreen_ = false; ///< Last fullscreen state we applied - // GL textures for splash screen and cursor + // Textures for splash screen and cursor Texture splashBg_; Texture cursor_; Texture cursorClicked_; @@ -76,7 +76,7 @@ class CGame void SetAppIcon(); void setGLViewport(); - bool CreateGLWindow(); + bool CreateWindow(); void ApplyWindowChanges(); public: diff --git a/CGame_Init.cpp b/CGame_Init.cpp index f7ccf9a..b6c24e7 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -14,7 +14,7 @@ #include #include -bool CGame::CreateGLWindow() +bool CGame::CreateWindow() { if(window_) return false; @@ -132,7 +132,7 @@ bool CGame::Init() SDL_ShowCursor(SDL_DISABLE); std::cout << "Create Window..."; - if(!CreateGLWindow()) + if(!CreateWindow()) { std::cout << "failure"; return false; @@ -164,7 +164,7 @@ bool CGame::Init() } } - // Create GL texture for splash background + // Create texture for splash background splashBg_.load(global::bmpArray[SPLASHSCREEN_LOADING_S2SCREEN].surface.get(), true); // std::cout << "\nShow loading screen..."; @@ -326,7 +326,7 @@ bool CGame::Init() // create the mainmenu callback::mainmenu(INITIALIZING_CALL); - // Create GL textures for cursor + // Create textures for cursor cursor_.load(global::bmpArray[CURSOR].surface.get()); cursorClicked_.load(global::bmpArray[CURSOR_CLICKED].surface.get()); cross_.load(global::bmpArray[CROSS].surface.get()); diff --git a/Texture.h b/Texture.h index 3444b2d..5c19b8c 100644 --- a/Texture.h +++ b/Texture.h @@ -8,7 +8,7 @@ #include #include -/// Wraps a GL texture with RAII and provides draw methods. +/// Wraps a texture with RAII and provides draw methods. class Texture { public: From fd498587a19e580e4baaabfed5f672c99641e065 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Fri, 3 Jul 2026 00:37:19 +0200 Subject: [PATCH 10/12] Move ApplyWindowChanges() out of Render() --- CGame.h | 4 +++- CGame_Event.cpp | 1 + CGame_Init.cpp | 2 -- CGame_Render.cpp | 2 -- callbacks.cpp | 1 + 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CGame.h b/CGame.h index 1080a02..f4a78e5 100644 --- a/CGame.h +++ b/CGame.h @@ -77,9 +77,11 @@ class CGame void SetAppIcon(); void setGLViewport(); bool CreateWindow(); - void ApplyWindowChanges(); public: + // Apply current GameResolution and fullscreen settings to the window/display. + void ApplyWindowChanges(); + void LoadSettings(); void SaveSettings() const; diff --git a/CGame_Event.cpp b/CGame_Event.cpp index ab3c082..7839c79 100644 --- a/CGame_Event.cpp +++ b/CGame_Event.cpp @@ -68,6 +68,7 @@ void CGame::EventHandling(SDL_Event* Event) if(Event->key.keysym.mod & KMOD_ALT) { fullscreen = !fullscreen; + ApplyWindowChanges(); SaveSettings(); } break; diff --git a/CGame_Init.cpp b/CGame_Init.cpp index b6c24e7..e160e18 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -50,8 +50,6 @@ void CGame::ApplyWindowChanges() { if(!window_) return; - if(GameResolution == appliedResolution_ && fullscreen == appliedFullscreen_) - return; if(fullscreen) { diff --git a/CGame_Render.cpp b/CGame_Render.cpp index 50ff896..93da104 100644 --- a/CGame_Render.cpp +++ b/CGame_Render.cpp @@ -42,8 +42,6 @@ void CGame::Render() glClear(GL_COLOR_BUFFER_BIT); SDL_FillRect(Surf_Display.get(), nullptr, SDL_MapRGBA(Surf_Display->format, 0, 0, 0, 0)); - ApplyWindowChanges(); - // if the S2 loading screen is shown, render only this until user clicks a mouse button if(showLoadScreen) { diff --git a/callbacks.cpp b/callbacks.cpp index 7c69194..6e16659 100644 --- a/callbacks.cpp +++ b/callbacks.cpp @@ -319,6 +319,7 @@ void callback::submenuOptions(int Param) case GRAPHICS_CHANGE: assert(SubMenu); + global::s2->ApplyWindowChanges(); global::s2->SaveSettings(); SubMenu->setWaste(); TextResolution = nullptr; From 4c107b9755b316bca11c22bec318e248e428b5a2 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Fri, 3 Jul 2026 01:08:05 +0200 Subject: [PATCH 11/12] return on errors --- CGame_Init.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CGame_Init.cpp b/CGame_Init.cpp index e160e18..3583f7c 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -60,27 +60,42 @@ void CGame::ApplyWindowChanges() dm.format = 0; // let SDL pick a supported format dm.refresh_rate = 0; if(SDL_SetWindowDisplayMode(window_.get(), &dm) != 0) + { std::cerr << "SDL_SetWindowDisplayMode failed: " << SDL_GetError() << std::endl; + return; + } const Uint32 flags = SDL_GetWindowFlags(window_.get()); if(!(flags & SDL_WINDOW_FULLSCREEN)) { if(SDL_SetWindowFullscreen(window_.get(), SDL_WINDOW_FULLSCREEN) != 0) + { std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + return; + } } else if(GameResolution != appliedResolution_) { // Already fullscreen and the resolution changed. Toggle fullscreen off and // back on so SDL/Wayland actually applies the new display mode. if(SDL_SetWindowFullscreen(window_.get(), 0) != 0) + { std::cerr << "SDL_SetWindowFullscreen(0) failed: " << SDL_GetError() << std::endl; + return; + } SDL_SetWindowSize(window_.get(), GameResolution.x, GameResolution.y); if(SDL_SetWindowFullscreen(window_.get(), SDL_WINDOW_FULLSCREEN) != 0) + { std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + return; + } } } else { if(SDL_SetWindowFullscreen(window_.get(), 0) != 0) + { std::cerr << "SDL_SetWindowFullscreen failed: " << SDL_GetError() << std::endl; + return; + } SDL_SetWindowSize(window_.get(), GameResolution.x, GameResolution.y); SDL_SetWindowPosition(window_.get(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); } From 1ba679b01df7c14938119a5143aa9e9dac779bc5 Mon Sep 17 00:00:00 2001 From: Morgan Christiansson Date: Fri, 3 Jul 2026 15:18:46 +0200 Subject: [PATCH 12/12] Remove resetBgTexture() --- CGame_Init.cpp | 1 - CIO/CMenu.h | 2 -- 2 files changed, 3 deletions(-) diff --git a/CGame_Init.cpp b/CGame_Init.cpp index 3583f7c..7f437db 100644 --- a/CGame_Init.cpp +++ b/CGame_Init.cpp @@ -131,7 +131,6 @@ void CGame::UpdateDisplaySize(const Extent& newSize) setGLViewport(); for(auto& menu : Menus) { - menu->resetBgTexture(); menu->resetSurface(); } for(auto& wnd : Windows) diff --git a/CIO/CMenu.h b/CIO/CMenu.h index 8c4a6c8..59d8400 100644 --- a/CIO/CMenu.h +++ b/CIO/CMenu.h @@ -19,8 +19,6 @@ class CMenu final : public CControlContainer bool render() final; public: - void resetBgTexture() { bgTexture_.reset(); } - CMenu(int pic_background); void setActive() { active = true; }; void setInactive() { active = false; };