Repository navigation
feat(terrain): renderoni/terrain — tiled heightfield cache and streamed LOD mesh - #40
cmaycumber wants to merge 1 commit into
Conversation
…ed LOD terrain mesh TiledHeightfield caches a caller's analytic height function in lazily filled, LRU-evicted tiles (bilinear heightAt, slopeAt with a tile-free coarse mode, prewarm, extra cached channels). Tiles are a pure function of the sampler so evictions rebuild bit-identically. TerrainMesh streams chunked, distance-LOD three.js terrain with skirts, a per-update build budget, unloading, any material, per-vertex attribute callbacks, and ray picking with an analytic raymarch fallback. TileCache and Buckets are exported on their own. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
elemarin
left a comment
There was a problem hiding this comment.
Thanks @cmaycumber. The lazy tile cache and budgeted chunk streaming are a good fit, and the tests are real. Build, typecheck and tests (243/243) pass, and dist/terrain is emitted. Before merge:
Bugs
- Frozen chunk matrices:
matrixWorldAutoUpdate = falseis applied at build time, so moving, scaling or reparentingterrain.groupis silently ignored, andpick()/raymarch()disagree for a moved group. Please drop the freeze or document the limit. - LOD-border normals: normals use each chunk's own step, so fine and coarse neighbours shade differently at the shared edge. The docs say borders "shade identically". Please fix the claim, or compute normals at a fixed spacing.
- Skirt: a fixed 6 m skirt won't hide cracks on rough terrain with the 18.75 m coarse LOD. Please scale it with the LOD spacing.
- Validation:
cacheTiles: 0throws a TypeError,chunkSize: 0andlodSpacing: [0]are accepted, NaN input creates junk tiles,prewarm(…, Infinity)loops forever, andtileKeyaliases beyond ±32768 tiles.
Generality
5. The defaults look tuned to Sunder March (step 9.4, 64-cell tiles, [2.5, 6.25, 18.75], [220, 520, 1150], chunkSize 187.5, raymarch constants). Please derive them from chunkSize/size, or put them in one named DEFAULTS block with units.
6. Only square, origin-centred worlds are supported. Please consider an origin/width/depth option. floor/ceiling read as water concepts; minHeight/maxHeight would be more generic.
7. Buckets (settlements and roads) isn't terrain. Please drop it from this PR.
Tests
8. Please add tests for LRU recency, negative and border coordinates, cross-LOD seams, a moved group, and add renderoni/terrain to tests/exports.test.ts.
Happy to re-review quickly.
A new
renderoni/terrainentry point for large procedural worlds, extracted from a shipping game.TiledHeightfield: caches a caller's analytic height function in lazily filled, LRU-evicted tiles. Subclass it and overrideanalytic(and optionallysampleExtrafor extra cached channels). It provides bilinearheightAt,slopeAtwith a tile-free coarse mode,prewarm,channel()and stats. Tiles are a pure function of the sampler, so an evicted tile rebuilds bit-identically, which keeps determinism.TerrainMesh: streamed, chunked, distance-LOD three.js terrain over anyHeightSource. It has skirts against cracks, a per-update build budget, unloading, any material, and aTerrainShadingcallback for per-vertex attributes (colour, texture-layer weights). Picking uses loaded chunks with an analytic raymarch fallback (floor and ceiling options for water).TileCacheandBuckets: exported for reuse.This is presentation and caching only; it never writes simulation state. The game's own renderer now runs on
TerrainMesh, and its chunk geometry was checked value by value against the original implementation at every LOD.Verification: typecheck, build, 243/243 tests.
🤖 Generated with Claude Code