# Terrain Authoring

Terrain is a first-class authored actor backed by `TerrainDesc`. Add it from the Inspector's
**Add Component** shelf by searching for Terrain. It conflicts with other geometry sources on the
same actor: Primitive, Brush, Static Mesh Renderer, and Skinned Mesh Renderer.

## Foundation Workflow

The Terrain card exposes grid resolution, sample spacing, height scale, chunk size, generated
collision, shadows, base material status, and validation. **Resize Flat** deliberately replaces the
height array with a flat grid; normal numeric edits retain all height samples. Every accepted edit is
one reflected history transaction and rebuilds only that terrain's generated chunks.

## Sculpt Workflow

Select a terrain actor and enable the mountains button in the existing horizontal viewport toolbar.
Choose **Raise**, **Lower**, **Flatten**, **Smooth**, or **Noise**, then LMB-drag across the terrain.
The radius and strength controls use world meters; the colored footprint follows the sampled terrain
surface and identifies the active mode.

Each drag is one stroke and one undo entry, regardless of how many interpolated dabs it contains.
Escape or right-click restores the exact pre-stroke height grid; when no stroke is active, either input
closes the sculpt tool. Flatten captures the height under the initial press. Smooth samples a stable
copy of the current neighborhood per dab, and Noise uses a deterministic per-stroke seed so authored
results remain reproducible.

## Material Layers And Paint Workflow

The Terrain card owns up to four shared Material or Material Instance layers. Use the plus icon to
add a channel, then browse or drag a material into its selector. Each layer exposes independent UV
tiling and can be reordered or removed; channel weights move with the layer. The first added layer
adopts a legacy Base Material assignment so existing terrain converts without visual loss.

With at least two layers assigned, select the paint-brush button beside Sculpt in the existing
horizontal viewport toolbar. Choose **Paint** or **Erase**, select the target layer, then LMB-drag.
Paint increases the selected channel while proportionally reducing the others; Erase redistributes
weight to the remaining channels. Every sample stays normalized to 255, the terrain-following cyan
or amber footprint previews the brush, and release records one undo entry. Escape or right-click
restores the exact pre-stroke weights or closes the idle tool.

`assets/levels/terrain_authoring_showcase.scn.ron` is the Terrain entry in the deterministic
[sample regression pack](sample-regression-pack.md). Its 5×5
grid forms an asymmetric hill split into four 2×2-quad chunks. It validates chunk boundaries,
normals, selection through generated children, collider generation, inspector state, and save
stripping. Height geometry is inline, while its committed shared Material and Material Instance
references deliberately validate terrain dependency resolution and layer transport.

## Data And Hydration

- Heights are finite normalized values multiplied by Height Scale at hydration time.
- Up to four Material/Material Instance references remain shared assets rather than copied material
  descriptors. RGBA8 weights are stored per height sample; an absent array means full layer zero.
- Generated chunk vertex colors carry normalized weights into the `blacksite_surface` terrain
  material, which blends layer base color/texture, normal, metallic, and roughness inputs.
- Sample Spacing controls X/Z distance and the grid remains centered on the actor origin.
- Chunk Size is measured in quads. Neighboring chunks share boundary samples but not runtime mesh
  assets, allowing later stroke updates to rebuild only affected chunks.
- Missing, unassigned, or invalid layer references produce a clear diagnostic and visible terrain
  fallback rather than missing geometry.
- Generated `HydratedTerrainChunk` children own mesh, optional trimesh collider, and shadow state.
  They are runtime-only and never serialized as authored actors.
- Foundation chunks are explicitly excluded from Solari submission and force their layer blend
  through Bevy's forward opaque raster path while Auto/Solari is active. Raster material-layer
  parity is shipped; matching Solari terrain evaluation remains explicit future work, and the editor
  never substitutes a semantically different proxy.

The terrain foundation is recorded in [ADR 0039](../adr/0039-inline-height-grid-terrain-foundation.md),
and layer weight persistence/render transport in
[ADR 0040](../adr/0040-terrain-material-layer-weights.md).

Live native-Wayland evidence and focused verification are recorded in the
[terrain foundation evaluation](evaluations/terrain-foundation/).
