Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR 0001: Rust building descriptions separate from geometry backends

Status: accepted, 2026-09-13.

Context

Games should be able to describe buildings as reusable Rust recipes without baking one architectural style into the engine. A macro language is premature before its underlying types are useful. An earlier external-authoring prototype was retired.

Decision

ashlar is a Bevy-free domain crate. It depends only on shared math, Serde and typed errors. It describes local solids, reusable parts, instances, material slots and attachment frames. Rust functions and builders author the same editable data that Serde loads.

Finalization validates a recipe and returns an immutable description. Backends consume that description; the domain has no IO or renderer dependencies. All dimensions and placements are local f64 metres in right-handed Y-up space. Style choices live in recipe geometry and supplied material bindings, with instance overrides. There is no mandatory grid or fixed rotation increment.

Consequences

The domain can be tested without a GPU. A future macro is a thin frontend over ordinary types. Mesh generation, physics export and visual preview remain separate implementation steps. The serialized recipe is experimental, not a runtime asset format.

Mesh adapter

GeometryMesher, TriangleMesh and MeshError define the renderer-independent output boundary in the domain. ashlar-manifold evaluates expressions via Manifold's Rust bindings, preserving shared part meshes and instance metadata. The native C++ dependency lives entirely in that adapter, outside the domain and outside any headless runtime graph that consumes fitted results. The kernel can be replaced by another implementation of the port. No architectural style, palette, grid policy or storey dimension is introduced into the backend.

Manifold was chosen to evaluate nested arbitrary solid subtraction without introducing a bespoke boolean kernel. Its native toolchain and first-build source download are explicit tradeoffs; see the recipe workflow for prerequisites.

Expression vocabulary

Shape grew union, array and mirror nodes beside the primitives and subtraction. They express repetition and symmetry as geometry rather than as a loop that produces one element per copy, so the kernel fuses the copies and the cut slots keep working across them: a union creates no cut faces of its own, every copy of an array carries the provenance of its source, and a reflection keeps the provenance of the faces it mirrors. Shape is #[non_exhaustive], and a backend rejects by path what it does not implement, so the vocabulary can grow without breaking one.

Material and physical detail

The domain also describes opaque PBR material libraries independently of building recipes. Texture keys remain opaque; the render adapter handles PNG loading, color spaces, repeat sampling and tangent generation. Per-material metre scale and pattern offsets preserve shared geometry across finish variants. Optional element cut slots use native surface provenance to split outer and cut batches; no architectural role or material type is baked into that mechanism.

Texture projection is per element: planar by default, box mapping for repeats that line up across coplanar elements of separate modules, and cylindrical for a metre-true wrap with one seam. The mode is applied after evaluation, so the mesher port is unchanged by it. Meshes are indexed, with vertices shared where position, normal and UV agree, which keeps creases and UV seams split and nothing else.

Physical chamfers are an explicit box primitive, not an implicit global smoothing policy. General edge modifiers can be added independently. The example content lives in ashlar-showcase together with the generator for its study textures and material RON, and demonstrates shared modules in two structures.

Sockets and collision

See ADR 0002: sockets derive an instance's pose, and elements declare a conservative convex collision proxy that the mesher derives from the evaluated solid.

Terrain fitting

Ground fitting is a pure calculation over a supplied triangulated height patch. The domain owns footprint, entrance, policy and fitted convex-solid data. It does not sample a world, select a style or create renderer/physics handles. Triangle clipping checks the full support and approach surface, including extrema between corners. The caller says which way its own cells split, because a patch read along the other diagonal is a different surface from the one its players walk on. World sampling and convex collision conversion belong to the embedding game.

ashlar-showcase is renderer-free content. ashlar-bevy shares mesh and PBR upload without a native kernel dependency, so a consumer can draw fitted results without compiling C++. This is not yet a building streaming system.