feat(client): animated terrain fluids + viewport-windowed ground blend on a 3x world (#222) #223

Merged
marco merged 2 commits from feat/terrain-animation into main 2026-07-31 10:05:01 +02:00
Collaborator

Summary

Two goals: make the terrain feel alive, and let the world be much bigger without the client paying for it.

  • Animated terrain fluids (#222). Ocean, coast, lakes, lava, dirty water and four one-way rivers now cycle real frames — display-only, data-driven by content/terrain_anim.txt (cyclic surfaces ping-pong 0-1-2-3-2-1; rivers loop one-way, the direction is the biome). The blend isn't re-run per frame — only the biome's atlas-cell pixels swap, so the shoreline blend animates for free. Frames are colour-matched to a common mean (genland --match) so a loop ripples instead of pulsing in brightness.
  • The ground blend is now client-side and bounded by the VIEWPORT, not the map. The SDF field/weight/palette textures are built only for a window around the camera (visible cells + a border margin) and rebuilt as the camera nears the edge. Load time and memory are now independent of map size — which is what makes the bigger world free on the client. Correctness holds because Compute is local: a cell ≥ EdgeMargin inside the window gets the exact same weights as the whole-map field.
  • Tripled the world to 2304×1728 and placed every animated fluid in open, prop-free areas (fluids only sit on non-walkable cells; props are re-scattered per biome onto walkable land, so the two never overlap). Redwood forest scales densely with its area; other biome props keep their authored counts.
  • Docs: docs/architecture.md gains a "Client ground blend" section (the durable model). scripts/genscatter.py (prop re-scatter) is now tracked in scripts/.

No wire-contract change — the animation and blend are purely client-side display, so ProtocolVersion is untouched.

Screenshots / recording

Captured by piloting the Debug client via the harness (docs/debug-harness.md) on the fresh 3× world. Each is a settled frame (/tp then wait ~6s — the visual position slides to the target, so an immediate capture shows the slide path, not the destination):

login test
key Enter → type /tp <x> <y> → key Enter
# wait ~6s for the camera to settle, then:
screenshot <path>            # a second shot ~1.4s later confirms fluid motion
  • Coast (tp 114 1571) — desert → beach → coast → animated teal ocean; smooth SDF blend, water animates (~13% of pixels move between two 1.4s-apart frames).
  • River (tp 680 1320) — one-way teal river flowing between dark redwood floor and grass (~5% motion, loop).
  • Lake (tp 1858 339) — an arctic lake in tundra, rounded shore, in an open prop-free area (~7% motion).
  • Pink sand (tp 177 1611) — a 4-biome blend: ocean → rare pink sand → desert → pale salt-plain.
  • Redwood forest (spawn 327 1172) — titanic trunks densely scattered around the central glade, dark blended forest floor.

(PNGs captured via the harness and shared with the author.)

How it was tested

  • New unit tests in TerrainBlendFieldTestsWindow_interior_weights_match_the_whole_map_field pins the locality property the whole viewport-windowing rests on (a cell ≥16 inside a window == the whole-map field); TerrainAnimationTests covers the frame-index maths (loop vs ping-pong).
  • Full solution build: 0 warnings, 0 errors. dotnet test: all green (Client.Core 165, GameServer 271, Shared 107, …).
  • Visual verification: the screenshots above, plus uniform-biome spot checks (desert/grass/tundra/salt-plain render their exact tints) and confirmation the 4M-cell world loads instantly on the client (viewport-windowed) and on the server.

Checklist

  • just lint passes (CSharpier + analyzers, zero warnings)
  • just test is green
  • The whole solution builds (client and tools included)
  • Multi-platform preserved (server on Win/macOS/Linux, client on Win/macOS — the shader ships as a pre-compiled cross-platform .xnb, not compiled at build)
  • Tests added/updated for this change
  • Linked the related issue (#222) and its Definition of Done is met
## Summary Two goals: make the terrain feel **alive**, and let the world be **much bigger** without the client paying for it. - **Animated terrain fluids (#222).** Ocean, coast, lakes, lava, dirty water and four one-way rivers now cycle real frames — display-only, data-driven by `content/terrain_anim.txt` (cyclic surfaces ping-pong `0-1-2-3-2-1`; rivers loop one-way, the direction *is* the biome). The blend isn't re-run per frame — only the biome's atlas-cell pixels swap, so the shoreline blend animates for free. Frames are colour-matched to a common mean (`genland --match`) so a loop ripples instead of pulsing in brightness. - **The ground blend is now client-side and bounded by the VIEWPORT, not the map.** The SDF field/weight/palette textures are built only for a **window** around the camera (visible cells + a border margin) and rebuilt as the camera nears the edge. Load time and memory are now independent of map size — which is what makes the bigger world free on the client. Correctness holds because `Compute` is *local*: a cell ≥ `EdgeMargin` inside the window gets the exact same weights as the whole-map field. - **Tripled the world to 2304×1728** and placed every animated fluid in open, prop-free areas (fluids only sit on non-walkable cells; props are re-scattered per biome onto walkable land, so the two never overlap). Redwood forest scales densely with its area; other biome props keep their authored counts. - Docs: `docs/architecture.md` gains a "Client ground blend" section (the durable model). `scripts/genscatter.py` (prop re-scatter) is now tracked in `scripts/`. No wire-contract change — the animation and blend are purely client-side display, so `ProtocolVersion` is untouched. ## Screenshots / recording Captured by piloting the Debug client via the harness (`docs/debug-harness.md`) on the fresh 3× world. Each is a settled frame (`/tp` then wait ~6s — the visual position slides to the target, so an immediate capture shows the slide path, not the destination): ``` login test key Enter → type /tp <x> <y> → key Enter # wait ~6s for the camera to settle, then: screenshot <path> # a second shot ~1.4s later confirms fluid motion ``` - **Coast (tp 114 1571)** — desert → beach → coast → animated teal ocean; smooth SDF blend, water animates (~13% of pixels move between two 1.4s-apart frames). - **River (tp 680 1320)** — one-way teal river flowing between dark redwood floor and grass (~5% motion, loop). - **Lake (tp 1858 339)** — an arctic lake in tundra, rounded shore, in an open prop-free area (~7% motion). - **Pink sand (tp 177 1611)** — a 4-biome blend: ocean → rare pink sand → desert → pale salt-plain. - **Redwood forest (spawn 327 1172)** — titanic trunks densely scattered around the central glade, dark blended forest floor. _(PNGs captured via the harness and shared with the author.)_ ## How it was tested - New unit tests in `TerrainBlendFieldTests` — `Window_interior_weights_match_the_whole_map_field` pins the locality property the whole viewport-windowing rests on (a cell ≥16 inside a window == the whole-map field); `TerrainAnimationTests` covers the frame-index maths (loop vs ping-pong). - Full solution build: **0 warnings, 0 errors**. `dotnet test`: **all green** (Client.Core 165, GameServer 271, Shared 107, …). - Visual verification: the screenshots above, plus uniform-biome spot checks (desert/grass/tundra/salt-plain render their exact tints) and confirmation the 4M-cell world loads instantly on the client (viewport-windowed) and on the server. ## Checklist - [x] `just lint` passes (CSharpier + analyzers, zero warnings) - [x] `just test` is green - [x] The whole solution builds (client and tools included) - [x] Multi-platform preserved (server on Win/macOS/Linux, client on Win/macOS — the shader ships as a pre-compiled cross-platform `.xnb`, not compiled at build) - [x] Tests added/updated for this change - [x] Linked the related issue (#222) and its Definition of Done is met
feat: SDF per-pixel ground blend + expanded biomes on a 2x world
All checks were successful
ci / Lua content lint (pull_request) Successful in 11s
ci / Lint & Test (pull_request) Successful in 6m28s
e402f7ac6a
feat(client): animated terrain fluids + viewport-windowed ground blend on a 3x world (#222)
Some checks failed
ci / Lua content lint (pull_request) Successful in 11s
ci / Lint & Test (pull_request) Failing after 1m10s
b508e2a157
Author
Collaborator

Screenshots (harness, settled frames)

Coast — desert→beach→coast→animated ocean (tp 114 1571)

coast.png

River — one-way teal flow between redwood floor and grass (tp 680 1320)

river.png

Lake — arctic lake in tundra, open prop-free area (tp 1858 339)

lake.png

Pink sand — 4-biome blend: ocean→pink sand→desert→salt-plain (tp 177 1611)

pink_sand.png

Redwood forest — titanic trunks around the central glade (spawn 327 1172)

redwood.png

## Screenshots (harness, settled frames) **Coast — desert→beach→coast→animated ocean (tp 114 1571)** ![coast.png](https://git.homelab.devncode.it/attachments/9649fb26-c46d-49d6-b138-311f51d2f62e) **River — one-way teal flow between redwood floor and grass (tp 680 1320)** ![river.png](https://git.homelab.devncode.it/attachments/92b26bbf-e613-420f-b383-dd577d435e79) **Lake — arctic lake in tundra, open prop-free area (tp 1858 339)** ![lake.png](https://git.homelab.devncode.it/attachments/172b87ee-52d8-4ac9-bd50-ee5dbb3e251b) **Pink sand — 4-biome blend: ocean→pink sand→desert→salt-plain (tp 177 1611)** ![pink_sand.png](https://git.homelab.devncode.it/attachments/7dbc7743-c6bd-48f9-bbba-0a387eef43f6) **Redwood forest — titanic trunks around the central glade (spawn 327 1172)** ![redwood.png](https://git.homelab.devncode.it/attachments/79a80a91-a883-40b6-ade5-e45b709312f3)
panda force-pushed feat/terrain-animation from b508e2a157
Some checks failed
ci / Lua content lint (pull_request) Successful in 11s
ci / Lint & Test (pull_request) Failing after 1m10s
to 02e52516e6
All checks were successful
ci / Lua content lint (pull_request) Successful in 10s
ci / Lint & Test (pull_request) Successful in 8m20s
2026-07-30 21:59:07 +02:00
Compare
marco merged commit d6fbae59dd into main 2026-07-31 10:05:01 +02:00
marco deleted branch feat/terrain-animation 2026-07-31 10:05:02 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo!223
No description provided.