t4as

Software & Sims · 2026

mapgen: Procedural Ruins and an Underground District

A Python generator for an abandoned world built by giants: eroded terrain, layered megastructure ruins and a multilayer artificial underground, exported as engine-ready glTF. Every triangle can be traced back to the structure or route that made it.

  • Python
  • NumPy
  • SciPy
  • NetworkX
  • Signed distance fields
  • Dual contouring
  • glTF 2.0
triangles for a 2 km region at 2 m voxels, in ~6 min on 16 cores
12.4 M
semantic materials, each triangle tagged with a feature id
14
infrastructure systems routed through 3D rock
5
triangles in the underground scene (1.25 m voxels, ~25 min)
38 M
Generated 2 km surface region, seed 7: eroded sand terrain with a stepped bowl, domes, spires, a wall and a colossal lattice tower

What it is

mapgen builds the level geometry for a world that giants built and then abandoned: kilometres of eroded terrain, several eras of megastructures built through each other, ruination and deep-time weathering, and beneath all of it a dense artificial underground of freight tunnels, coolant lines, reactor halls and grid-like “backrooms”. The output has no decoration. It is bare, closed geometry that a game engine can light and dress, with a manifest that says what every surface is and why it exists.

The game camera, line-of-sight and cutaway work built on top of this generator, together with the newer city, sewers and underworld, is written up in Seeing Through Stone.

One representation for everything

The whole world is one ordered CSG program over signed distance fields. Every op is a union, subtract, smooth union or intersect of an SDF node, with a semantic material and a feature id. Order is history. Terrain comes first, then each construction era, then collapse and weathering, then catastrophes, then the underground’s own eras. Later work cuts through and builds over earlier work, which is what this world needs.

I chose fields over explicit meshes because the hard part here is intersections. Tunnels meet halls, eras cut through each other, and erosion and collapse eat into all of it. In a field those joins come for free and don’t break. With meshes, every case needs its own boolean welding.

Stage How
Evaluation Region split into chunks; pure air or pure rock skipped; the rest evaluated in forked workers. Fields are computed analytically and never stored.
Culling Every node has a conservative bounding box; ops are pre-filtered by a vectorised index, then by shape-aware tests (per segment for sweeps, annulus for rings). Culling never changes the field’s sign near a surface, and a test checks it. It halved the evaluated chunks of the underground scene.
Meshing Surface-nets topology with dual-contouring vertices: each vertex solves a small least-squares (QEF) problem over exact field normals, so walls are flat and corners sharp.
Seams Chunks own disjoint edge sets and compute positions from global sample indices, integer-first, so neighbouring chunks produce bit-identical seam vertices. The mesh is crack-free, and a test checks it.
Export Tiled glTF 2.0 (+Y up, metres), one primitive per material, a _FEATURE_ID vertex attribute, LODs by re-meshing at 2× voxel size.

The underground is planned, not carved

The underground district isn’t a cave pass. Records are written first and geometry second:

  • Five infrastructure systems. Freight, coolant, power, ventilation and maintenance each have a cross-section, a preferred depth band, a slope limit, redundancy, bundling and clearance rules. A NetworkX demand graph decides what connects to what: freight trunks through exchanges, coolant draining to the reservoir, power trees from containment reactors, ventilation up to daylight shafts.
  • 3D routing. Each route is solved with SciPy’s Dijkstra on an anisotropic 24 × 24 × 12 m lattice. The cost combines rock hardness, depth preference, distance from reserved volumes, attraction to the same system and repulsion from others. Maintenance galleries are drawn towards what they serve.
  • Rooms by grammar. Complexes are allocated top-down by a split grammar: spine, exchange, halls, service blocks, corridors, rooms, stair shafts. This happens before any solid exists. A failed allocation backtracks through alternative layouts and then drops optional demands, recording the reason.
  • History. A later era installs a machine envelope where older infrastructure is densest. It backfills the volume, marks the crossed routes as sealed, and re-routes them as bypasses. Another era drives a transit line straight through older complexes.
  • Backrooms. Cell fields are one SDF domain repetition each. About 4 % of cells break the rule in a deterministic, recorded way: a missing door, a double-height room, a shaft through every level.

All of it is written to underground_plan.json, including every route’s system, era, purpose, origin, destination, status and path. The _FEATURE_ID on each triangle points back into that record.

Output and scale

Run (16-core machine) Triangles LOD0 / LOD1 GLB LOD0 Time
quick, 1 km, 3 m voxels 2.6 M / 0.56 M ~75 MB ~1.5 min
default, 2 km, 2 m 12.4 M / 2.9 M 440 MB ~6 min
underground, 2 km, 1.25 m 38 M / 9.2 M 1.2 GB ~25 min

A sampled field can’t hold anything thinner than about 2.5 voxels, so a detail module raises small features to that limit or leaves them out, and keeps noise wavelengths above it. That is why doors are 4 m: they are built for giants, and they stay clean at 1.25 m voxels. For an engine, the LOD files or a decimation pass are the starting point. Adaptive octree meshing and finer detail zones for human-scale interiors are the next steps.

The tests cover the invariants rather than pictures: noise determinism, exact SDF primitives, culling safety, watertight crack-free seams, outward winding, identical geometry digest for the same seed, valid GLB structure, manifest references, open tunnels, underground record consistency, split-grammar backtracking, and deterministic cell-field violations.