Skip to content

Conversation

@Halvance
Copy link

@Halvance Halvance commented Dec 17, 2025

Radial Transparency System:

  • Implement radial transparency zones for upper map layers
  • Focused layer remains fully opaque, upper layers fade with distance
  • Configurable transparency amount (0.0 = only focused layer, 1.0 = max transparency)
  • Toggle radial transparency on/off
  • Hotkey support for quick toggle
  • Improves visibility of multi-level dungeons and vertical map sections

Background Image Support:

  • Load custom background images for the map canvas
  • Multiple Middle-earth themed backgrounds included:
    • Arda maps (MERP style)
    • Arrival scenes (Shire, Caras Galadhon, Rivendell)
    • Moria Gate
  • Fit modes: Fit, Fill, Stretch, Tile, Center, Focused
  • Adjustable opacity (0-100%)
  • Focused mode with scale and offset controls
  • Background rendering behind map geometry

Graphics Configuration:

  • New transparency controls in Graphics preferences
  • Background image browser and configuration panel
  • Live preview of settings
  • Fit mode selection with visual feedback
  • Scale and offset sliders for fine-tuning

Shader Enhancements:

  • Updated vertex and fragment shaders for transparency support
  • Alpha blending for layered rendering
  • Proper depth testing with transparency
  • Optimized shader uniforms

Technical Implementation:

  • Enhanced OpenGL rendering pipeline
  • Background texture loading and management
  • Layer-based transparency calculations
  • Shader program updates for new rendering modes

This creates a more immersive and visually appealing mapping experience with improved depth perception.

Summary by Sourcery

Introduce configurable radial transparency and background imagery for the map canvas, expand graphics and texture options, and add richer UI tooling for communications, hotkeys, visibility filtering, and seasonal rendering.

New Features:

  • Add configurable background image rendering for the map canvas with multiple fit modes, opacity, and focused player-centric positioning.
  • Introduce radial transparency for upper and lower map layers, including a toolbar/menu toggle and optional hotkey control.
  • Add a communications dock panel backed by a CommsManager and CommsWidget with dedicated logging and save-to-file support.
  • Add a visibility filter dock for selectively showing or hiding infomark marker classes and connections on the canvas.
  • Introduce a centralized, configurable hotkey system with per-action shortcuts applied at runtime and registered as global shortcuts.
  • Add support for multiple texture sets (classic/modern/custom) with optional seasonal textures driven by in-game time and clock season changes.

Enhancements:

  • Extend graphics preferences with new controls for background images, layer transparency, texture set selection, and seasonal textures, including live preview callbacks.
  • Enhance OpenGL rendering and shaders to support world-space background rendering, alpha blending, radial transparency uniforms, and night-time darkening based on game time.
  • Refine room rendering to account for time-of-day, terrain, and player light source when determining dark vs. lit rooms.
  • Add configuration groups and settings for communications colors, styles, logging, GMCP clock broadcasting, and visibility filters for markers and connections.

Radial Transparency System:
- Implement radial transparency zones for upper map layers
- Focused layer remains fully opaque, upper layers fade with distance
- Configurable transparency amount (0.0 = only focused layer, 1.0 = max transparency)
- Toggle radial transparency on/off
- Hotkey support for quick toggle
- Improves visibility of multi-level dungeons and vertical map sections

Background Image Support:
- Load custom background images for the map canvas
- Multiple Middle-earth themed backgrounds included:
  * Arda maps (MERP style)
  * Arrival scenes (Shire, Caras Galadhon, Rivendell)
  * Moria Gate
- Fit modes: Fit, Fill, Stretch, Tile, Center, Focused
- Adjustable opacity (0-100%)
- Focused mode with scale and offset controls
- Background rendering behind map geometry

Graphics Configuration:
- New transparency controls in Graphics preferences
- Background image browser and configuration panel
- Live preview of settings
- Fit mode selection with visual feedback
- Scale and offset sliders for fine-tuning

Shader Enhancements:
- Updated vertex and fragment shaders for transparency support
- Alpha blending for layered rendering
- Proper depth testing with transparency
- Optimized shader uniforms

Technical Implementation:
- Enhanced OpenGL rendering pipeline
- Background texture loading and management
- Layer-based transparency calculations
- Shader program updates for new rendering modes

This creates a more immersive and visually appealing mapping
experience with improved depth perception.
@sourcery-ai
Copy link

sourcery-ai bot commented Dec 17, 2025

Reviewer's Guide

Implements configurable radial transparency and background-image rendering for the map canvas, introduces texture-set & seasonal texture controls, adds a communications panel with its configuration, centralizes hotkey configuration, and extends shaders/OpenGL plumbing to support new per-layer transparency and time-of-day effects.

Sequence diagram for map rendering with background image, radial transparency, and time-of-day

sequenceDiagram
    participant MW as MainWindow
    participant MC as MapCanvas
    participant GL as OpenGL
    participant CFG as Configuration
    participant BLD as MapBatchBuilder
    participant SH as Shaders

    MW->>BLD: generateMapDataFinisher(textures,font,map,timeOfDay,playerRoom,playerHasLight)
    activate BLD
    BLD-->>BLD: visitRooms(..., timeOfDay, playerRoom, playerHasLight)
    BLD-->>BLD: build LayerMeshesIntermediate per layer
    BLD-->>BLD: create InternalData(timeOfDay)
    BLD-->>MW: SharedMapBatchFinisher
    deactivate BLD

    MW->>MC: paintGL()
    activate MC
    MC->>MC: actuallyPaintGL()
    MC->>GL: renderBackgroundImage()
    activate GL
    MC->>CFG: read canvas.advanced (backgroundImagePath, fitMode, opacity)
    alt useBackgroundImage is false or path empty
        GL->>GL: clear(backgroundColor alpha=1)
    else background image enabled
        MC->>MC: loadBackgroundImageIfNeeded()
        MC->>GL: renderColoredTexturedQuads(fullscreen quad, state.withTexture0(texId))
    end
    GL-->>MC: background done
    deactivate GL

    MC->>MC: paintMap()
    MC->>MC: renderMapBatches()
    loop for each layer thisLayer
        MC->>MC: drawLayer(thisLayer,currentLayer)
        MC->>SH: LayerMeshes.render(thisLayer,currentLayer,playerPos,isNight)
        activate SH
        SH-->>SH: set GLRenderState.uniforms
        SH-->>SH: set uPlayerPos,uCurrentLayer
        SH-->>SH: set uEnableRadial, uIsNight
        SH-->>GL: draw terrain, walls, overlays with blending
        deactivate SH
    end
    MC-->>MW: frame complete
    deactivate MC
Loading

Class diagram for updated configuration and graphics settings

classDiagram
    class Configuration {
      +GeneralSettings general
      +CanvasSettings canvas
      +Hotkeys hotkeys
      +CommsSettings comms
      +MumeClockSettings mumeClock
    }

    class CanvasSettings {
      +bool trilinearFiltering
      +bool softwareOpenGL
      +QString resourcesDirectory
      +TextureSetEnum textureSet
      +bool enableSeasonalTextures
      +float layerTransparency
      +bool enableRadialTransparency
      +Advanced advanced
      +VisibilityFilter visibilityFilter
    }

    class Advanced {
      +NamedConfig~bool~ autoTilt
      +NamedConfig~bool~ printPerfStats
      +bool useBackgroundImage
      +QString backgroundImagePath
      +int backgroundFitMode
      +float backgroundOpacity
      +float backgroundFocusedScale
      +float backgroundFocusedOffsetX
      +float backgroundFocusedOffsetY
      +FixedPoint~1~ fov
      +FixedPoint~1~ verticalAngle
      +FixedPoint~1~ horizontalAngle
      +FixedPoint~1~ layerHeight
      +Advanced()
      +void registerChangeCallback(Lifetime lifetime, Function callback)
    }

    class VisibilityFilter {
      +NamedConfig~bool~ generic
      +NamedConfig~bool~ herb
      +NamedConfig~bool~ river
      +NamedConfig~bool~ place
      +NamedConfig~bool~ mob
      +NamedConfig~bool~ comment
      +NamedConfig~bool~ road
      +NamedConfig~bool~ object
      +NamedConfig~bool~ action
      +NamedConfig~bool~ locality
      +NamedConfig~bool~ connections
      +VisibilityFilter()
      +bool isVisible(InfomarkClassEnum markerClass)
      +void setVisible(InfomarkClassEnum markerClass, bool visible)
      +bool isConnectionsVisible()
      +void setConnectionsVisible(bool visible)
      +void showAll()
      +void hideAll()
      +void registerChangeCallback(Lifetime lifetime, Function callback)
    }

    class Hotkeys {
      +NamedConfig~QString~ fileOpen
      +NamedConfig~QString~ fileSave
      +NamedConfig~QString~ fileReload
      +NamedConfig~QString~ fileQuit
      +NamedConfig~QString~ editUndo
      +NamedConfig~QString~ editRedo
      +NamedConfig~QString~ editPreferences
      +NamedConfig~QString~ editPreferencesAlt
      +NamedConfig~QString~ editFindRooms
      +NamedConfig~QString~ editRoom
      +NamedConfig~QString~ viewZoomIn
      +NamedConfig~QString~ viewZoomOut
      +NamedConfig~QString~ viewZoomReset
      +NamedConfig~QString~ viewLayerUp
      +NamedConfig~QString~ viewLayerDown
      +NamedConfig~QString~ viewLayerReset
      +NamedConfig~QString~ viewRadialTransparency
      +NamedConfig~QString~ viewStatusBar
      +NamedConfig~QString~ viewScrollBars
      +NamedConfig~QString~ viewMenuBar
      +NamedConfig~QString~ viewAlwaysOnTop
      +NamedConfig~QString~ panelLog
      +NamedConfig~QString~ panelClient
      +NamedConfig~QString~ panelGroup
      +NamedConfig~QString~ panelRoom
      +NamedConfig~QString~ panelAdventure
      +NamedConfig~QString~ panelDescription
      +NamedConfig~QString~ panelComms
      +NamedConfig~QString~ modeMoveMap
      +NamedConfig~QString~ modeRaypick
      +NamedConfig~QString~ modeSelectRooms
      +NamedConfig~QString~ modeSelectMarkers
      +NamedConfig~QString~ modeSelectConnection
      +NamedConfig~QString~ modeCreateMarker
      +NamedConfig~QString~ modeCreateRoom
      +NamedConfig~QString~ modeCreateConnection
      +NamedConfig~QString~ modeCreateOnewayConnection
      +NamedConfig~QString~ roomCreate
      +NamedConfig~QString~ roomMoveUp
      +NamedConfig~QString~ roomMoveDown
      +NamedConfig~QString~ roomMergeUp
      +NamedConfig~QString~ roomMergeDown
      +NamedConfig~QString~ roomDelete
      +NamedConfig~QString~ roomConnectNeighbors
      +NamedConfig~QString~ roomMoveToSelected
      +NamedConfig~QString~ roomUpdateSelected
    }

    class CommsSettings {
      +NamedConfig~QColor~ tellColor
      +NamedConfig~QColor~ whisperColor
      +NamedConfig~QColor~ groupColor
      +NamedConfig~QColor~ askColor
      +NamedConfig~QColor~ sayColor
      +NamedConfig~QColor~ emoteColor
      +NamedConfig~QColor~ socialColor
      +NamedConfig~QColor~ yellColor
      +NamedConfig~QColor~ narrateColor
      +NamedConfig~QColor~ prayColor
      +NamedConfig~QColor~ shoutColor
      +NamedConfig~QColor~ singColor
      +NamedConfig~QColor~ backgroundColor
      +NamedConfig~QColor~ talkerYouColor
      +NamedConfig~QColor~ talkerPlayerColor
      +NamedConfig~QColor~ talkerNpcColor
      +NamedConfig~QColor~ talkerAllyColor
      +NamedConfig~QColor~ talkerNeutralColor
      +NamedConfig~QColor~ talkerEnemyColor
      +NamedConfig~bool~ yellAllCaps
      +NamedConfig~bool~ whisperItalic
      +NamedConfig~bool~ emoteItalic
      +NamedConfig~bool~ showTimestamps
      +NamedConfig~bool~ saveLogOnExit
      +NamedConfig~QString~ logDirectory
      +NamedConfig~bool~ muteDirectTab
      +NamedConfig~bool~ muteLocalTab
      +NamedConfig~bool~ muteGlobalTab
      +void read(QSettings conf)
      +void write(QSettings conf) const
    }

    class MumeClockSettings {
      +int64_t startEpoch
      +bool display
      +NamedConfig~bool~ gmcpBroadcast
      +NamedConfig~int~ gmcpBroadcastInterval
      +MumeClockSettings()
      +void read(QSettings conf)
      +void write(QSettings conf) const
    }

    class BackgroundFitModeEnum {
      <<enumeration>>
      FIT
      FILL
      STRETCH
      CENTER
      TILE
      FOCUSED
    }

    Configuration --> CanvasSettings : has
    Configuration --> Hotkeys : has
    Configuration --> CommsSettings : has
    Configuration --> MumeClockSettings : has
    CanvasSettings --> Advanced : has
    CanvasSettings --> VisibilityFilter : has
    Advanced --> BackgroundFitModeEnum : uses
    CanvasSettings --> TextureSetEnum : uses
    VisibilityFilter --> InfomarkClassEnum : uses
Loading

Class diagram for MainWindow, communications, visibility, and hotkeys

classDiagram
    class MainWindow {
      -QDockWidget* m_dockDialogLog
      -QDockWidget* m_dockDialogClient
      -QDockWidget* m_dockDialogGroup
      -QDockWidget* m_dockDialogRoom
      -QDockWidget* m_dockDialogAdventure
      -QDockWidget* m_dockDialogDescription
      -QDockWidget* m_dockDialogComms
      -QDockWidget* m_dockDialogVisibleMarkers
      -std::unique_ptr~GameObserver~ m_gameObserver
      -AutoLogger* m_logger
      -MumeClock* m_mumeClock
      -CommsManager* m_commsManager
      -CommsWidget* m_commsWidget
      -VisibilityFilterWidget* m_visibilityFilterWidget
      -QAction* radialTransparencyAct
      -QAction* saveCommsLogAct
      +MainWindow()
      +void createActions()
      +void setupMenuBar()
      +void wireConnections()
      +void closeEvent(QCloseEvent* event)
      +void slot_onPreferences()
      +void slot_alwaysOnTop()
      +void slot_setRadialTransparency()
      +void applyHotkeys()
      +void registerGlobalShortcuts()
    }

    class CommsManager {
      +CommsManager(QObject* parent)
      +void slot_parseGmcpInput(GmcpMessage gmcp)
      +void slot_parseRawGameText(QString text)
      +signal sig_log(QString msg)
    }

    class CommsWidget {
      +CommsWidget(CommsManager manager, AutoLogger logger, QWidget* parent)
      +void slot_saveLog()
      +void slot_saveLogOnExit()
      +void slot_loadSettings()
    }

    class VisibilityFilterWidget {
      +VisibilityFilterWidget(QWidget* parent)
      +signal sig_visibilityChanged()
      +signal sig_connectionsVisibilityChanged()
    }

    class MapWindow {
      +MapCanvas* getCanvas()
      +void slot_graphicsSettingsChanged()
    }

    class MapCanvas {
      +void slot_reloadTextures()
      +void infomarksChanged()
    }

    class ConfigDialog {
      +signal sig_graphicsSettingsChanged()
      +signal sig_textureSettingsChanged()
      +signal sig_groupSettingsChanged()
      +signal sig_commsSettingsChanged()
      +signal sig_hotkeysChanged()
    }

    class GameObserver {
      +signal sig2_sentToUserGmcp(GmcpMessage gmcp)
      +signal sig2_rawGameText(QString text)
      +signal sig2_connected()
    }

    class MumeClock {
      +MumeClock(int64_t startEpoch, GameObserver observer, QObject* parent)
      +MumeMoment getMumeMoment() const
      +signal sig_log(QString msg)
      +signal sig_seasonChanged(SeasonEnum season)
    }

    class AutoLogger {
      +AutoLogger(QObject* parent)
    }

    MainWindow --> CommsManager : creates
    MainWindow --> CommsWidget : creates
    MainWindow --> VisibilityFilterWidget : creates
    MainWindow --> MapWindow : has
    MainWindow --> ConfigDialog : uses
    MainWindow --> GameObserver : owns
    MainWindow --> MumeClock : owns
    MainWindow --> AutoLogger : owns

    GameObserver --> CommsManager : signals to
    CommsManager --> CommsWidget : used by

    VisibilityFilterWidget --> MapCanvas : signals to
    ConfigDialog --> MapWindow : signals to
    ConfigDialog --> MapCanvas : textureSettingsChanged
    ConfigDialog --> CommsWidget : commsSettingsChanged
    ConfigDialog --> MainWindow : hotkeysChanged
Loading

Class diagram for MapCanvas rendering, batches, shaders, and radial transparency

classDiagram
    class MapCanvas {
      -std::optional~QString~ m_backgroundImagePath
      -std::shared_ptr~MMTexture~ m_backgroundTexture
      -OpenGL m_opengl
      -int m_currentLayer
      -glm::vec2 m_scroll
      -MapBatches m_batches
      +void actuallyPaintGL()
      +void paintMap()
      +void renderMapBatches()
      +void loadBackgroundImageIfNeeded()
      +void renderBackgroundImage()
      +OpenGL& getOpenGL()
    }

    class MapBatches {
      +std::unordered_map~int, LayerMeshes~ batchedMeshes
      +BatchedConnections connectionDrawerBuffers
      +std::unordered_map~int, RoomNameBatchIntermediate~ roomNameBatches
      +bool isNight
      +PendingUpdateFlashState pendingUpdateFlashState
    }

    class LayerMeshesIntermediate {
      +LayerMeshes getLayerMeshes(OpenGL gl) const
    }

    class LayerMeshes {
      +void render(int thisLayer, int focusedLayer, glm::vec3 playerPos, bool isNight)
    }

    class InternalData {
      +std::unordered_map~int, LayerMeshesIntermediate~ batchedMeshes
      +BatchedConnections connectionDrawerBuffers
      +std::unordered_map~int, RoomNameBatchIntermediate~ roomNameBatches
      +MumeTimeEnum timeOfDay
      +void virt_finish(MapBatches output, OpenGL gl, GLFont font) const
    }

    class GLRenderState {
      +BlendModeEnum blend
      +std::optional~DepthParams~ depth
      +Uniforms uniforms
      +GLRenderState withDepthFunction(DepthFunctionEnum f) const
      +GLRenderState withBlend(BlendModeEnum mode) const
      +GLRenderState withColor(Color c) const
      +GLRenderState withTexture0(TextureId id) const
    }

    class Uniforms {
      +Color color
      +Textures textures
      +std::optional~float~ pointSize
      +glm::vec3 playerPos
      +int currentLayer
      +bool enableRadialTransparency
      +bool texturesDisabled
      +bool isNight
    }

    class OpenGL {
      +void clear(Color c)
      +void renderColoredQuads(vector~ColorVert~ verts, GLRenderState state)
      +void renderColoredTexturedQuads(vector~ColoredTexVert~ verts, GLRenderState state)
      +void setTextureLookup(TextureId id, shared_ptr~MMTexture~ tex)
    }

    class AbstractShaderProgram {
      +void setUniform1iv(GLint location, GLsizei count, GLint* value)
      +void setUniform1fv(GLint location, GLsizei count, GLfloat* value)
      +void setUniform3fv(GLint location, GLsizei count, GLfloat* value)
      +void setUniform4fv(GLint location, GLsizei count, GLfloat* value)
      +void setUniform4iv(GLint location, GLsizei count, GLint* value)
      +void setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, GLfloat* value)
      +void setViewport(char* name, Viewport viewport)
      +void setVec3(char* name, glm::vec3 v)
      +void setInt(char* name, int value)
    }

    class BackgroundFitModeEnum {
      <<enumeration>>
      FIT
      FILL
      STRETCH
      CENTER
      TILE
      FOCUSED
    }

    class MumeTimeEnum {
      <<enumeration>>
      UNKNOWN
      DAY
      DUSK
      NIGHT
    }

    MapCanvas --> MapBatches : uses
    MapCanvas --> OpenGL : uses
    MapCanvas --> BackgroundFitModeEnum : uses
    MapBatches --> LayerMeshes : contains
    InternalData --> MapBatches : fills
    InternalData --> LayerMeshesIntermediate : contains
    InternalData --> MumeTimeEnum : uses
    LayerMeshesIntermediate --> LayerMeshes : builds
    LayerMeshes --> GLRenderState : configures
    GLRenderState --> Uniforms : has
    OpenGL --> AbstractShaderProgram : uses
Loading

File-Level Changes

Change Details Files
Persist new canvas, background image, visibility-filter, hotkey, comms, and clock GMCP settings in configuration with helpers and change callbacks.
  • Add conversion helpers between TextureSetEnum and int and store texture set and seasonal texture flags.
  • Introduce new QSettings keys and read/write logic for background image parameters, layer transparency, visibility filters, and seasonal textures in CanvasSettings.
  • Add Hotkeys and CommsSettings structs with full read/write implementations and associated QSettings groups/keys.
  • Extend MumeClockSettings with GMCP broadcast flags and default ctor, and wire new visibility filter helper methods including callbacks.
src/configuration/configuration.cpp
src/configuration/configuration.h
Extend MainWindow with a communications dock/panel, visibility-filter dock, radial-transparency toggle, hotkey application, global shortcut registration, and seasonal texture wiring.
  • Instantiate GameObserver, MumeClock, AutoLogger, CommsManager, and CommsWidget earlier; connect GMCP and raw text into CommsManager and route comms log saving on exit.
  • Create a Communications QDockWidget and VisibilityFilterWidget dock, hook filter signals to MapCanvas infomark/connection updates, and expose both docks via the View menu.
  • Add a checkable Radial Transparency QAction, hook it to canvas config and menu, and initialize based on configuration.
  • Remove hard-coded action shortcuts and implement applyHotkeys() plus registerGlobalShortcuts() to bind shortcuts from Configuration::Hotkeys and register actions for global use.
  • Wire ConfigDialog signals for texture settings, comms settings, and hotkeys to MapCanvas, CommsWidget, and MainWindow respectively.
  • Connect MumeClock seasonChanged to MapCanvas, initialize current season on startup, and expose save communications log menu action.
src/mainwindow/mainwindow.cpp
src/mainwindow/mainwindow.h
Add background image rendering pipeline to MapCanvas with multiple fit modes and background opacity, including focused world-space mode.
  • Introduce BackgroundFitModeEnum and extend GLRenderState uniforms with player position, current layer, radial transparency, texture-disable, and night flags.
  • Add MapCanvas::loadBackgroundImageIfNeeded and MapCanvas::renderBackgroundImage to manage texture loading, OpenGL registration, and rendering for FIT/FILL/STRETCH/CENTER/TILE/FOCUSED modes, with configurable opacity and focused scale/offset.
  • Adjust MapCanvas::actuallyPaintGL to delegate background clear/render to renderBackgroundImage while preserving later 3D map rendering.
  • Register visibilityFilter change callbacks alongside advanced canvas settings for map updates.
  • Change the async-update indicator to a minimal symbol instead of a large warning.
src/display/mapcanvas_gl.cpp
src/opengl/OpenGLTypes.h
Implement radial transparency, time-of-day lighting, and layered alpha fading in room rendering and shaders, with player-position awareness.
  • Thread Mume time-of-day, player room, and player light state into the map batch generator, storing timeOfDay in InternalData and computing isNight on finish.
  • Implement dynamic room darkness determination based on RoomLightEnum, RoomTerrainEnum, time of day, and whether the player has light, keeping player room with light always lit and only applying darkness to appropriate terrains/times.
  • Extend LayerMeshes::render to accept player position and night flag, set new shader uniforms (playerPos, currentLayer, enableRadialTransparency, texturesDisabled, isNight), and replace fixed layer coloring with configurable layerTransparency plus distance-based alpha for layers above/below the focus.
  • Confine room tinting (DARK/NO_SUNDEATH) to the focused layer only, to avoid undesired modulation on transparent layers, and remove additional darkening overlays now that opacity-based fading is used.
  • Add fragment/vertex shader uniforms and varyings (uPlayerPos, uCurrentLayer, uEnableRadial, uIsNight, vWorldPos, uTexturesDisabled) across plain and textured programs, and implement organic radial transparency masks and night darkening based on layer distance and Chebyshev distance from player.
  • Extend AbstractShaderProgram to support vec3 and int uniforms and plumb the new uniform setting helpers.
src/display/MapCanvasRoomDrawer.cpp
src/display/MapCanvasRoomDrawer.h
src/resources/shaders/legacy/plain/ucolor/vert.glsl
src/resources/shaders/legacy/plain/ucolor/frag.glsl
src/resources/shaders/legacy/tex/acolor/vert.glsl
src/resources/shaders/legacy/tex/acolor/frag.glsl
src/resources/shaders/legacy/tex/ucolor/vert.glsl
src/resources/shaders/legacy/tex/ucolor/frag.glsl
src/opengl/legacy/AbstractShaderProgram.cpp
src/opengl/legacy/AbstractShaderProgram.h
Expose new graphics preferences for texture set/seasonal tiles and wire them to texture reloads, plus minor GL/Windows tweaks.
  • Add tile-set selection and seasonal-tiles checkbox on the graphics page, load/save them from CanvasSettings, and emit a dedicated sig_textureSettingsChanged.
  • Hook texture-set and seasonal toggle slots to update config and trigger MapCanvas texture reloading via ConfigDialog.
  • Slightly tweak canvas defaults (background color and 3D horizontal angle range) and ensure seasonal texture changes respond to MumeClock seasons.
  • Include windows.h on Windows build in OpenGL.cpp to support GPU selection exports.
src/preferences/graphicspage.cpp
src/preferences/graphicspage.h
src/preferences/graphicspage.ui
src/opengl/OpenGL.cpp
src/configuration/configuration.cpp
src/configuration/configuration.h

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • The three fragment shaders (plain/ucolor, tex/acolor, tex/ucolor) now duplicate identical noise/fractal/organicPattern logic and radial-transparency code; consider factoring these helpers into a shared GLSL include or macro block so that behavior changes only need to be made in one place and stay in sync.
  • The radial-transparency and night-lighting shaders use a lot of hard‑coded magic numbers for distances, thresholds, and alpha values (e.g., 0.4/0.7/1.3, 99.9, 1.1, various baseAlpha tables); it would be easier to tune and reason about if these were consolidated into named constants or uniforms driven from C++ rather than scattered literals.
  • applyHotkeys() logs every shortcut application with qDebug, which could become quite noisy when users tweak preferences; you may want to either compile-guard or rate-limit these logs, or only log when something actually changes.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The three fragment shaders (plain/ucolor, tex/acolor, tex/ucolor) now duplicate identical noise/fractal/organicPattern logic and radial-transparency code; consider factoring these helpers into a shared GLSL include or macro block so that behavior changes only need to be made in one place and stay in sync.
- The radial-transparency and night-lighting shaders use a lot of hard‑coded magic numbers for distances, thresholds, and alpha values (e.g., 0.4/0.7/1.3, 99.9, 1.1, various baseAlpha tables); it would be easier to tune and reason about if these were consolidated into named constants or uniforms driven from C++ rather than scattered literals.
- applyHotkeys() logs every shortcut application with qDebug, which could become quite noisy when users tweak preferences; you may want to either compile-guard or rate-limit these logs, or only log when something actually changes.

## Individual Comments

### Comment 1
<location> `src/configuration/configuration.cpp:56-65` </location>
<code_context>
     }
 }

+NODISCARD TextureSetEnum intToTextureSet(int value)
+{
+    switch (value) {
+    case 0:
+        return TextureSetEnum::CLASSIC;
+    case 1:
+        return TextureSetEnum::MODERN;
+    case 2:
+        return TextureSetEnum::CUSTOM;
+    default:
+        return TextureSetEnum::MODERN; // Default to Modern
+    }
+}
+
+NODISCARD int textureSetToInt(TextureSetEnum value)
+{
+    return static_cast<int>(value);
+}
+
</code_context>

<issue_to_address>
**issue (bug_risk):** Enum <-> int mapping may get out of sync with `TextureSetEnum` underlying values

`intToTextureSet` hardcodes 0/1/2 → CLASSIC/MODERN/CUSTOM, but `textureSetToInt` relies on `static_cast<int>` matching those exact underlying enum values and order. If `TextureSetEnum` is reordered or given explicit values, saved config/UI indices could map to the wrong texture set. To avoid this, either centralize the mapping (e.g., a `constexpr` table used in both directions) or make `textureSetToInt` explicitly map the known enum constants to their intended integers so the mapping stays symmetric and stable if the enum changes.
</issue_to_address>

### Comment 2
<location> `src/preferences/graphicspage.cpp:192-200` </location>
<code_context>
     graphicsSettingsChanged();
 }
+
+void GraphicsPage::slot_textureSetChanged(int index)
+{
+    auto &config = setConfig().canvas;
+    switch (index) {
+    case 0:
+        config.textureSet = TextureSetEnum::CLASSIC;
+        break;
+    case 1:
+        config.textureSet = TextureSetEnum::MODERN;
+        break;
+    case 2:
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Texture-set selection logic duplicates `intToTextureSet`, which risks drift

This manual index→`TextureSetEnum` mapping duplicates `intToTextureSet` in `configuration.cpp`. If one is updated (new enum value, changed default) and the other isn’t, UI selection, stored config, and runtime behavior can diverge. Consider calling `intToTextureSet(index)` here (with appropriate bounds handling) instead of maintaining a separate switch.

Suggested implementation:

```cpp
void GraphicsPage::slot_textureSetChanged(int index)
{
    auto &config = setConfig().canvas;

    // Delegate index→enum mapping to shared helper to avoid duplicated logic.
    config.textureSet = intToTextureSet(index);

    graphicsSettingsChanged();
    emit sig_textureSettingsChanged();
}

```

1. Ensure the declaration of `intToTextureSet(int)` is visible in this translation unit. If it is not already included transitively, add the appropriate header at the top of `graphicspage.cpp`, for example:
   - `#include "configuration.h"` (or whichever header declares `intToTextureSet` and `TextureSetEnum` in your project).
2. If `intToTextureSet` does *not* perform bounds checking or defaulting internally, update its implementation in `configuration.cpp` to:
   - Clamp or validate the integer index.
   - Return the desired default (likely `TextureSetEnum::MODERN`) for out-of-range values.
   This keeps all index→enum policy in one place instead of reintroducing a `switch` here.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +56 to +65
NODISCARD TextureSetEnum intToTextureSet(int value)
{
switch (value) {
case 0:
return TextureSetEnum::CLASSIC;
case 1:
return TextureSetEnum::MODERN;
case 2:
return TextureSetEnum::CUSTOM;
default:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Enum <-> int mapping may get out of sync with TextureSetEnum underlying values

intToTextureSet hardcodes 0/1/2 → CLASSIC/MODERN/CUSTOM, but textureSetToInt relies on static_cast<int> matching those exact underlying enum values and order. If TextureSetEnum is reordered or given explicit values, saved config/UI indices could map to the wrong texture set. To avoid this, either centralize the mapping (e.g., a constexpr table used in both directions) or make textureSetToInt explicitly map the known enum constants to their intended integers so the mapping stays symmetric and stable if the enum changes.

Comment on lines +192 to +200
void GraphicsPage::slot_textureSetChanged(int index)
{
auto &config = setConfig().canvas;
switch (index) {
case 0:
config.textureSet = TextureSetEnum::CLASSIC;
break;
case 1:
config.textureSet = TextureSetEnum::MODERN;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Texture-set selection logic duplicates intToTextureSet, which risks drift

This manual index→TextureSetEnum mapping duplicates intToTextureSet in configuration.cpp. If one is updated (new enum value, changed default) and the other isn’t, UI selection, stored config, and runtime behavior can diverge. Consider calling intToTextureSet(index) here (with appropriate bounds handling) instead of maintaining a separate switch.

Suggested implementation:

void GraphicsPage::slot_textureSetChanged(int index)
{
    auto &config = setConfig().canvas;

    // Delegate index→enum mapping to shared helper to avoid duplicated logic.
    config.textureSet = intToTextureSet(index);

    graphicsSettingsChanged();
    emit sig_textureSettingsChanged();
}
  1. Ensure the declaration of intToTextureSet(int) is visible in this translation unit. If it is not already included transitively, add the appropriate header at the top of graphicspage.cpp, for example:
    • #include "configuration.h" (or whichever header declares intToTextureSet and TextureSetEnum in your project).
  2. If intToTextureSet does not perform bounds checking or defaulting internally, update its implementation in configuration.cpp to:
    • Clamp or validate the integer index.
    • Return the desired default (likely TextureSetEnum::MODERN) for out-of-range values.
      This keeps all index→enum policy in one place instead of reintroducing a switch here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant