euca-terrain) is a chunked heightmap: a grid of height samples you create flat and
then sculpt with brushes — raise, lower, flatten, smooth — each applied at a world position with a
radius and amount. Foliage scatters instanced meshes across an area in one layer. The terrain data
(the heightmap component and its edits) is fully headless and works on the bare server; the mesh —
and the foliage — need a render host’s mesh assets. This page builds and sculpts a terrain end to end;
every command is real and runnable against a local server, with output captured from one.
Sculpt a heightmap
1. Create a flat terrain. You give it a grid size and cell size; it spawns a terrain entity and reports the resulting world extents. Note the extents are(cells − 1) × cell_size — a 64-cell grid
spans 63 world units:
/terrain/edit applies one of four brush ops at (x, z) with a radius and
amount. The edit lands immediately on the heightmap of the first terrain entity — no /step:
lower digs, flatten pulls toward a target level, smooth
averages neighbours — all the same request shape:
Scatter foliage
/foliage/scatter places an instanced layer of a named mesh across an area. The scatter is
deterministic (fixed seed) — but it needs the host’s DefaultAssets to resolve the mesh and
material. The minimal server doesn’t register them, so on it the call honestly reports the missing
resource and the layer list stays empty:
DefaultAssets registered, the same scatter returns
{"ok":true,"instance_count":N,"mesh":"cube","density":0.5} and /foliage/list reports each layer’s
index, instance count, density, and max render distance.
Behavior and gotchas
The things you only find out by running it:/terrain/createstores a heightmap component, not a rendered mesh. It builds a CPUHeightmap::flatand spawns it; building the displayable mesh (CPU or GPU) is the render host’s job. Creating terrain bumpsentity_countonGET /; reading the rendered surface needs a host.- Edits are immediate — no
/step. Unlike combat events, a brush op mutates the heightmap on the call. Read-back is via the host (the heightmap isn’t exposed as JSON), but the op itself succeeds synchronously. /terrain/editacts on the first terrain entity. It queries for anyTerrainComponentand edits the first one found. With no terrain, or an unknown op, it returns{"ok":false,"message":"No terrain entity found or invalid operation"}.- World extents are
(cells − 1) × cell_size. Awidth:64, cell_size:1.0terrain hasworld_width:63.0, not 64 — the grid has 64 vertices spanning 63 cells. - Brush coordinates are world-space
(x, z), not grid indices;radiusandamountshape the falloff and strength. The four ops dispatch identically — only the math on the heightmap differs. - Foliage scatter needs host mesh assets. Without
DefaultAssetsit can’t resolvemesh_name({"ok":false,"error":"DefaultAssets resource not found"}); an unknown mesh name on a host returns{"ok":false,"error":"Unknown mesh name"}.
Endpoints
Status
- Shipped & headless — the CPU heightmap, the four brush ops, the terrain entity, and deterministic foliage scatter logic. Terrain create and edit work fully on the bare server.
- Host-dependent — the terrain mesh and foliage placement need render-side assets:
/foliage/scatterrequires a registeredDefaultAssets; displaying terrain or foliage needs a render host. - GPU terrain generation — a real compute-shader mesh generator (
GpuTerrainGenerator) ships behind thegpu-terrainfeature flag and needs a liveRenderDevice. It is not the path the HTTP endpoint uses; the endpoint builds the heightmap on the CPU.
Terrain & foliage endpoints
Create, edit, scatter, and list endpoints, with request and response schemas.