diff --git a/src/client/render/gl/passes/structure-pass.ts b/src/client/render/gl/passes/structure-pass.ts index ea186f0ea..d5874fad8 100644 --- a/src/client/render/gl/passes/structure-pass.ts +++ b/src/client/render/gl/passes/structure-pass.ts @@ -76,6 +76,7 @@ export class StructurePass { private uDotsThreshold: WebGLUniformLocation; private uDotScale: WebGLUniformLocation; private uScaleFactor: WebGLUniformLocation; + private uIconGrowZoom: WebGLUniformLocation; private uShapeScales: WebGLUniformLocation; private uIconFills: WebGLUniformLocation; private uGhostAlpha: WebGLUniformLocation; @@ -147,6 +148,7 @@ export class StructurePass { "uDotsThreshold", )!; this.uScaleFactor = gl.getUniformLocation(this.program, "uScaleFactor")!; + this.uIconGrowZoom = gl.getUniformLocation(this.program, "uIconGrowZoom")!; this.uShapeScales = gl.getUniformLocation(this.program, "uShapeScales")!; this.uIconFills = gl.getUniformLocation(this.program, "uIconFills")!; this.uGhostAlpha = gl.getUniformLocation(this.program, "uGhostAlpha")!; @@ -334,6 +336,7 @@ export class StructurePass { gl.uniform1f(this.uDotsThreshold, ss.dotsZoomThreshold); gl.uniform1f(this.uDotScale, ss.dotScale); gl.uniform1f(this.uScaleFactor, ss.iconScaleFactorZoomedOut); + gl.uniform1f(this.uIconGrowZoom, ss.iconGrowZoom); // Build per-structure uniform arrays from settings, ordered by atlas column const scales = new Float32Array(ATLAS_COLS); diff --git a/src/client/render/gl/render-settings.json b/src/client/render/gl/render-settings.json index b9da108d4..2a0105c4c 100644 --- a/src/client/render/gl/render-settings.json +++ b/src/client/render/gl/render-settings.json @@ -82,10 +82,11 @@ "railAlpha": 1 }, "structure": { - "iconSize": 65, + "iconSize": 50, "dotsZoomThreshold": 1.2, "dotScale": 0.3, - "iconScaleFactorZoomedOut": 3.5, + "iconScaleFactorZoomedOut": 3, + "iconGrowZoom": 7, "shapes": { "City": { "scale": 1, diff --git a/src/client/render/gl/render-settings.ts b/src/client/render/gl/render-settings.ts index adbd373cf..1b661dea9 100644 --- a/src/client/render/gl/render-settings.ts +++ b/src/client/render/gl/render-settings.ts @@ -89,6 +89,13 @@ export interface RenderSettings { /** Icon size multiplier when zoomed out past dotsZoomThreshold. */ dotScale: number; iconScaleFactorZoomedOut: number; + /** + * Zoom level at which structures begin growing with the canvas. + * Below this zoom, structures stay at a fixed screen size (capped). + * Above this zoom, they grow proportionally to zoom — i.e. world-anchored, + * so they cover a fixed area of the map. + */ + iconGrowZoom: number; shapes: Record; highlightOutlineWidth: number; highlightDimAlpha: number; diff --git a/src/client/render/gl/shaders/structure/structure.vert.glsl b/src/client/render/gl/shaders/structure/structure.vert.glsl index b71006637..41be22f30 100644 --- a/src/client/render/gl/shaders/structure/structure.vert.glsl +++ b/src/client/render/gl/shaders/structure/structure.vert.glsl @@ -14,6 +14,7 @@ uniform float uIconSize; uniform float uDotsThreshold; uniform float uDotScale; uniform float uScaleFactor; +uniform float uIconGrowZoom; uniform float uShapeScales[ATLAS_COLS]; uniform float uIconFills[ATLAS_COLS]; @@ -38,6 +39,11 @@ void main() { float iconScale; if (uZoom <= uDotsThreshold) { iconScale = uDotScale; + } else if (uZoom >= uIconGrowZoom) { + // World-anchored: grow proportionally to zoom so the structure covers a + // fixed area of the map. Past this zoom, structures should feel like + // they're "on" the canvas rather than overlaid at constant pixel size. + iconScale = uZoom / uIconGrowZoom; } else { iconScale = min(1.0, uZoom / uScaleFactor); }