The land is a part: one large placed part that never moves, which can be asked questions about itself in its own frame. "How high is the ground here?" is a method on that part, land.height(spot). The features that shape the land, the walkway pressed into it and the brook carved through it, are parts the terrain owns, applied by the terrain's own algorithm in a fixed order, and still separate, named things that a material, a scatter or a sound can refer to. Whether there is ground everywhere is the designer's choice: a bounded land says what lies past its edge. This page sets out that design, and how it is written.
There is no separate kind of world field, something present everywhere, asked by position. Such a kind fails in outer space, where there is no ground under every point and no single "up": there ground belongs to a body, such as a planet, which has a place and may orbit. So the land is a part like a planet, and what looked special about it is two general mechanisms: questions a part answers (methods), and features a part owns.
Words used on this page:
- Height grid
- The land's height stored at points 2 m apart; the example's is 1025 by 1025 points over 2048 m.
- Signed distance
- How far a point is from an edge, negative inside, positive outside.
- Erosion
- Wearing the land as water and weather would, by simulation or by a filter on single points.
- Level of detail
- Drawing far ground with fewer points than near ground.
How you write it
field cottageSpot: place = (-16, 0, 330) // a root field: the land's pad and the cottage both read it
part land, at: (0, 0, 0), material: shaded(terrain), edge: sea(wade: 0.6 m), // placed, and never moves; it draws its own ground
fineOver: outline.points(points: [(-640, -696), (784, -696), (784, 704), (-640, 704)]) // drawn finest here
part walkway: spine.through(points: [(-22, 0, 498), (-22, 0, 476), ..., (-16, 0, 333.6)], kind: uniform, ends: doubled) // nine points
field halfWidth: meters = 0.9 m
part brook: spine.through(points: [(34, 0, 128), (22, 0, 150), ..., (-54, 0, 499)], kind: uniform, ends: doubled) // 26 points, spring to sea
field table : curve = curve([(0, 30.3 m), (1, 19.1 m), ..., (25, 0.15 m)]) // the water's surface at each point
field halfBed : curve = curve([(0, 1.5 m), ..., (8, 2.2 m), ..., (25, 3.0 m)])
field bank : curve = curve([(0, 0.8), ..., (8, 0.45), ..., (25, 0.3)]) // rise over run
field depth : curve = curve([(0, 0.5 m), ..., (8, 0.22 m), ..., (25, 0.1 m)]) // the bed below the water
field rounding: curve = curve([(0, 1 m), ..., (8, 0.6 m), ..., (25, 1 m)])
part water: brookRibbon(width: ..., speed: ...)
field dressedArea: outline = outline.points(points: [(-190, 285), (175, 285), ..., (-215, 420)]) // on the plan: (x, z)
field ground: heightGrid = { // the terrain's own order: a grid of heights, built once
let grid = heightGrid(cell: 2 m, points: 1025, height: baseHeight);
let pressed = press(grid, walkway, depth: 5 cm, fullWithin: 1.4 m, goneBy: 6 m, smoothedAlong: 9, passes: 2,
endHeight: 4.6 m, endClear: 5 m, endFull: 14 m); // it ends level with the pad
carve(pressed, brook, bed: brook.level - brook.depth, flat: brook.halfBed, banks: brook.bank, rounding: brook.rounding)
}
fn height(here: point) meters { ground.heightAt(here) } // reads here.x and here.z
fn gradient(here: point) direction { ground.gradientAt(here) } // a vector on the plan, rise over run
fn slope(here: point) number { length(gradient(here)) }
fn normal(here: point) direction { ground.normalAt(here) }
fn curvature(here: point, over: meters) number { ground.curvatureAt(here, over) }
fn forestDensity(here: point) fraction { ... } // how thickly forest grows: 0 none, 1 thickest
fn groveDensity(here: point) fraction { ... } // conifer groves on the far slopes
fn pathDistance(here: point) meters { walkway.nearest(here, within: 16 m) ... } // from the walkway's middle
fn brookDistance(here: point) meters { brook.nearest(here, within: 8 m) ... } // from the brook's middle
fn dressed(here: point) bool { dressedArea.contains(here) } // where the vegetation is laid out
field amphoraSpot: place = (-13.4, 0, 337) // a place on the land's plan
part amphora, at: (amphoraSpot.x, land.height(amphoraSpot), amphoraSpot.z) // asks the land how high the ground is
part cottage, at: (cottageSpot.x, land.height(cottageSpot), cottageSpot.z) // stands on the land the pad was cut in for it
part pathLanterns
part lantern0: pathLantern,
at: land.walkway.spotAt(land.walkway.fromEnd(1.8 m), across: -1.5 m) // already on the ground
A place on the land is a point. There are no two-number spots: a position is always (x, y, z) (Values: positions). The land's questions take a point and read only its x and z, so land.height(p) is the height of the ground under or over p, whatever p.y is. The walkway and the brook are spines whose points are written on the land's plan, at height 0; the land lays them on its surface, so the spot a feature gives (spotAt, Appendix: shape tools) is already on the ground and needs no height asked. A flat area of the land, such as the dressed area, is an outline whose points are (x, z); it answers contains(p) and is never a height. A slope is a plain number, rise over run (0.5 is a rise of 1 m in every 2 m).
The land draws its own ground. The height grid is the land part's own shape, so material: on the part line applies to it as to any drawn part (Materials); there is no separate ground part. shaded(terrain) is the browser's ground shader.
A fact both the land and another part need. The cottage stands on the land, and the land's pad is flat for the cottage, so the pad reads the cottage's spot and the cottage reads the land's height there: if each read the other, neither could be built first. The pattern is a field of the root that both read, cottageSpot, a place on the plan: the land's pad is cut round it (baseHeight, below) and the cottage's height is asked of the land at it. The spot is a fact stated once. Whether a part's ground position and its height can be kept apart in general is open.
The steps and numbers are an example's. heightGrid, press, carve and brookRibbon are illustrations of steps a land might take: no package offers them in this form. dress is not one of them: the dressed area changes no height. It is a field of the land, an outline, that rules ask whether a place lies in. baseHeight is the land's formula of position (below and on Open functions).
The base height: functions of position
field padMiddle: place = cottageSpot + (12 m, 0 m, 8 m) // the cottage's pad: 12 m east, 8 m south of its front
field padHeight: meters = 4.6 m
fn baseHeight(here: point) meters {
let s = 0 m - coast(here); // meters in from the coast; negative at sea
var h = shelf(s); // sea floor, shelf, beach, lowland
if s > 0 m {
h += gullies(here, hill(here), gradient(hill, at: here, step: 2 m)) * smoothstep(s, from: 60 m, to: 220 m);
h += roll(here) * (0.8 m + 7.5 m * smoothstep(planDistance(here, padMiddle), from: 46 m, to: 170 m))
* smoothstep(s, from: 0 m, to: 40 m); // the rolling fades in from the shore
}
lerp(h, padHeight, t: 1 - smoothstep(planDistance(here, padMiddle), from: 46 m, to: 100 m)) // the pad, flat
}
fn hill(here: point) meters { rawHill(here) * 150 m / highest(rawHill, every: 32 m) } // its top at 150 m
The land's height is a sum of three shapes: the shelf, set by how far a point is from the coast (a sea floor shelving to 30 m deep, a beach, a lowland rising to 7 m by 90 m inland); a hill 150 m high, 540 m in radius at its foot, cut by gullies, fading in between 60 and 220 m inland; and a gentle rolling of the lowland, from 0.8 m near the cottage to 8.3 m far off, fading in over the first 40 m from the shore. Round the cottage's pad the land is flat at 4.6 m out to 46 m, blending back by 100 m; the blend is the last step. All distances to the pad are planDistance, on x and z only. The hill's top is found before any point of it is worked out, a question about the whole field: highest(rawHill, every: 32 m) is the largest value of rawHill over a lattice of places 32 m apart, from the grid's corner (-1024 m, -1024 m) to the other. The lattice is part of the question, because a different spacing finds a different top and so makes a different island. The coast and the gullies are on Open functions.
What it means
Height is a method of the land
The land part answers four questions about the ground, in its own frame, each reading the point's x and z:
| Call | Answer | How it is worked out |
|---|---|---|
land.height(p) | meters | the ground's height. Between the grid's samples it is exact against the drawn triangles: each square of four samples is split into two triangles by one rule, the diagonal alternating from square to square, and the height is that of the triangle the point is in. Past the grid it is -30 m |
land.gradient(p) | a vector on the plan, direction (x, 0, z), rise over run | uphill, from the height 1 m either side of the point on x and on z, the difference divided by 2 m. land.slope(p) is its length, a plain number |
land.normal(p) | direction, length 1 | the way the ground faces: up, tilted against the gradient taken over a wider reach, the height 4 m either side (two grid cells), so the grid's own corners do not show in the lighting |
land.curvature(p, over: d) | a plain number per meter | how the ground hollows: the heights at the four places d away on x and z, added, less four times the height at p, divided by d squared. Positive in a hollow, negative on a rib. The island's ground look asks it over 9 m |
A placed part's height reads height; a scatter's rule reads it and the slope; the groundcover's maps, the sea's depth tint and the minimap read it whole. Slope follows from height, so the land can answer it without anyone writing it. A land's methods are its contract: two lands made differently are interchangeable to every consumer that asks the same questions. Most of the land's questions are asked by engines, not by lines of content: walkers stand on it, the navigation mesh is built from it, the birds steer round the hill by asking it. So the contract must be exact enough for engines the world never mentions.
What you stand on is what you see, to within 1 mm. This is the engine's guarantee, not something content asks for: the ground a walker stands on and the ground drawn are one, because the height is answered from the very triangles the picture is drawn with. It holds wherever the ground is drawn at its finest, which is fineOver (below). The island's finest ground covers every place a walker can reach, and the check that measures it (4000 places, each against the drawn triangle's plane) holds the largest gap under 1 mm. A land is also solid on the ground layer with no call at all: a walker, a prop and a thrown ball stand on it (Physics).
Features the terrain owns
The walkway and the brook are spines the land holds (the brook's 26 points). The land's own algorithm applies them in a fixed order: the grid, the walkway pressed in, the brook carved. And other parts ask them by name: each answers how far a point is from it (nearest(p, within: d), Shape tools: what a spine answers). So one owner holds the height and the order, and the features are still separate, named things. This removes the ordering problem that separate parts changing one land would have: pressing the walkway after carving the brook would fill the brook where they meet, and when two parts change one thing, their order is meaning.
- The walkway is a smooth curve through nine points from the landing beach to the foot of the cottage's steps, 1.8 m wide. Its own height is the land's along it, smoothed (a box of nine of its points, about 9 m, twice), the last point set to the pad's height so the walkway meets the pad with no step, and the ground near it eased to 5 cm below that, fully within 1.4 m of its middle and fading by 6 m, so it lies in a shallow groove; the pressing grows from none within 5 m of its last point, where it meets the pad, to the full 5 cm at 14 m. It answers its distance to whatever
within:asks (nearest(p, within: d)); the land's questionspathDistanceask 70 m and 60 m of it in the scatter's rules. The island's land keeps the distances in a grid that stops at 16 m, so a farther place is answered as 16 m, and a test that the walkway is nearer than 60 m or 70 m is always true there. - The brook is about 434 m long, from a spring 30 m up the hill to the sea. Its water level is the designed table held to two rules: never less than 0.3 m below the land around it, and never rising downstream; in the yard it falls at least 3 mm a meter so it flows. The land is lowered to a trench with a flat bed and banks. The water itself is a part of the brook, a see-through ribbon flowing at 0.15 m/s plus 4.5 m/s times the square root of the fall per meter, never more than 3 m/s. It answers distance up to 8 m. It also answers
waterLevelAt(along), the water's surface that far along the course, andwaterSpeedAt(along), the water's speed there: the fall over the three points each way along the course (about 3 m each way, held at the ends) divided by their distance apart, put through the speed rule above. A sound reads these (Sounds). It answers three more questions about the course:bedWidthAt(along), the width of the wet bed there (0.7 m to 3 m);bankSides(along), the two banks there, one point on each side of the bed, the left one first looking downstream; andonBridge(p), true when the placeplies within 4 m of the bridge's middle. A stone laid in the bed is stood by the water: its top rises 5 cm overwaterLevelAt(along), so its foot is at the water plus 0.05 m less (its mesh height less its 0.2 m sink) times its size; the squat rock's mesh height is 0.467 m at size 1. - The dressed area round the cottage, where vegetation is laid out, is an outline of eleven corners on the plan, a field of the land that answers
contains(p). It changes no height, so it is not a step of the land's order, and it is not a boundary: a walker may go anywhere. - Densities are methods too: how thickly forest grows (
forestDensity), and conifer groves on the far slopes (groveDensity). The author paints where the forest is thick in one place, and a scatter turns it into trees (Generators).
The two steps, exactly. press and carve are illustrations of how a land might apply a feature (no package offers them in this form), but the numbers are the island's, and each step is worked out as follows, with a spine's points about a meter apart.
| Call | What it does |
|---|---|
press(grid, spine, depth:, fullWithin:, goneBy:, smoothedAlong:, passes:, endHeight:, endClear:, endFull:) | First the spine's own height line: the grid's height at each of spine.points(), smoothed by a box of smoothedAlong of its points (9) averaged passes times (twice), the box shorter at the two ends; then the last point is set to endHeight (4.6 m, the pad's). Then each grid point within goneBy (6 m) of the spine is eased toward the line's height at the nearest point of the spine, less the pressing, by a weight that is 1 within fullWithin (1.4 m) and falls to 0 at goneBy as a smoothstep. The pressing is depth (5 cm) times a smoothstep of the distance from the spine's last point, none within endClear (5 m) and the whole of it beyond endFull (14 m) |
carve(grid, spine, bed:, flat:, banks:, rounding:) | For each grid point near the course, with its nearest point on it: the trench is flat at bed (the water's level there less how far the bed lies below it) for flat either side of the middle, and beyond that rises at banks (rise over run). Where the trench is lower than the ground plus rounding, the grid takes smoothMin(ground, trench, width: rounding), so the shoulders are round. A table with a value for each control point (flat, banks, rounding, the bed's depth below the water) is a curve keyed by the control point's number (the brook's 0 to 25), read at spine.indexAt(along) and so blended in a straight line from one control point to the next |
Edges: the designer's choice
Whether there is ground everywhere is the world designer's choice. An endless landscape answers everywhere and says so in its type. A bounded one must say what happens past its edge. The edge is an option of the land part, edge:, and takes one of these:
| Edge | What happens at it |
|---|---|
sea(wade: 0.6 m) | the land shelves under a sea that runs out to the horizon. A walker may not wade into ground more than wade under the sea, and a walker who meets deeper water slides along the shore instead (see below). The island's edge |
wall() | a walker is stopped at the border of the grid and may slide along it |
wrap() | walking off one side of the grid comes back from the opposite side |
fall(to: place) | past the border a walker falls, to the place named (the clouds below a floating island) |
Only sea is built; wall, wrap and fall are the other edges the vocabulary would name (an illustration), and others, dragons at the horizon, are a world's own. The edge is a feature of the land like the brook. The sea's edge, as the island does it. The step a walker means to take is allowed when the ground it ends on is no deeper than wade under the sea. If it is deeper, the part of the step that goes seaward (the way down the ground's slope there, the gradient pointing away) is taken off, so the walker slides along the shore; if that still ends in deep water, it moves on only one of x and z if one is clear, and otherwise stays where it stands. A walker already in deeper water than wade (it fell in, or was set down in the sea) may go to ground no deeper than its own, so it can walk out. Past the grid the land answers -30 m, the depth of its sea floor, so the deep water also stops a walker at the grid's edge.
The terrain's frame
The land is a placed part, with the placed kind of frame: position, rotation and size from its parent (Frames). Its questions are asked in its frame, so a land on a moving body (a floating island, a planet) answers the same way. Physics and the network take placed frames; the land is given to physics whole, as a heightfield (Physics).
The grid is centered on the land part's origin. The example's 1025 points 2 m apart reach 1024 m each way from it, on x and on z, so the grid covers 2048 m by 2048 m with its middle at the part's place. The part's at: moves the whole grid, and so the island, with it.
Build, order and cost
- The land is built first: a lantern's place reads its height, and the scatter reads its height, slope and densities. The order follows what reads what, not the tree (Loading).
- A loop of subscriptions: the brook's level reads the land, and the land's last step carves by the brook's level. Only build order breaks it: the brook reads the land "as the walkway left it". A rule that "the land" means the land before this feature touched it, or an explicit stage, is needed.
- The land may be sent as its formula (small, slow to compute: about 0.6 s in the example) or as its grid (4.2 MB, instant). Content states the formula; a browser keeping a baked copy is a resource choice, provided the two agree.
- Resolution shows through: the trench is about 4.4 m wide because a 2 m grid cannot hold a channel 1 m wide. By the rule that content states requirements, the trench should be stated at its true size and the engine left to represent it.
- Streamed by region: terrain, ocean, forests are sent in pieces; which piece and when is execution. Content may give hints such as piece size or how far away it matters.
- Where the ground is drawn finest: the land option
fineOver: outlineis such a hint. Inside the outline (on the plan, points (x, z)) the ground is drawn at the grid's own cell, one vertex for every sample; outside it, one for every fourth sample, with the fine ground's edge drawn along the coarse ground's line so nothing cracks. The island'sfineOveris a rectangle from (-640, -696) to (784, 704), the whole of its land, whose edges fall on whole blocks of four cells (8 m), so it covers every place a walker can reach and the guarantee above holds wherever a walker can stand.fineOversays where detail is wanted, not how it is made.
The ground's look
The ground is colored by its height, its slope, its density fields and its distances to the walkway and the brook (sand low by the sea, rock where it is steep, packed earth by the walkway, damp ground by the brook). It is the land's own look (material: on the land part, above). Its cover of grass and flowers is the groundcover package's, from eight maps of the land (Generators).
The packages
The packages are what a world would name.
terraingen: landscapes from parameters
It describes a landscape as data, a TerrainSpec, then asks for it, in four steps: generate, erode, drainage with rivers and lakes, masks and biomes.
- The shape record (
ShapeParams): a seed; up to four noise layers, each fBm, ridged or billow, with frequency, octaves, amplitude, a seed offset and gating by another layer's value through a soft window; a domain warp; base height, scale and power; terraces; a plateau; an island falloff; contrast; a detail octave. The order is warp, sum the layers, normalize, shape, scale.heightAt(p, x, z)is a pure function of the parameters and the position, within 3 mm of the main processor's on the graphics card. - Erosion by simulation over the grid: hydraulic by the virtual pipe model and thermal talus erosion, on the main processor or the graphics card, which agree (
erodeCpu,erodeGpu). - Rivers and lakes: depressions filled, flow directions and drainage computed, channels carved where the drainage area is large enough, lakes given a flat surface (
analyze,carveRivers). - Masks and biomes: slope, moisture, exposure, flow, river, lake and water distance; biomes as soft windows on slope, height and moisture;
groundSignalsfills the groundcover's eight maps. - Presets:
alpine,mesa,hills, each taking a seed;buildLandscape(spec). - Not here: wind or glacial erosion, rivers carved by splines (roads, canals), tiled or infinite worlds; a sea level only lifts a flat water plane. So a walkway and a brook, features carved along a designed curve, have no terraingen form; and a per-point gullies filter (Open functions) is a different method from terraingen's simulation. A landscape is built once.
terrainlod: ground that refines with distance
Ground that refines itself with distance, over any height function or heightmap tiles: a fine grid near the camera, half as fine at each doubling of distance, blended so nothing pops and stitched so nothing cracks. It takes the height as a function, source: &<fn(x: f32, z: f32) f32>, passed to update and to the queries (groundHeight, raycast), so the picture and the physics ask one function. That is the land-as-method design in a package: the land is a function, and drawing it at any distance is the engine's. Not built: shadows cast by terrain; beyond about 10 km the ground jitters.
oceanwaves and the sea
An FFT ocean, calm to storm: a sea state from wind speed, direction and fetch, or a Beaufort force (SeaState.make, SeaState.beaufort), with a seed (OceanSpec.forSea(sea, seed)); it answers heightAt, normalAt, foamAt and spray spots for effects. The sea is a part of its own, a surface at height 0, reading the land's depth.
How it connects
- Open functions: the coast and the gullies.
- Generators: the scatter and the groundcover read the land.
- Sky and weather: the other "everywhere" things, placed by direction or round the camera.
- Sounds: the brook's sound, placed on its course.
- Physics: the land as a heightfield; the wading rule.
- Frames: the placed kind.
- The land, written out.
Open questions
- Terrain built in stages that read what came before: the brook reads the pressed ground and then carves it, and its water level is a running scan down the course. A design question.
- A field that reads a part: the pad depends on the cottage's place and the cottage stands on the land; a root field both read (
cottageSpot) answers this one case, but keeping a part's ground position and its height apart in general is open. - Changing the land while running: nothing digs or floods today; whether the land may have state.
- A ground look from where you are: the ground's color is chosen from its height, its facing, its masks, the distance to the eye, the size of a pixel on it and the time, not from texture coordinates, so neither a texture nor
paintedcan state it. A design question. - A land's type: the questions above (
forestDensity,groveDensity,pathDistance,brookDistance,dressed) are the island's own; which of them every land must answer, and which are a world's additions, is open. - Edges: which edges the vocabulary has beyond
sea(wall, wrap and fall are illustrations), and what each answers past the grid. - Features in packages: spline-carved features (paths, rivers, roads) are not in terraingen; a feature vocabulary the terrain owns does not exist yet.
- Per-point filter or simulation: the gullies filter and terraingen's erosion are two methods; does content name the method or the result?