From e6256e62699f6681d50b9ff32ef4c5465abcc5a6 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 16 Jun 2026 18:27:40 -0700 Subject: [PATCH] =?UTF-8?q?Flip=20structure=20icon=20glyph=20to=20light=20?= =?UTF-8?q?on=20dark=20territory=20=F0=9F=8C=91=20(#4312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem In the structure render pass, when **classic icons** are enabled the inner icon glyph is tinted with a *darkened version of the player's fill color* (`uIconDarken = 0.3`). When a player's territory color is already dark, both the structure shape and its glyph render dark, so the icon blends into the shape and the dark territory behind it — making it effectively unreadable. (With non-classic icons the glyph is already the light `uIconColor`, so only the classic path was affected.) ## Fix In `structure.frag.glsl`, when classic icons are active, compute the fill's perceptual luminance and flip the glyph to the light icon color (`uIconColor`, white by default) when the fill is too dark: ```glsl vec3 glyphColor = uIconColor; if (uIconDarken > 0.0) { float fillLum = dot(fillColor.rgb, vec3(0.299, 0.587, 0.114)); glyphColor = fillLum < 0.25 ? uIconColor : darken(fillColor.rgb, uIconDarken); } ``` The non-classic path is unchanged. The change is contained to the shader — no new uniforms or plumbing. ## Notes - The `0.25` luminance threshold is hardcoded in the shader to keep the change surgical. It could be promoted to a `render-settings.json` knob if preferred. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 --- .../render/gl/shaders/structure/structure.frag.glsl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/client/render/gl/shaders/structure/structure.frag.glsl b/src/client/render/gl/shaders/structure/structure.frag.glsl index 7ff8c2c39..0aa3a7299 100644 --- a/src/client/render/gl/shaders/structure/structure.frag.glsl +++ b/src/client/render/gl/shaders/structure/structure.frag.glsl @@ -132,8 +132,16 @@ void main() { iconAlpha = iconSample.a * borderMask * inBounds; } - // Composite: tinted icon over player-colored shape - vec3 glyphColor = uIconDarken > 0.0 ? darken(fillColor.rgb, uIconDarken) : uIconColor; + // Composite: tinted icon over player-colored shape. + // Classic icons (uIconDarken > 0) tint the glyph with a darkened player + // color. When the shape itself is already dark, that darkened glyph blends + // into the shape (and the dark territory behind it) and becomes unreadable — + // so flip the glyph to the light icon color when the fill is too dark. + vec3 glyphColor = uIconColor; + if (uIconDarken > 0.0) { + float fillLum = dot(fillColor.rgb, vec3(0.299, 0.587, 0.114)); + glyphColor = fillLum < 0.25 ? uIconColor : darken(fillColor.rgb, uIconDarken); + } vec3 finalRGB = mix(bgColor.rgb, glyphColor, iconAlpha); // Red X overlay for units marked for deletion