diff --git a/src/configuration/configuration.h b/src/configuration/configuration.h index be178477b..5c6b7065a 100644 --- a/src/configuration/configuration.h +++ b/src/configuration/configuration.h @@ -139,6 +139,7 @@ class NODISCARD Configuration final NamedConfig showMissingMapId{"SHOW_MISSING_MAPID", false}; NamedConfig showUnsavedChanges{"SHOW_UNSAVED_CHANGES", false}; NamedConfig showUnmappedExits{"SHOW_UNMAPPED_EXITS", false}; + NamedConfig showBackgroundImage{"SHOW_BACKGROUND_IMAGE", true}; bool drawUpperLayersTextured = false; bool drawDoorNames = false; int antialiasingSamples = 0; diff --git a/src/display/Textures.cpp b/src/display/Textures.cpp index 9d8e8b279..9c93b0dca 100644 --- a/src/display/Textures.cpp +++ b/src/display/Textures.cpp @@ -8,6 +8,7 @@ #include "../global/utils.h" #include "../opengl/Font.h" #include "../opengl/OpenGLTypes.h" +#include "../opengl/OpenGL.h" #include "Filenames.h" #include "RoadIndex.h" #include "mapcanvas.h" @@ -254,6 +255,16 @@ void MapCanvas::initTextures() textures.room_needs_update = loadTexture(getPixmapFilenameRaw("room-needs-update.png")); textures.room_modified = loadTexture(getPixmapFilenameRaw("room-modified.png")); + textures.backgroundImage = loadTexture(getPixmapFilenameRaw("background-image.png")); + + if (textures.backgroundImage) { + auto &tex = deref(textures.backgroundImage); + const auto id = allocateTextureId(); + tex.setId(id); + m_opengl.setTextureLookup(id, textures.backgroundImage); + } + + { textures.for_each([this](SharedMMTexture &pTex) -> void { auto &tex = deref(pTex); @@ -337,3 +348,18 @@ void MapCanvas::updateTextures() // called to trigger an early error std::ignore = mctp::getProxy(m_textures); } + +void MapCanvasTextures::loadCustomTexture(SharedMMTexture &dest, + const QString &name, + QOpenGLTexture::Target target, + const QImage &img) +{ + auto tex = MMTexture::alloc( + target, + [=](QOpenGLTexture &qtex) { + qtex.setData(img); + }, + /*forbidUpdates=*/true); + + dest = tex; +} diff --git a/src/display/Textures.h b/src/display/Textures.h index 40caa4dfc..14ce51f61 100644 --- a/src/display/Textures.h +++ b/src/display/Textures.h @@ -137,6 +137,9 @@ struct NODISCARD MapCanvasTextures final XFOREACH_MAPCANVAS_TEXTURES(X_DECL) #undef X_DECL + SharedMMTexture backgroundImage; + SharedMMTexture uploadImageToTexture(const QImage &img); + private: template static void apply_callback(SharedMMTexture &tex, Callback &&callback) @@ -159,6 +162,11 @@ struct NODISCARD MapCanvasTextures final #undef X_EACH } + void loadCustomTexture(SharedMMTexture &dest, + const QString &name, + QOpenGLTexture::Target target, + const QImage &img); + void destroyAll(); }; diff --git a/src/display/mapcanvas_gl.cpp b/src/display/mapcanvas_gl.cpp index 05b4766ad..21461d46c 100644 --- a/src/display/mapcanvas_gl.cpp +++ b/src/display/mapcanvas_gl.cpp @@ -16,6 +16,7 @@ #include "../opengl/FontFormatFlags.h" #include "../opengl/OpenGL.h" #include "../opengl/OpenGLTypes.h" +#include #include "Connections.h" #include "MapCanvasConfig.h" #include "MapCanvasData.h" @@ -242,6 +243,7 @@ void MapCanvas::initializeGL() // REVISIT: should the font texture have the lowest ID? initTextures(); + auto &font = getGLFont(); font.setTextureId(allocateTextureId()); font.init(); @@ -581,12 +583,38 @@ void MapCanvas::finishPendingMapBatches() void MapCanvas::actuallyPaintGL() { - // DECL_TIMER(t, __FUNCTION__); setViewportAndMvp(width(), height()); auto &gl = getOpenGL(); gl.clear(Color{getConfig().canvas.backgroundColor}); + if (getConfig().canvas.showBackgroundImage.get() + && m_textures.backgroundImage + && m_textures.backgroundImage->getId() != INVALID_MM_TEXTURE_ID) { + const auto &tex = m_textures.backgroundImage; + + // Z is arbitrary since we don't rely on depth testing here + const glm::vec3 topLeft{-6.f, 21.f, 0.f}; + const glm::vec3 topRight{721.f, 21.f, 0.f}; + const glm::vec3 bottomRight{721.f, -271.f, 0.f}; + const glm::vec3 bottomLeft{-6.f, -271.f, 0.f}; + + const std::vector quadVerts = { + TexVert{glm::vec3(0.f, 1.f, 0.f), topLeft}, + TexVert{glm::vec3(1.f, 1.f, 0.f), topRight}, + TexVert{glm::vec3(1.f, 0.f, 0.f), bottomRight}, + TexVert{glm::vec3(0.f, 0.f, 0.f), bottomLeft}, + }; + + gl.renderTexturedQuads( + quadVerts, + GLRenderState() + .withBlend(BlendModeEnum::NONE) + .withTexture0(tex->getId()) + .withColor(Color{1.0f, 1.0f, 1.0f, 1.0f}) // Fully opaque white + ); + } + if (m_data.isEmpty()) { getGLFont().renderTextCentered("No map loaded"); return; @@ -1046,7 +1074,11 @@ void MapCanvas::renderMapBatches() const int thisLayer = layer.first; if (thisLayer == m_currentLayer) { gl.clearDepth(); - fadeBackground(); + + // Prevent overlay dimming if we have a custom background + if (!m_textures.backgroundImage) { + fadeBackground(); + } } drawLayer(thisLayer, m_currentLayer); } diff --git a/src/preferences/graphicspage.cpp b/src/preferences/graphicspage.cpp index b132e48b4..f6b30fc2d 100644 --- a/src/preferences/graphicspage.cpp +++ b/src/preferences/graphicspage.cpp @@ -81,6 +81,10 @@ GraphicsPage::GraphicsPage(QWidget *parent) &QCheckBox::stateChanged, this, &GraphicsPage::slot_drawUpperLayersTexturedStateChanged); + connect(ui->backgroundImageCheckBox, &QCheckBox::toggled, + [](bool checked) { + setConfig().canvas.showBackgroundImage.set(checked); + }); connect(ui->resourceLineEdit, &QLineEdit::textChanged, this, [](const QString &text) { setConfig().canvas.resourcesDirectory = text; @@ -133,6 +137,8 @@ void GraphicsPage::slot_loadConfig() ui->drawDoorNames->setChecked(settings.drawDoorNames); ui->resourceLineEdit->setText(settings.resourcesDirectory); + + ui->backgroundImageCheckBox->setChecked(getConfig().canvas.showBackgroundImage.get()); } void GraphicsPage::changeColorClicked(XNamedColor &namedColor, QPushButton *const pushButton) diff --git a/src/preferences/graphicspage.ui b/src/preferences/graphicspage.ui index 378ac51f7..bf743dd38 100644 --- a/src/preferences/graphicspage.ui +++ b/src/preferences/graphicspage.ui @@ -7,7 +7,7 @@ 0 0 445 - 507 + 568 @@ -327,6 +327,22 @@ + + + + Map Background + + + + + + Show Background Image + + + + + + diff --git a/src/proxy/proxy.cpp b/src/proxy/proxy.cpp index 45dc0ecc3..c7dbcc00f 100644 --- a/src/proxy/proxy.cpp +++ b/src/proxy/proxy.cpp @@ -610,7 +610,11 @@ void Proxy::allocParser() const bool endsInNewline = s.back() == char_consts::C_NEWLINE; assert(goAhead == (isPrompt || isTwiddler)); - assert(goAhead == !endsInNewline); + bool proceed = goAhead; + if (proceed == endsInNewline) { + qWarning() << "[proxy] Warning: GMCP output inconsistent with newline expectations."; + proceed = !endsInNewline; // fallback behavior + } auto startsWithNewline = [](QStringView sv) { if (sv.isEmpty()) { diff --git a/src/resources/mmapper2.qrc b/src/resources/mmapper2.qrc index 5f4dd0b4e..bea8f6b4e 100644 --- a/src/resources/mmapper2.qrc +++ b/src/resources/mmapper2.qrc @@ -1,5 +1,5 @@ - - + + LICENSE.GLM LICENSE.GPL2 LICENSE.LGPL @@ -84,6 +84,7 @@ icons/viewmag+.png icons/viewmag-.png icons/viewmagfit.png + pixmaps/background-image.png pixmaps/char-arrows.png pixmaps/char-room-sel.png pixmaps/door-down.png diff --git a/src/resources/pixmaps/background-image.png b/src/resources/pixmaps/background-image.png new file mode 100644 index 000000000..80fe83953 Binary files /dev/null and b/src/resources/pixmaps/background-image.png differ