# ADR 0040: Terrain Material Layers And Weight Transport

## Status

Accepted

## Context

Terrain needs multiple shared project materials, editable weight painting, deterministic persistence,
and raster behavior that remains consistent between the editor and game. Duplicating full material
descriptors into scenes would break the shared Material/Material Instance contract. Storing floating
point weights would also create avoidable scene churn and normalization drift.

## Decision

`TerrainDesc` stores at most four `TerrainMaterialLayer` entries referencing shared assets and one
RGBA8 weight tuple per height sample. Every explicit tuple sums to 255; an absent weight array means
full channel-zero coverage for backward compatibility. Generated terrain chunks transport normalized
weights through `Mesh::ATTRIBUTE_COLOR`.

`shared` owns persistence, validation, chunk generation, and a runtime-only terrain material binding.
`blacksite_surface` owns the terrain-specific `ExtendedMaterial` and blends each resolved layer's
albedo, tangent-space normal, metallic, and roughness inputs. Invalid or unavailable references leave
the existing visible terrain fallback in place and emit diagnostics. Terrain remains excluded from
Solari geometry until a matching ray-tracing evaluator exists, and its opaque material is therefore
forced through Bevy's forward raster path even when the project default is deferred.

Editor paint strokes materialize the implicit default map only when needed. Each pointer stroke is a
single reflected `TerrainDesc` transaction; cancel restores the exact original descriptor.

## Consequences

- Four channels give fixed shader bindings, compact deterministic scenes, and simple normalization.
- Adding more than four simultaneous layers requires a deliberate schema and renderer revision.
- Weight resolution follows the height grid; independent high-resolution splat maps remain future work.
- Terrain raster rendering gains project-material texture parity while Solari terrain parity remains an
  explicit later milestone rather than using a semantically different proxy.
