Files
openvistapro/docs/plans/terrain-generation.md
2026-05-25 16:47:15 -04:00

6.3 KiB

Terrain Generation Roadmap

Purpose

Define the next terrain-generation workstream so future slices stay small, deterministic, and testable. This plan is intentionally clean-room: it uses only project-owned synthetic fixtures and open math/algorithm ideas, never proprietary VistaPro material.

Current baseline

OpenVistaPro already has:

  • src/terrain_gen.rs with TerrainGenerationSpec, TerrainGenerationSettings, and DeterministicTerrainGenerator, plus cargo test terrain_gen coverage for the determinism/seed note.

  • src/terrain.rs with immutable HeightGrid storage, safe indexing, min/max, and deterministic plane / radial_hill fixtures.

  • src/render.rs with a deterministic top-down preview and a CPU perspective spike that only depends on HeightGrid + Scene.

  • src/scene.rs and src/app_state.rs for scene controls and preview wiring.

  • src/import.rs for the open-format import boundary.

  • docs/plans/initial-roadmap.md and docs/plans/phase-4-formats-scripts-ui.md for the broader project sequence.

The core seeded procedural terrain pipeline now exists; the remaining work in this roadmap is about future generator variants and richer presets, not the first shipped fractal slice.

Decision summary

First generator family: seeded 2D value-noise fBm

Implement a deterministic, seedable fractal terrain family first, built from 2D value noise with fBm-style octaves.

Why this first:

  • It is fully clean-room and easy to describe/test.
  • It produces organic terrain that is more representative than the current plane/hill fixtures.
  • It works well with tiny synthetic grids, which keeps tests fast and stable.
  • It can grow into ridged / warped / terraced variants later without changing the external boundary.

Initial scope should stay modest: generate a single height grid from a seed, dimensions, and a small set of parameters. Do not add erosion, climate, or streaming at first.

API boundary

Keep HeightGrid as pure data plus basic helpers.

Recommended split:

  • src/terrain.rs: immutable grid storage, validation, indexing, min/max, and tiny deterministic fixtures like plane and radial_hill.
  • New generator module, e.g. src/terrain/generation.rs or src/generation.rs: procedural generation logic, seed handling, interpolation/noise helpers, and generator presets.
  • Public API should return HeightGrid and nothing renderer-specific.
  • Any generator metadata should live in a separate spec/config type, not in the grid itself.

A good minimal boundary is:

  • TerrainGenerationSpec or similar: dimensions, seed, octave count, lacunarity, gain, base frequency, amplitude.
  • generate(spec) -> Result<HeightGrid, TerrainError>

If the implementation later needs richer provenance, add a lightweight wrapper beside the spec, but keep the renderer and scene code consuming HeightGrid only.

Roadmap slices

Slice 1: generator module skeleton

Goal: introduce the procedural terrain namespace without changing renderer behavior.

Deliverables:

  • module scaffolding for generator code
  • a small spec type with seed and size fields
  • a deterministic construction path that still returns a HeightGrid

Acceptance:

  • HeightGrid stays unchanged as a storage type
  • generator tests compile without touching render code

Slice 2: deterministic value-noise core

Goal: implement the smallest reusable noise primitive.

Deliverables:

  • seeded lattice/value-noise helper
  • interpolation across grid cells
  • deterministic output for identical inputs

Acceptance:

  • same seed + same spec always yields identical samples
  • different seeds produce different grids
  • tiny grids do not panic at edges

Slice 3: fBm terrain composition

Goal: layer octaves into usable synthetic landscapes.

Deliverables:

  • octave accumulation
  • amplitude/frequency controls
  • normalization into the range expected by the renderer/palette logic

Acceptance:

  • output min/max remain bounded and predictable
  • generated terrain exercises multiple elevation bands in the preview

Slice 4: preset profiles

Goal: provide a few named generator presets for common shapes.

Suggested first presets:

  • plain-like flat terrain via zeroed noise amplitude
  • island / continental profile with a radial falloff mask
  • mountain profile with stronger octave contrast
  • fractal / noise preset exposed through the app shell and CLI as the first shipped procedural family

Acceptance:

  • preset selection is deterministic
  • presets remain thin wrappers around the shared generator core

Slice 5: later enhancements

Only after the core is stable:

  • ridged multifractal terrain
  • domain warping
  • terraces / plateaus
  • simple erosion pass
  • derived slope / normal helpers if the renderer needs them
  • CLI/script integration so generated terrain can be rendered and scripted like imported terrain

Test matrix

Use tiny synthetic fixtures only.

Unit tests

  • dimensions are preserved exactly
  • zero dimensions are rejected
  • identical seed/spec pairs produce identical grids
  • different seeds change output
  • sample access stays in-bounds and row-major expectations remain intact
  • min/max are sane after normalization
  • presets produce the expected broad shape characteristics

Behavior tests

  • a small generated grid renders successfully through the existing top-down path
  • the perspective spike accepts generated grids without special-case code
  • the generator does not mutate renderer or scene state

Validation commands

Run these for each generator slice:

cargo fmt --check
cargo test terrain
cargo test
cargo clippy --all-targets -- -D warnings
cargo run -- render --preset fractal --seed 1337 --width 64 --height 64 --output /tmp/openvistapro-fractal-plan.png

Add one feature-specific smoke command for the slice once the generator has a public entry point, for example a tiny render or sample-generation command that writes to /tmp and proves the generated grid is usable end-to-end.

Definition of done for the workstream

The terrain-generation workstream is ready to close when:

  • the generator module is separate from HeightGrid
  • at least one seeded procedural family exists
  • tests prove determinism and shape invariants
  • generated terrain can flow into the existing render pipeline without special handling
  • future slices can add more generator families without changing the grid storage contract