From b1e9955af39d56c95a38a9284acb36897948e50e Mon Sep 17 00:00:00 2001 From: FrederikJA Date: Fri, 12 Jun 2026 05:11:34 +0200 Subject: [PATCH] Coordinate grid (#4224) **Add approved & assigned issue number here:** Resolves #3839 ## Description: A bunch of small updates to make the coordinate grid a lot nicer. - Removes black backgrounds on text. - Allows user to modify the opacity of the coordinate grid - Persist the enable state of the coordinate grid ### Before image ### After image image ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file ## Please put your Discord username so you can be contacted if a bug or regression is found: FrederikJA --- resources/lang/en.json | 2 + .../hud/layers/GraphicsSettingsModal.ts | 46 +++++++++++++++++++ src/client/render/gl/GraphicsOverrides.ts | 1 + src/client/render/gl/RenderOverrides.ts | 4 ++ src/client/render/gl/RenderSettings.ts | 1 + src/client/render/gl/Renderer.ts | 12 +++++ .../render/gl/passes/CoordinateGridPass.ts | 9 +++- src/client/render/gl/render-settings.json | 1 + .../render/gl/shaders/grid/grid.frag.glsl | 46 ++++++++----------- 9 files changed, 93 insertions(+), 29 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index e7ae5f614..071730a05 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -972,6 +972,8 @@ "territory_sat_desc": "How vivid the territory fill colors are (lower mutes them)", "territory_alpha_label": "Territory opacity", "territory_alpha_desc": "How opaque the territory fill is (lower lets terrain show through)", + "coordinate_grid_opacity_label": "Coordinate grid opacity", + "coordinate_grid_opacity_desc": "How opaque the coordinate grid is (lower lets more things show through)", "rail_distance_label": "Train track draw distance", "rail_distance_desc": "How far zoomed out train tracks remain visible", "rail_thickness_label": "Train track thickness", diff --git a/src/client/hud/layers/GraphicsSettingsModal.ts b/src/client/hud/layers/GraphicsSettingsModal.ts index b41842be6..d880e9515 100644 --- a/src/client/hud/layers/GraphicsSettingsModal.ts +++ b/src/client/hud/layers/GraphicsSettingsModal.ts @@ -52,6 +52,10 @@ const TERRITORY_ALPHA_MIN = 0; const TERRITORY_ALPHA_MAX = 1; const TERRITORY_ALPHA_STEP = 0.01; +const COORDINATE_GRID_OPACITY_MIN = 0; +const COORDINATE_GRID_OPACITY_MAX = 1; +const COORDINATE_GRID_OPACITY_STEP = 0.01; + // Train track "draw distance" is presented inverted: a higher slider value means // tracks stay visible when more zoomed out, i.e. a lower railMinZoom. const RAIL_ZOOM_MIN = 0; @@ -252,6 +256,13 @@ export class GraphicsSettingsModal extends LitElement implements Controller { ); } + private currentCoordinateGridOpacity(): number { + return ( + this.userSettings.graphicsOverrides().mapOverlay?.coordinateGridOpacity ?? + renderDefaults.mapOverlay.coordinateGridOpacity + ); + } + private currentRailMinZoom(): number { return ( this.userSettings.graphicsOverrides().railroad?.railMinZoom ?? @@ -291,6 +302,11 @@ export class GraphicsSettingsModal extends LitElement implements Controller { this.patchMapOverlay({ territoryAlpha: value }); } + private onCoordinateGridOpacityChange(event: Event) { + const value = parseFloat((event.target as HTMLInputElement).value); + this.patchMapOverlay({ coordinateGridOpacity: value }); + } + private onRailDrawDistanceChange(event: Event) { const drawDistance = parseFloat((event.target as HTMLInputElement).value); // Invert: higher draw distance => tracks visible when more zoomed out. @@ -412,6 +428,7 @@ export class GraphicsSettingsModal extends LitElement implements Controller { const highlightThicken = this.currentHighlightThicken(); const territorySat = this.currentTerritorySat(); const territoryAlpha = this.currentTerritoryAlpha(); + const coordinateGridOpacity = this.currentCoordinateGridOpacity(); const railDrawDistance = RAIL_ZOOM_MAX - this.currentRailMinZoom(); const railThickness = this.currentRailThickness(); const colorblind = this.currentColorblind(); @@ -751,6 +768,35 @@ export class GraphicsSettingsModal extends LitElement implements Controller { +
+
+
+ ${translateText( + "graphics_setting.coordinate_grid_opacity_label", + )} +
+
+ ${translateText( + "graphics_setting.coordinate_grid_opacity_desc", + )} +
+ +
+
+ ${coordinateGridOpacity.toFixed(2)} +
+
+
diff --git a/src/client/render/gl/GraphicsOverrides.ts b/src/client/render/gl/GraphicsOverrides.ts index 1a34dd585..916ed458a 100644 --- a/src/client/render/gl/GraphicsOverrides.ts +++ b/src/client/render/gl/GraphicsOverrides.ts @@ -24,6 +24,7 @@ export const GraphicsOverridesSchema = z highlightThicken: z.number(), territorySaturation: z.number(), territoryAlpha: z.number(), + coordinateGridOpacity: z.number(), }) .partial(), railroad: z diff --git a/src/client/render/gl/RenderOverrides.ts b/src/client/render/gl/RenderOverrides.ts index 6c47f0c85..15c582d71 100644 --- a/src/client/render/gl/RenderOverrides.ts +++ b/src/client/render/gl/RenderOverrides.ts @@ -56,6 +56,10 @@ export function applyGraphicsOverrides( if (overrides.mapOverlay?.territoryAlpha !== undefined) { settings.mapOverlay.territoryAlpha = overrides.mapOverlay.territoryAlpha; } + if (overrides.mapOverlay?.coordinateGridOpacity !== undefined) { + settings.mapOverlay.coordinateGridOpacity = + overrides.mapOverlay.coordinateGridOpacity; + } if (overrides.railroad?.railMinZoom !== undefined) { settings.railroad.railMinZoom = overrides.railroad.railMinZoom; } diff --git a/src/client/render/gl/RenderSettings.ts b/src/client/render/gl/RenderSettings.ts index 511eafe5d..936790a6a 100644 --- a/src/client/render/gl/RenderSettings.ts +++ b/src/client/render/gl/RenderSettings.ts @@ -113,6 +113,7 @@ export interface RenderSettings { territorySaturation: number; /** Absolute opacity of the territory fill. 1 = fully opaque (terrain hidden), ~0.588 = default. */ territoryAlpha: number; + coordinateGridOpacity: number; staleNukeBase: number; staleNukeVariation: number; staleNukeAlpha: number; diff --git a/src/client/render/gl/Renderer.ts b/src/client/render/gl/Renderer.ts index 3835f29df..b10fdaa4d 100644 --- a/src/client/render/gl/Renderer.ts +++ b/src/client/render/gl/Renderer.ts @@ -91,6 +91,8 @@ const SAM_RADIUS_HIGHLIGHT_TYPES = new Set([ "Hydrogen Bomb", ]); +const GRID_VIEW_KEY = "renderer:grid_view_enabled"; + export class GPURenderer { private gl: WebGL2RenderingContext; private camera: Camera; @@ -526,6 +528,11 @@ export class GPURenderer { mapH, this.settings, ); + try { + this.gridView = window.localStorage.getItem(GRID_VIEW_KEY) === "true"; + } catch { + this.setGridView(false); + } for (const p of header.players) { if (p.team !== null) this.playerTeams.set(p.smallID, p.team); @@ -1078,6 +1085,11 @@ export class GPURenderer { setGridView(active: boolean): void { this.gridView = active; + try { + window.localStorage.setItem(GRID_VIEW_KEY, active ? "true" : "false"); + } catch { + // Ignore if we are unable to use localstorage. + } } getSettings(): RenderSettings { diff --git a/src/client/render/gl/passes/CoordinateGridPass.ts b/src/client/render/gl/passes/CoordinateGridPass.ts index 5993a02d6..26c6116a6 100644 --- a/src/client/render/gl/passes/CoordinateGridPass.ts +++ b/src/client/render/gl/passes/CoordinateGridPass.ts @@ -31,6 +31,7 @@ export class CoordinateGridPass { private uCellSize: WebGLUniformLocation; private uZoom: WebGLUniformLocation; private uFontSize: WebGLUniformLocation; + private uOpacity: WebGLUniformLocation; private mapW: number; private mapH: number; @@ -57,6 +58,7 @@ export class CoordinateGridPass { this.uCellSize = gl.getUniformLocation(this.program, "uCellSize")!; this.uZoom = gl.getUniformLocation(this.program, "uZoom")!; this.uFontSize = gl.getUniformLocation(this.program, "uFontSize")!; + this.uOpacity = gl.getUniformLocation(this.program, "uOpacity")!; gl.useProgram(this.program); gl.uniform1i(gl.getUniformLocation(this.program, "uGlyphTex"), 0); @@ -73,6 +75,7 @@ export class CoordinateGridPass { gl.uniform1f(this.uCellSize, this.cellSize); gl.uniform1f(this.uZoom, zoom); gl.uniform1f(this.uFontSize, this.settings.altView.gridFontSize); + gl.uniform1f(this.uOpacity, this.settings.mapOverlay.coordinateGridOpacity); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, this.glyphTex); @@ -95,14 +98,16 @@ export class CoordinateGridPass { canvas.height = GLYPH_H; const ctx = canvas.getContext("2d")!; - ctx.fillStyle = "black"; - ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.strokeStyle = "black"; + ctx.lineWidth = 4; ctx.fillStyle = "white"; ctx.font = `bold ${GLYPH_H - 8}px monospace`; ctx.textAlign = "center"; ctx.textBaseline = "middle"; for (let i = 0; i < CHARS.length; i++) { + ctx.strokeText(CHARS[i], i * GLYPH_W + GLYPH_W / 2, GLYPH_H / 2); ctx.fillText(CHARS[i], i * GLYPH_W + GLYPH_W / 2, GLYPH_H / 2); } diff --git a/src/client/render/gl/render-settings.json b/src/client/render/gl/render-settings.json index f52a6d7e7..d3664ec64 100644 --- a/src/client/render/gl/render-settings.json +++ b/src/client/render/gl/render-settings.json @@ -69,6 +69,7 @@ "territoryDefenseDarken": 0.85, "territorySaturation": 1, "territoryAlpha": 0.588, + "coordinateGridOpacity": 0.5, "staleNukeBase": 0, "staleNukeVariation": 0.05, "staleNukeAlpha": 1, diff --git a/src/client/render/gl/shaders/grid/grid.frag.glsl b/src/client/render/gl/shaders/grid/grid.frag.glsl index 7174d0efa..193529956 100644 --- a/src/client/render/gl/shaders/grid/grid.frag.glsl +++ b/src/client/render/gl/shaders/grid/grid.frag.glsl @@ -6,6 +6,7 @@ uniform float uCellSize; uniform float uZoom; uniform float uFontSize; uniform sampler2D uGlyphTex; +uniform float uOpacity; in vec2 vWorldPos; out vec4 fragColor; @@ -29,7 +30,7 @@ void main() { // --- Grid lines (at cell boundaries) --- if (localX < lineW || localY < lineW) { - fragColor = vec4(1.0, 1.0, 1.0, 0.35); + fragColor = vec4(1.0, 1.0, 1.0, uOpacity); return; } @@ -41,7 +42,6 @@ void main() { float gw = fontSize * 0.6 * px; // glyph width in world units float gh = fontSize * px; // glyph height float pad = 8.0 * px; // padding from cell corner - float bgPad = 2.0 * px; // background extends beyond text float lx = localX - pad; float ly = localY - pad; @@ -73,32 +73,24 @@ void main() { float totalW = float(nc) * gw; - // Check label background area (text + padding) - if (lx < -bgPad || ly < -bgPad || lx >= totalW + bgPad || ly >= gh + bgPad) - discard; - // Check if on actual glyph - if (lx >= 0.0 && ly >= 0.0 && lx < totalW && ly < gh) { - int ci = int(floor(lx / gw)); - if (ci < nc) { - int g; - if (ci == 0) g = c0; - else if (ci == 1) g = c1; - else if (ci == 2) g = c2; - else g = c3; - - float cu = fract(lx / gw); - float cv = ly / gh; - float au = (float(g) + cu) / GLYPH_COUNT; - float mask = texture(uGlyphTex, vec2(au, cv)).r; - - if (mask > 0.3) { - fragColor = vec4(1.0, 1.0, 1.0, 0.9); - return; - } - } + if (lx < 0.0 || ly < 0.0 || lx >= totalW || ly >= gh) { + discard; } - // Background behind label - fragColor = vec4(0.08, 0.08, 0.08, 0.7); + int ci = int(floor(lx / gw)); + if (ci < nc) { + int g; + if (ci == 0) g = c0; + else if (ci == 1) g = c1; + else if (ci == 2) g = c2; + else g = c3; + + float cu = fract(lx / gw); + float cv = ly / gh; + float au = (float(g) + cu) / GLYPH_COUNT; + vec4 gColor = texture(uGlyphTex, vec2(au, cv)); + fragColor = gColor.a * vec4(gColor.rgb, uOpacity); + return; + } }