[art] Terrain tiles render as flat solid-color rectangles — atlas exists but map draw falls back to fillRect #11

Closed
opened 2026-09-23 14:33:44 +00:00 by Mark · 1 comment
Owner

Asset gap (rendering side).

The sprite atlas declares floor, wall, door_closed, door_open, barrel, crate, terminal, mainframe, pod regions at row 0 (web/assets/sprites.png + godot/scripts/sprite_atlas.gd). The atlas is real content (100% non-bg coverage for floor/wall/door regions per pixel analysis).

However: web/js/ui.js map-draw code uses ctx.fillRect(x * TILE, y * TILE, TILE, TILE) for the terrain layer, not ctx.drawImage(sprImg, ...). Confirmed via screenshot at tools/playwright_smoke.png: the map reads as flat color blocks, not atlas tiles.

The Godot side (godot/scripts/grid_map.gd + tile_set_builder.gd) does use the atlas via TileMap + TileSetAtlasSource — that path is correct.

What's missing (web only):

  • Web map renderer in web/js/ui.js should drawImage(sprImg, sx, sy, 32, 32, dx, dy, TILE, TILE) for terrain tiles using the same SPR map as the units
  • The terrain atlas is currently 32×32 per tile but rendered at TILE size (TILE constant = 28 from data? verify). If the scale mismatch is the actual blocker, bump TILE to 32 or scale the draw call.

Acceptance:

  • Sector 7 Underworks map shows textured floor/wall/door tiles, not flat color rectangles
  • Atlas update path stays single source of truth (web/js/ui.js SPR == godot/scripts/sprite_atlas.gd REGIONS)

Refs: web/js/ui.js line ~30 has the SPR map and drawImage for units; the map-draw block uses fillRect.

**Asset gap (rendering side).** The sprite atlas declares `floor`, `wall`, `door_closed`, `door_open`, `barrel`, `crate`, `terminal`, `mainframe`, `pod` regions at row 0 (`web/assets/sprites.png` + `godot/scripts/sprite_atlas.gd`). The atlas is real content (100% non-bg coverage for floor/wall/door regions per pixel analysis). **However:** `web/js/ui.js` map-draw code uses `ctx.fillRect(x * TILE, y * TILE, TILE, TILE)` for the terrain layer, not `ctx.drawImage(sprImg, ...)`. Confirmed via screenshot at `tools/playwright_smoke.png`: the map reads as flat color blocks, not atlas tiles. The Godot side (`godot/scripts/grid_map.gd` + `tile_set_builder.gd`) does use the atlas via TileMap + TileSetAtlasSource — that path is correct. **What's missing (web only):** - Web map renderer in `web/js/ui.js` should `drawImage(sprImg, sx, sy, 32, 32, dx, dy, TILE, TILE)` for terrain tiles using the same SPR map as the units - The terrain atlas is currently 32×32 per tile but rendered at TILE size (TILE constant = 28 from data? verify). If the scale mismatch is the actual blocker, bump TILE to 32 or scale the draw call. **Acceptance:** - Sector 7 Underworks map shows textured floor/wall/door tiles, not flat color rectangles - Atlas update path stays single source of truth (`web/js/ui.js` SPR == `godot/scripts/sprite_atlas.gd` REGIONS) Refs: `web/js/ui.js` line ~30 has the SPR map and `drawImage` for units; the map-draw block uses `fillRect`.
Author
Owner

Fixed in d2ecf25 (cybertactics#11).

Quick note on the original framing: the web map renderer was already using drawImage (via spr()) for floor/wall when sprReady=true — that path was correct. The fillRect calls at web/js/ui.js:46-54 are the pre-atlas-load fallback that fires only while the sprite image is still fetching. Probe via headless Chromium showed sprReady=true post-deploy and the canvas pixels of visible tiles match the atlas palette exactly (wall center (36,52,74), floor center (30,42,58) before this fix, after matches the new brick/panel pixels).

The real issue was the atlas content itself: the floor tile was 32x32 of (17,24,38) base + one dust speck, and the wall tile was 4 horizontal bands of solid color. At TILE=28 render those read as flat blocks even though the atlas was being drawn.

Fix: enhanced the floor + wall tiles in web/assets/sprites.png (and godot/assets/sprites.png — same file, single source of truth):

  • Floor: 2x2 panel grid with light grout lines + scattered dust/wear specks per quadrant. Uses only existing palette.
  • Wall: 4-course brick pattern (stretcher-bond offset on courses 2+4), dark mortar lines, top courses brighter than bottom for natural lighting falloff. Uses only existing palette.

All other tiles (door, scenery, units) untouched. Atlas stays the single source of truth for both web (web/js/ui.js SPR map) and Godot (godot/scripts/sprite_atlas.gd REGIONS).

Local make ci green (7 smoke scripts + lint + playwright + godot-import). Note: forgejo-hosted CI run #18 failed on actions/setup-python@v5 with version 3.12 not found for this operating system — pre-existing runner-side issue that also blocked the prior cybertactics#10 run #17. Code itself is correct; runner infra is the gate to clear next time.

Fixed in d2ecf25 (cybertactics#11). Quick note on the original framing: the web map renderer was already using drawImage (via spr()) for floor/wall when sprReady=true — that path was correct. The `fillRect` calls at web/js/ui.js:46-54 are the pre-atlas-load fallback that fires only while the sprite image is still fetching. Probe via headless Chromium showed sprReady=true post-deploy and the canvas pixels of visible tiles match the atlas palette exactly (wall center (36,52,74), floor center (30,42,58) before this fix, after matches the new brick/panel pixels). The real issue was the atlas content itself: the floor tile was 32x32 of (17,24,38) base + one dust speck, and the wall tile was 4 horizontal bands of solid color. At TILE=28 render those read as flat blocks even though the atlas was being drawn. Fix: enhanced the floor + wall tiles in web/assets/sprites.png (and godot/assets/sprites.png — same file, single source of truth): - Floor: 2x2 panel grid with light grout lines + scattered dust/wear specks per quadrant. Uses only existing palette. - Wall: 4-course brick pattern (stretcher-bond offset on courses 2+4), dark mortar lines, top courses brighter than bottom for natural lighting falloff. Uses only existing palette. All other tiles (door, scenery, units) untouched. Atlas stays the single source of truth for both web (web/js/ui.js SPR map) and Godot (godot/scripts/sprite_atlas.gd REGIONS). Local `make ci` green (7 smoke scripts + lint + playwright + godot-import). Note: forgejo-hosted CI run #18 failed on actions/setup-python@v5 with `version 3.12 not found for this operating system` — pre-existing runner-side issue that also blocked the prior cybertactics#10 run #17. Code itself is correct; runner infra is the gate to clear next time.
Mark closed this issue 2026-09-24 16:01:44 +00:00
Sign in to join this conversation.
No description provided.