Flip structure icon glyph to light on dark territory 🌑 (#4312)

## 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 <noreply@anthropic.com>
This commit is contained in:
Evan
2026-06-16 18:27:40 -07:00
committed by GitHub
parent 6c84919801
commit e6256e6269
@@ -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