Skip to content

[M2-TILE-01] Tilemap data + auto-depth from tile height (FR-2.6) - #76

Merged
offdev merged 2 commits into
masterfrom
feat/m2-tile-01-tilemap
Oct 6, 2026
Merged

offdev merged 2 commits into
masterfrom
feat/m2-tile-01-tilemap

Conversation

@offdev

@offdev offdev commented Oct 6, 2026

Copy link
Copy Markdown
Owner

Implements M2-TILE-01 (roadmap/M2-rendering-2.5d.md): tilemap data + auto-depth from tile height (FR-2.6).

What

  • laige::render::TileMap<Backend> (header-only, templated over the SimMath backends — the presentation.h pattern; TileMap::Options is the M2-ISO-02 table's Options, one type, no duplicated validation):
    • The chunked tile grid — per tile: textureId (the game-assigned atlas ref), height, animationId (data only in M2; M2-TILE-02 cycles frames from it).
    • Auto-depth wiring: the height lives in the owned IsoDepthKeyTable alone (one source of truth); setTile/rebuild route the heights into the table's incremental / from-scratch update — a tile's Y height is automatically reflected in its depth key (radius 0: a height edit changes exactly that cell's key).
    • declareTo(batcher, options) — the S-5 batch path: one SpriteItem per tile (tile-center pos (gx+0.5, gy+0.5), scale (1,1), rotation 0, the fixed-frame UV, the table's key — depthOverride stays false, G-R11, the tile's texture as atlasId) in the grid's row-major order (RENDER-003 — the tile's grid position is its stable identity). The batcher's (atlas, material, blend) grouping renders a tilemap in a bounded number of draw calls: one per distinct (textureId, material, blend) combination — tiles of one chunk sharing one texture and blend form one group.
  • Rejected operations leave the tile data and the table unchanged (validated before any write; the Status is the failure channel — LOG-002); the happy update/declare paths log nothing.
  • ARCH-009: headless-buildable, presentation-only, sim-phase writes / render-phase reads; the declare loop allocates nothing per frame (FR-2.2 — pinned by a 1000-frame zero-allocation test).

Tests (CTest entry tilemap — the step's Verify command)

17 tests / 6 suites, no GL (runs in every tree): grid options + flat/empty contract, per-tile writes / rejected edits / scene load, the rebuild-from-scratch == incremental property, the height change → depth-table increment (roadmap unit test, radius-0 pin + hand-computed golden + independent oracle), the hand-computed 4×4 chunk's tile quad positions/depth goldens (16 literal keys, the exact tie structure) + grouping (2 textures → exactly 2 groups, ascending order, hand-computed in-group instance sequences) + cross-frame determinism, the frame protocol / failure paths / custom options / no-log happy path, and the zero-allocation declare loop.

Docs (same change)

NEW docs/api/tilemap.md; docs/README.md index; the module README; docs/concepts/coordinates.md (new §4.9 + the §5 conversion row + Related); roadmap box + board (M2 17/33, total 63/194) + change-log row.

API surface / budgets / compat

  • laige-api.json regenerated: 1233 → 1265 symbols, 38 → 39 headers (+32 additive).
  • No standalone budgets.json entry (count stays 16 — the BudgetHarnessTable pin): the per-frame declare cost is part of the composite 50k render-CPU budget (sprites_50k_cpu, M2-PERF-01).
  • Additive only — no existing symbol's signature or meaning changed.

Verification (all local, no GPU)

  • All six trees warning-clean + full ctest green: build 112/112, build-clang 112/112, build-release 101/101, build-shared 112/112, build-asan 109/109, build-tsan 109/109.
  • ctest -R tilemap green (17/17); api-real-tree/api-check-fresh (6/6), tools/laige-include-lint (68 source files), tools/laige-determinism-lint (28 sim files, 0 violations) green.

Note for the CI lanes: MSVC is not compiled locally — the new code path uses only explicit casts (no implicit size_t→u32/int narrowing); the ci:windows label gate applies as usual.

offdev added 2 commits October 6, 2026 18:31
- TileMap<Backend>: chunked tile grid (per tile: textureId, height,
  animationId — data only in M2) owning one IsoDepthKeyTable; the
  height lives in the table alone (one source of truth) and setTile/
  rebuild route into the table's incremental/from-scratch update —
  the auto-depth wiring (a height edit changes exactly that cell's
  key, radius 0).
- declareTo(batcher, options): the S-5 batch path — one SpriteItem per
  tile (tile-center pos, unit scale, the table's key — depthOverride
  stays false, G-R11, the tile's texture as atlasId, the fixed-frame
  UV) in row-major grid order (RENDER-003); the batcher's (atlas,
  material, blend) grouping renders a tilemap in a bounded number of
  draw calls (one per chunk group sharing texture + blend).
- Tests: tests/laige-render/tilemap_tests.cpp (CTest entry tilemap —
  the step's Verify command; 17 tests / 6 suites, no GL): grid
  options/flat-empty contract, per-tile data writes/rejected edits/
  scene load, the rebuild == incremental property, the height ->
  depth-table wiring, the hand-computed 4x4 chunk's quad positions +
  depth goldens + grouping + cross-frame determinism, the frame
  protocol/failure paths/custom options/no-log happy path, and the
  1000-frame zero-allocation declare loop (FR-2.2).
- Docs (same change): NEW docs/api/tilemap.md; docs/README index; the
  module README; docs/concepts/coordinates.md (NEW section 4.9 + the
  section 5 conversion row + Related); roadmap box + board (M2 17/33,
  total 63/194) + change-log row.
- API manifest: laige-api.json regenerated (1233 -> 1265 symbols,
  38 -> 39 headers). No budgets.json entry (the per-frame declare
  cost is part of the composite 50k render-CPU budget, M2-PERF-01).
- Verified: all six local trees warning-clean + full ctest green
  (build 112/112, build-clang 112/112, build-release 101/101,
  build-shared 112/112, build-asan 109/109, build-tsan 109/109);
  api-real-tree/api-check-fresh, include-lint, determinism-lint
  green.
…l header edits)

The manifest was regenerated before the last tilemap.h edits (the
cassert include + the rebuild invariant assert shifted the line
numbers of the 32 new symbols); the CI api-check-fresh lane caught
the 64-line diff. Regenerated: symbol set and headers unchanged,
line fields only.
@offdev
offdev merged commit ff5529b into master Oct 6, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant