Files
2026-09-25 17:02:24 +03:00

938 lines
59 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Painted templates
A template is a picture of a world and a legend saying what its colours mean. The generator turns the two
into terrain: `Tools/Terrain/bin/terrain.exe plan` reads them and says what a bake would involve, and
`terrain bake` solves it. The contract between them is [`../Planet.json`](../Planet.json).
`World.json` next to it is a different world — the square 14.28 km canvas the numpy pipeline and
`create_world.py` still build. The two share nothing but their defaults, and nothing here touches that path.
```bash
# a minute: read the painting, cut the planet into regions, solve nothing
Tools/Terrain/bin/terrain.exe plan
# an hour or two: solve every region and composite the world. Run it detached.
# The output goes into the next free RawContent/World/Bake_NNN, so a re-bake never
# destroys the one before it - the interesting question is almost always "what did
# that change", and answering it needs both.
Tools/Terrain/bin/terrain.exe bake
# one landmass, short, for tuning the legend
Tools/Terrain/bin/terrain.exe bake --only 11 --steps 200 --out /tmp/try
# the same painting as a different world: massifs, rock, faults and coastline detail all re-rolled
Tools/Terrain/bin/terrain.exe plan --seed 9342
# the upland fabric at another size, without editing the manifest
Tools/Terrain/bin/terrain.exe plan --massif-km 5
# the painting tool: brushes that carry the legend's numbers, and Plan as a button
Tools/Terrain/bin/terrain.exe studio # http://127.0.0.1:8099
```
## The studio
`terrain studio` is the tool to reach for first. It exists because the two halves of a painted world used to
live in different programs: the shapes in an image editor that knows nothing about uplift rates, the meanings
in this legend, which cannot show you where they land.
The brushes **are** the classes. Picking `highland` is picking 0.25 mm/yr, and the panel tells you that is
11.7° of ground and reads as hill country while you are painting it — with the 32° divide angle beside it in
grey, because that is where the number comes from and what the repose clamp eventually binds against. So the
table in the section above stops being something to look up. Change a number and every swatch re-reads
instantly; nothing is written until you press a Save button.
| | |
| --- | --- |
| paint | left-drag. `[` and `]` change the brush size |
| pan | shift-drag, middle-drag or right-drag |
| zoom | wheel |
| Plan | runs the plan **against what is on screen**, not what is on disk, and prints the class table, the seam check and the region cuts |
| the map buttons | draw `class`, `uplift`, `regions`, `erodibility` or `overlay` over the canvas, each with a key under it. Press again, or Escape, to go back to painting |
| layers | the two tabs at the top, or press **o**. `classes` is the geology; `overlay` is the annotation layer, where the brushes are its marks and the last one is an eraser. See **The overlay** |
**Plan is seven seconds the first time and about a third of a second after that**, as long as all you changed
was a number. That is the tuning loop, and it is worth knowing why it works: the classification, the despeckle,
the coast mask, the projection and the region cuts depend on the painting and on the class *colours* only — an
uplift rate decides how the maps are coloured in and nothing else — so a plan that differs from the last one
only in the legend's numbers reuses the whole of it. Touch the painting, or any of the coast or margin
settings, and the next plan is a full one again. The report says which you got.
Rendering the maps smaller does **not** make it quicker, which is worth stating because it is the obvious
guess. Measured on the 100 km template, prepare is flat at about 6.5 s from a 400 px map to a 2400 px one, and
it breaks down as classify 0.09 s, dissolve strokes 0.93, despeckle 1.52, the coast mask 1.62, project 0.05
and `region.Build` **2.68** — and the last one works on the 76-million-cell planet grid, which the size of the
painting and the size of the maps both have nothing to do with.
Three things about it are deliberate:
- **The brush is hard-edged and writes exact colours.** No antialiasing and no soft edges, because a blended
pixel is not a colour between two classes — it is a pixel that classifies as whichever *third* class happens
to sit near the midpoint. See the note on the coastline below; that defect is real and this is where not to
create it.
- **The canvas wraps.** Paint off the left edge and it arrives at the right, because the map is a cylinder.
The first template disagrees with itself on 9.4 % of its rows and this is how that gets fixed.
- **There are two sheets, and only one of them is geology.** The class painting is what the solve reads; the
overlay rides on top of it and, `coast_jitter` aside, changes no height anywhere. It is dimmed while the
brush is on the classes so that a road can never be mistaken for something the solve will act on.
- **It never overwrites anything, and the base map least of all.** Saves go to the next free `Map3_001.png`,
`Map3_002.png`, ... beside the template, and the manifest is repointed at the new one — the same convention
as `Bake_001`, for the same reason: the interesting question is almost always "what did that change", and
answering it needs both. Rolling back is pointing `planet.template` at an earlier one. A painting is
something you made by hand and there is no undo for it outside the studio, and writing back over a *JPEG*
would additionally re-create the blended boundary pixels the despeckle pass exists to remove, compounding
them on every save.
Legend and manifest saves **patch the text** rather than rewriting the file, so the commentary in both
survives and a change still shows up as a small diff.
### Baking from the studio
The **Bake** panel runs the real solve on the painting as it is on screen, and you can watch it. Because the
geology is decomposed per landmass, each one comes out whole — so the preview redraws every time a region
lands and the world fills in a continent at a time. The first one out tells you whether the numbers are right;
the other seventeen do not have to finish for you to know.
| | |
| --- | --- |
| regions | blank bakes everything (~2 h on the 100 km template). A few ids — `11,13` — is the quick version, and the ids are the ones in the regions key |
| steps | 0 is the manifest's full count. A few hundred is under-eroded but enough to see the shape |
| jobs | how many landmasses solve at once. The wall time is set by the largest single region, which runs at about one core, so this helps least on exactly the template that needs it most |
| Cancel | stops it. Regions in flight stop at the end of their current step, so the wait is one step of the biggest one |
A finished bake goes to the next `Bake_NNN`. A **cancelled** one goes to `Partial_NNN` instead — the regions
that finished are complete and worth keeping, everything else in it is still at sea level, and the separate
name is so that nothing downstream (`tiles --bake` takes the newest `Bake_NNN`) can mistake one for the other.
## The picture
Flat cylindrical: **X wraps** — the left and right edges are the same meridian, so a landmass may straddle
them and comes out whole — and **Y does not**: the top and bottom rows are the poles. Any aspect works; the
paint is never stretched to 2:1.
**The painting has to wrap too, and this is the thing to check first.** The left and right edges are the same
meridian, so anything that does not continue from one to the other is a real discontinuity down one line of
the world — and it is the one defect you cannot see by looking at the picture, because the two edges are as
far apart on screen as they can be. `terrain plan` measures it:
```
wrap the left and right edges are the same meridian: they disagree on 355 of 3761 rows (9.4%),
261 of those land against water, and 48 px in the outermost columns match no class.
THAT IS A VISIBLE SEAM. The generator wraps; the painting has to as well.
```
That is `Map3.jpg` as it stands. The crater island crosses the seam perfectly — sampled at the geology grid,
the heights run continuously from the last column into the first — but the islets near the left edge were
drawn touching `x = 0` with nothing to meet them at `x = W-1`, so the world has a 400 m cliff down the seam
wherever that happens. Painting round the edge, or nudging those islets inwards, fixes it.
PNG is better than JPEG, and the seam is where it shows. JPEG bleeds colour across every class boundary, and
two legend colours closer together than the bleed will swap pixels where they meet — survivable in the middle
of the map, but on the outermost columns a lossy encoder leaves a halo, and a halo on the seam is a stripe of
the wrong class down the one line of the world that cannot hide it. `Map3.jpg`'s first two columns are
lighter than the ocean behind them, which the classifier reads as shelf: a 400 m ledge two pixels wide,
running the height of the map.
The paint does not have to match the grid. `Map3.jpg` is 7738 px across a 100 km world, so a pixel is 12.9 m
against an 8 m geology cell: the painting is slightly *coarser* than the simulation, which is the right way
round. The rule from `Docs/Terrain-Next.md` §3.2 is that the painted map owns the wavelengths above its pixel
size and noise owns those below.
## Paint the uplift, never the height
This is the one thing to understand before painting anything. A class does not say how high the ground is; it
says **how fast the rock is rising** and **how easily it erodes**. The simulation then produces the terrain.
Painting a heightmap instead does not work and the reason is worth knowing: a stream-power solve handed a
painted surface erodes it into something else within a few hundred steps, and what comes out has no
relationship to what was drawn — while the drainage network, which is the entire reason this generator exists,
is thrown away. Paint intent; get rivers.
Rivers cannot be painted either. A river is an *output* of the drainage solve. What does work is biasing:
raise `k_mult` along a line so the water finds the soft rock, and the solve chooses to put a river there for
its own reasons — and the result is still a coherent network.
## The legend
One JSON file beside the image. Keys beginning with an underscore are comments; any other unknown key is an
error, because a misspelt key silently ignored is a class quietly running on the default.
```jsonc
{
"image": "Map3.jpg",
"warn_distance": 60, // how far in RGB a pixel may sit from every class before the run says so
"classes": [
{ "name": "ocean", "rgb": [91, 175, 185], "sea": true, "depth_m": 512 },
{ "name": "lowland", "rgb": [153, 204, 102], "uplift_mm_yr": 0.08, "k_mult": 1.0 }
]
}
```
| key | meaning |
| --- | --- |
| `name` | what it is called in the reports and the maps |
| `rgb` | the painted colour. Every pixel takes the nearest one, so there is no unclassified pixel |
| `sea` | water. The solve holds every sea cell at base level for its whole run |
| `depth_m` | sea only: how deep the open water is, in metres below sea level, positive |
| `uplift_mm_yr` | land only: rock uplift, and **the hillslope angle**. See below: `tan(divide) = 2.5 × rate ÷ k_mult` at an 8 m cell, and the *ground* is a third of that in tangent — so 0.08 is 3.8° of median hillslope with 11.3° at its divides, and 0.25 is 11.7° with 32° |
| `k_mult` | land only: the multiplier on stream-power erodibility. Soft rock above 1, hard below |
| `stroke` | decoration rather than data — dissolved into whichever real class is nearest |
| `edge_class` | a stroke touching the top or bottom row of the map is not a stroke; it becomes this class |
| `derived` | never painted: no colour matching, exists only as something else's `edge_class`. Its `rgb`, if any, is for the diagnostic maps |
| `coastal_plain_km` | land only: how far inland the rate ramps up to its full value, so the range sits behind a plain |
| `coastal_floor_mm_yr` | land only: the rate at the waterline. Defaults to 0.02, never raised above the class rate |
| `massif` | land only: `{floor_mm_yr, fraction}` — the class breaks into plain and upland instead of holding one rate everywhere. **Read the next section before writing a legend without one** |
| `faults` | land only: `{per_1000km2, throw_m, length_km}` — traces placed in this class's ground. Absent means none, which is right for a plain. See **One painting, many worlds** |
| `lithology_mix` | land only: 0 to 1, how much of the planet's rock field shows through here. Default 1; 0 is uniform rock, which is what an ice cap or a crater floor should be |
| `crater` | land only: `{rim_m, floor_m, rim_at, wall_at}`, stamped on the finished terrain |
| `snow` | land only: permanently under ice. **Display and material only** — no height moves, no pass reads it, and it is drawn with the palette's `ice` colour. What makes a polar cap *terrain* is its uplift rate and its `detail` block like any other class |
| `detail` | land only: `{droplets_per_cell, strata_contrast, amplitude_m}`, what the detail passes do differently on this ground |
### A rate is an angle, and one rate is one landscape
This is the second thing to understand, and it is the one that caught the first painted planet out.
For `n = 1` the steady-state slope is `S = U / (K·A^m)`, and with `critical_area_m2` at 0 that law reaches down
to a single cell — so at a drainage divide `A` is one cell and the uplift rate, on its own, fixes the hillslope
angle. At the 8 m geology cell and `K` 5e-5 it works out at:
```
tan(angle) = 2.5 × uplift_mm_yr ÷ k_mult
```
**But a divide is the steepest place in a catchment, and almost none of a map is divide.** `A` is smallest at
the top of a hillslope and grows all the way down, so the divide angle is the *ceiling* on a class and not its
landscape. Measured on a 600² grid of 8 m cells at 1000 steps with the manifest's own constants, the median
slope comes out at a third of it in tangent, and the ratio barely moves over a factor of twenty in rate
(0.34, 0.33, 0.33, 0.32). So there are two columns, and **the one to steer by is the second**:
| `uplift_mm_yr` | divide | typical ground | reads as |
| --- | --- | --- | --- |
| 0.012 | 1.7° | 0.6° | plain |
| 0.02 | 2.9° | 0.9° | plain |
| 0.04 | 5.7° | 1.9° | plain |
| 0.08 | 11.3° | 3.8° | rolling |
| 0.10 | 14.0° | 4.7° | rolling |
| 0.25 | 32.0° | 11.7° | hill country |
| 0.28 | 35° | 12.9° | the angle of repose at a divide — above this the clamp shapes the ground, not erosion |
`terrain plan` prints both for every class in the legend, along with the word in the right-hand column, and so
does the studio panel as you type. Read it before starting a bake; it is four seconds against two hours.
**Reading the divide column as the landscape is how a legend ends up two or three times too hot everywhere**,
and it is the same mistake as the one in the paragraph below, one level up.
**Do not read `internal/stats`' "plain below 0.1 mm/yr" as a description of terrain.** It is a reporting bucket
calibrated for the old procedural path, and 0.1 mm/yr is a fourteen-degree hillslope: hill country wherever it
is painted. That sentence is how the first planet's `lowland` ended up ten times too hot.
### Flat ground, which needs two rates and not one
Because a class was one rate over every cell an author painted with it, a landmass painted one colour came out
**uniformly** dissected — from the waterline to the summit, at whatever angle the rate named, with no flat
ground anywhere on it. That is not what a continent looks like. Europe away from the Alps is a plain at a
fraction of a degree with isolated massifs standing out of it, and what separates the two is not the rate, it
is that the rate is not the same everywhere.
So a class carries a `massif` block: the class rate is re-read as the rate a *massif* reaches, `floor_mm_yr` is
the plain between them, and `fraction` is how much of the class stands above the midpoint of the two.
```jsonc
{ "name": "lowland", "rgb": [153,204,102], "uplift_mm_yr": 0.08,
"massif": { "floor_mm_yr": 0.012, "fraction": 0.16 } }
```
A sixth of it is rolling ground at 3.8° (11.3° at its divides); the rest is a 0.6° plain that tops out about
30 m above the sea over a whole continent. Half the fraction again — a twelfth — reaches 0.08 outright, and
half again above that is off the plain at all.
**So "the lowlands come out too hilly" is almost always one of two numbers, and neither of them is the floor.**
It is `fraction`, because the ramp opens at `1 - 1.5 × fraction` in probability, so 0.16 leaves about a
*quarter* of the class off the plain rather than a sixth; or it is `uplift_mm_yr`, which is what those raised
parts climb to. The floor itself has been measured baking out at 0.58° median with 4 % of it over three
degrees, which is a plain by any reading.
Three things worth knowing about it:
- **The floor wants to be about a tenth of the rate.** It is the number that decides whether the class has flat
ground at all. If a bake comes out too busy, though, drop `fraction` first: the floor is already a plain and
lowering it further only flattens ground that was flat.
- **`fraction` is a share of the planet's surface, so it is only the *expected* share of any one island.** A
small island may get all of a massif or none of it, exactly as it would if it were a real island that
happened to sit on or off an orogen. That variance is the point; normalising it per landmass would hand
every island its quota of hills, which is the thing this exists to stop.
- **There is one fabric for the whole planet**, `planet.massif_wavelength_km` in `Planet.json`, and every class
cuts the same one at a different level. So a highland belt and the hills in the lowland beside it come out as
the high and low parts of one structure — a foreland and its outliers — rather than as two unrelated noises
meeting at a painted edge. Set the wavelength well below the size of a landmass: at 12.5 km against islands
of 20–45 km one island came out entirely above the cut, which is the original defect over again, only
smaller. 7 km puts several blocks across every continent.
A class with no `massif` block is one rate all over, which is what every class was before this existed. That is
still the right answer for a small class, for a crater's flanks, and for anything an author would rather paint
by hand.
### Coastlines, which are not drawn lines
A shore drawn with a bezier tool is smooth, and a real coast is not: it has bays inside bays inside bays, and
how long it is depends on the ruler you measure it with. Projected straight, the painting's own smoothness
survives all the way to the heightmap and the result reads as what it is — a shape somebody drew.
So the waterline is roughened before the painting is projected. Fractal noise is added to the signed distance
from the shore and the sign is read again, so land juts out where the noise is positive and the sea reaches in
where it is negative. Nothing away from the shore moves.
```jsonc
// in Planet.json, not the legend: it is about the world, not about one colour
"coast_jitter_px": 48, // how far the shore may move, template px
"coast_jitter_wavelength_px": 384, // how wide the biggest bay is
"coast_jitter_octaves": 5,
"coast_jitter_gain": 0.55 // near 0.5: each scale as prominent as the last
```
At 12.92 m a pixel that is bays about 5 km wide and up to 620 m deep. **The ratio of the two is the knob that
matters** — amplitude far below wavelength gives a rough line, amplitude approaching half the wavelength gives
a fjord coast. Tune it in the studio, which prints both in kilometres as you type. `0` projects the painting
exactly as drawn.
Small islands are safe: the amplitude is capped per cell at two thirds of the widest land within reach, so an
islet gets a ragged edge instead of being swallowed by a wavelength ten times its size.
**And a coastline you drew on purpose is exempt.** The argument above is true of a shore nobody thought about
and false of one traced off a real map, so the amplitude is a *field* rather than a number: paint a
`coast_jitter` mark from the overlay over the stretch you want kept and it stays exactly where you put it
while the rest of the world is still roughened. Zero pins it; above one chews it harder, which is how a fjord
coast is made without turning the whole planet into one. See **The overlay** below.
### A note on JPEG, which is not a style preference
The README says export PNG. Here is what it costs not to. Classification gives every pixel its nearest class
in RGB, and a lossy codec blends across every boundary. On this template the blend of `surf` (221,238,238) and
`lowland` (153,204,102) comes out at about (186,219,174) — whose distance to `desert` (238,221,153) is **53.8**
against **77.9** to either of the colours it was actually mixed from. So the antialiased edge between the sea
and the shore classifies as *desert*, and the map gains a one-pixel ribbon of it along every temperate coast:
1607 pixels on this one.
One pixel wide that is invisible. It stopped being invisible the moment the coast mask arrived, because a
stray pixel is still **land** — so it became the nearest land to a stretch of open water, and every cell the
mask turned into shore inherited its class. An eleven-pixel band of desert appeared along a green continent.
A despeckle pass now removes any class that holds a one-pixel line with two others either side of it, which
costs 0.068 % of the map and takes that count to zero. It is a safety net and not a licence: paint in the
studio or export PNG, and none of this happens.
## The overlay
A second painting, the same size as the template and registered to it, for everything that is *placed on* the
finished world rather than solved: where the forest is, where the village is, what a road follows, and which
coastlines to leave alone.
It is a separate image rather than more colours on the template because the two answer different questions.
Every colour on the template is geology — an uplift rate the solve answers for — and there is no uplift rate
for a town. A mark has to be able to sit on top of any class without changing it.
```jsonc
// Planet.json
"overlay_legend": "Templates/Map3.overlay.json",
"overlay": "Templates/Map3.overlay_001.png", // written by the studio; leave it out to begin
```
```jsonc
// Map3.overlay.json
{
"image": "Map3.overlay.png",
"match_distance": 40, // an opaque pixel further than this from every mark is dropped
"min_area_px": 24, // below this a blob is a speck and is not reported
"marks": [
{ "name": "drawn_coast", "rgb": [255, 0, 255], "coast_jitter": 0 },
{ "name": "wild_coast", "rgb": [255, 128, 0], "coast_jitter": 2.5 },
{ "name": "forest", "rgb": [ 0, 128, 0] },
{ "name": "city", "rgb": [220, 30, 30], "min_area_px": 400 },
{ "name": "road", "rgb": [ 90, 60, 30], "kind": "path", "width_m": 8 }
]
}
```
**Nothing is a colour.** An unpainted pixel is transparent, not a background colour, so no colour has to be
spent on emptiness and an export with a white matte behind it does not turn the world into whatever mark white
is nearest. An opaque pixel that matches no mark inside `match_distance` is dropped and counted — `terrain
plan` says how many, which is the only way a colour the legend forgot ever shows.
### What the generator reads, which is one thing
`coast_jitter` and nothing else. It scales how far the waterline roughening may move the shore inside the
mark: `0` pins it exactly as painted, `2.5` chews it two and a half times as hard as the rest of the world.
Painting either side of the waterline is enough — the roughening already knows, for every cell it might move,
which cell on the far side it would take its class from — so a brush stroke drawn along the shore covers both
and nothing has to be traced precisely.
The check is exact: an overlay painted `coast_jitter: 0` over every pixel produces a class raster byte for
byte identical to a run with `coast_jitter_px` at zero.
### What travels, which is everything else
No pass reads a `forest` or a `city` or a `road`. Two bakes with and without them are the same terrain to the
bit. What those marks do is come out the other end in two shapes:
- **A mask per tile.** `Planet_x11_y07_overlay.png` beside every heightmap: 8-bit, one mark index a detail
cell, `0` for nothing. Marks cannot overlap — the overlay is one painting and a pixel is one colour — so
254 of them fit in the file one would have taken.
- **Features in world metres.** `overlay.json` beside `tiles.json`, and beside the maps of every plan and
bake. An **area** mark gives a centre, an area, a radius and an extent per connected blob; a **path** mark
is thinned to its centreline and gives an ordered polyline and a length, because the thing built from it on
the other side is a spline. Both are in metres east of the seam and metres south of the top painted row.
A blob that crosses the seam is one feature, not two, and its centre lands on the blob rather than on the far
side of the world. A road across the seam comes out as one polyline whose X runs past the circumference,
which a consumer can wrap back knowing how wide the world is.
One stroke is one path. A fork reports its two longest arms as a single line and drops the third, so paint
each run separately — `terrain plan` prints how many pieces each mark has, which is where a fork shows.
### Generating a first draft
The overlay starts blank, and the three things most worth putting on it - woodland, settlements and the roads
between them - all depend on ground you cannot see while painting: the classes are painted before the solve
exists, and once it does exist it is a seventy-six-million-pixel heightmap. So there is a command that reads a
finished bake and proposes them.
```bash
terrain overlay # the newest Bake_NNN, writes the next Map3_NNN.overlay.png
terrain overlay --no-save # say what it would place and write nothing
terrain overlay --bake RawContent/World/Bake_022
terrain overlay --replace # start from a blank sheet instead of filling in around what is painted
```
**In the studio it is a button.** Switch to the `overlay` tab and press **Generate marks**. That is the loop
this is really for: press it, look at where the towns landed, press it again, keep the third one. Two things
differ from the command, and both follow from a button being pressed repeatedly rather than once.
- **Every press is a new seed**, so it is a re-roll by construction. The painting fixes where the land is;
the seed decides everything it does not - which patch of eligible ground becomes woodland, and which of
several equally good sites gets the town.
- **Each press replaces the last draft** instead of piling on top of it. The studio remembers exactly which
pixels the previous generation put down and clears those, and only those, first. Anything you painted is
never in that set.
The sheet is replaced wholesale rather than stroked, so **ctrl+z will not walk back over a generation** and
the undo history is dropped when you press it. The status line says so. Nothing you painted is lost either
way, because generation only fills blanks.
**It works before the first bake**, on the painting alone, and says which of the two ran. That matters: with
no solve there are no rivers to sit on and no slope for a road to bend around, so a draft made that way is a
sketch that respects land, sea and classes and nothing else. Bake, then press again for one that reads the
terrain. A bake made from a *different painting* is ignored rather than used - two paintings of the same
planet encode their heightmaps identically, so nothing else would catch it and the marks would be placed
against terrain from another world.
**It never touches a pixel you painted.** The sheet on disk is loaded first and generation fills around it, so
running this against a half-painted overlay adds to it, and running it twice is safe. That is what makes the
round trip work in both directions: generate a draft, move the towns where you want them in the studio,
regenerate the roads around your edits. `--replace` is the explicit way to throw a generation away.
**It is opt-in per mark.** A mark generates only if it carries a `generate` block, so a legend written before
this existed produces the blank sheet it always did, and a mark you want to own completely simply says
nothing.
```jsonc
{ "name": "forest", "rgb": [0,128,0],
"generate": { "kind": "forest", "cover": 0.45, "wavelength_km": 6,
"max_slope_deg": 22, "not_classes": ["ice", "desert"] } },
{ "name": "city", "rgb": [220,30,30], "min_area_px": 400,
"generate": { "kind": "settlement", "count": 3, "min_spacing_km": 9, "not_classes": ["ice"] } },
{ "name": "road", "rgb": [90,60,30], "kind": "path", "width_m": 8,
"generate": { "kind": "road", "max_slope_deg": 20 } }
```
Four kinds:
- **`forest`** fills ground that is shallow enough and below the treeline, broken up by a noise field so it
reads as woodland rather than as a contour band. `cover` is the share of eligible ground it takes and
`wavelength_km` how big the patches are. The treeline defaults to a quantile of the land's own heights,
because a number in metres means nothing until the world is baked - the same legend has to put trees on a
47 m plain and on a 2800 m range.
- **`settlement`** scores the land on three things the terrain actually knows - drainage, flat ground and
distance to the sea - and takes the best sites with a minimum spacing. Several marks may use it and they
share one spacing rule, largest first, so a village never lands inside a city. Everything else about where
a town is - trade, history, who won a war - is yours, which is why these are proposals in a sheet you edit.
- **`road`** joins the settlements along least-cost paths, as a minimum spanning tree rather than every pair,
so there is exactly enough road to reach everywhere. **Water is impassable**, so each landmass gets its own
network and a bridge or a ferry stays a deliberate act.
- **`coast`** bands the waterline. **It is not enabled in the shipped legend on purpose**: `coast_jitter` is
the one overlay property a pass reads, so generating that mark changes the next bake's coastline
everywhere, and that is a decision to take deliberately rather than to inherit from a default.
**`only_classes` and `not_classes`** keep a mark off ground you painted a particular way. They matter more
than they sound: height and slope cannot tell an ice cap from a meadow, and the first run of this generator
grew woodland across both polar caps, which are flat, below the treeline, and no business of anybody's. A
class name that is not in the class legend is an error rather than an empty filter.
**Read the run's report.** It says what it kept, what it painted, and - the useful one - when it placed fewer
settlements than you asked for, which means the spacing or the flat ground ran out. On this template asking
for 26 villages at a 9 km spacing gets 17.
### Painting it
Press **o** in the studio, or use the `overlay` tab. The brushes become the legend's marks, the last one is an
eraser, and the sheet rides over the class painting — full strength while you are on it, dimmed while you are
painting classes, because a mark means nothing except against the coastline it was drawn along. **Save
overlay** writes the next numbered PNG and points the manifest at it, exactly as the class painting does.
A planet with `overlay_legend` set and no image yet is the normal way to start: the legend says what the marks
mean and the sheet is empty until the first save.
## One painting, many worlds
A painting is a **composition**: where the continents are, where the ranges run, which coast you drew on
purpose. It is not the whole world. Everything inside it that nobody drew comes from the seed, and changing
the seed re-rolls all of it while the painting stays exactly as painted.
```bash
Tools/Terrain/bin/terrain.exe plan --seed 9342 # four seconds; the maps say what moved
Tools/Terrain/bin/terrain.exe bake --seed 9342 --out RawContent/World/Bake
Tools/Terrain/bin/terrain.exe tiles --seed 9342 --bake RawContent/World/Bake
```
Or in the studio: the **seed** box and the **Re-roll** button, then Save planet and Plan.
`--seed` is on `tiles` as well, and that is not a convenience: the detail passes hash the seed into every
droplet, so a tile run has to be told the seed its heightmap was baked under. `CheckBake` refuses a mismatch
rather than giving you gullies from a different world.
### What moves
| | |
| --- | --- |
| the upland fabric | where the massifs stand inside every class that has one |
| the rock | which lithology province is where, and so which flank of a range is gullied and which is benched |
| the faults | where every trace is, which way it runs, and how much it throws |
| the regional swell | the long-wavelength warp that puts divides on a plain |
| the initial relief | the small symmetry-breaking the solve starts from |
| the coastline detail | the bays and headlands the waterline roughening cuts, except where the overlay pins it |
Measured on this template, seed 7 against 9342: **13.8 %** of `map_uplift.png` and **24.9 %** of
`map_erodibility.png` change, against 2.1 % of `map_class.png` — and that 2.1 % is only the coastline.
### Rock
One low-frequency pattern over the whole planet, cut into `pipeline.lithology`'s types at **quantiles of the
planet** so every region agrees, multiplying each class's own `k_mult`:
```jsonc
// Planet.json
"lithology_wavelength_km": 9, // how big a province is. 0 switches it off
"pipeline": { "lithology": { "types": 3, "k_multipliers": [0.6, 1.0, 1.8] } }
```
Set the wavelength well *below* a range or a range is one rock and you have gained nothing: 9 km against
belts of 15–25 km puts two or three provinces across each of them. The bands are equal-area, so every type
appears whatever the seed did — a seed that produced no hard rock anywhere would be a seed that quietly
removed a process.
Boundaries are graded rather than blurred, and pointwise: the width of a transition on the ground follows the
field's own gradient, sharp where the rock changes fast. That is not a style choice. A blur is a neighbourhood
operation, and one near a region's edge would read cells a different decomposition of the planet would not
have given it — the same rule that makes the cut a planet quantile rather than a regional percentile.
### Faults
A fault is **a difference in uplift rate across a line**, steep on one side and gentle on the other, which
erosion carves into a scarp. That is why it is not something to paint: painted as terrain the solve erodes it
away, applied as a rate it maintains itself. It is what puts an escarpment and a straight fifteen-kilometre
valley inside a range, and neither is a shape a brush can draw.
```jsonc
{ "name": "highland", "uplift_mm_yr": 0.25,
"faults": { "per_1000km2": 25, "throw_m": [120, 400], "length_km": [6, 18] } }
```
- **`per_1000km2` is a density over this class**, not a count, because a class covers whatever you painted
with it. This template has 549 km² of highland, so 25 is about fourteen traces before the long ones step.
- **`throw_m` is the whole step across the fault over the run**, not a rate — how much higher the upthrown
side would stand than the downthrown one if nothing eroded either, which is a number you can picture. It
is the *step*, not the height of each side: before D-62 the profile put a full throw on each flank and so
built two, and a legend written against that number is now asking for half what it used to get.
- **Put the block on the classes that are orogens.** A plain should have none. The *influence* is not
restricted to the class, because a range-front fault runs along the edge of a range by definition and its
scarp faces the lowland.
`planet.fault_grain_km` is only how they are **aimed**: a grain field, so traces near each other come out
sub-parallel and the set swings gradually across the world rather than running as corduroy.
**A fault is kilometres wide, not a line.** The rate crosses from the downthrown side to the upthrown one
over about a kilometre and the upthrown flank reaches six, so what a fault makes is a *range front* — a belt
of ground with its own valleys cut into it, its crest a kilometre or so back from the trace. It used to be a
step across one cell inside a 600 m welt, which erosion could not touch, so it came out as a smooth ruled
ridge; that is D-62. Two consequences for you: a fault influences ground several kilometres from where you
would draw the line, and it needs that much room, so faulting a class painted in strips two kilometres wide
will not give you what the picture in your head has.
**And a set of them saturates rather than stacking** (D-63). Faults near each other are sub-parallel on
purpose — that is what `fault_grain_km` is for — so once each one is kilometres wide they overlap, and
overlapping faults that all push the same way used to add. Where several reach the same ground the total is
now bent onto at most 1.6 times whatever the strongest single fault there asked for. One fault is untouched
by that bound, so `throw_m` still means exactly what it says; what it stops is five parallel faults
delivering five throws. The practical consequence for you: **raising `per_1000km2` past the point where
faults overlap buys texture, not height.** If you want a belt to stand higher, raise `throw_m`.
Watch `fault_grain_km` against the size of your landmasses. It is the wavelength of the field the strikes
are drawn from, so a landmass much smaller than it sits inside one grain cell and *every* fault on it comes
out near-parallel. The shipped 45 km against a 22 km landmass is exactly that case.
Traces over twelve kilometres are drawn as two or three overlapping en-echelon segments, the throw rises and
falls along strike as a bell rather than holding flat through the middle, and the only ceiling is the repose
angle — or whatever your own numbers asked for, whichever is higher. A bake's per-region line says what share of the land that bound
touched, and says nothing when it touched none; a large share means the throws are big for the class they are
sitting in.
**Expect erosion to take most of the throw.** Measured on this template, region 15 baked whole: five traces
with throws of 139 to 399 m left scarps of 2.7 to 50 m, all five facing the side the fault raises. That is
the point of applying a fault as a *rate* — a painted 400 m step would have been eroded to nothing in a
thousand steps, while a rate difference keeps rebuilding the scarp as fast as the rivers cut it down. (Those
five were measured 600 m either side of the trace and under the pre-D-62 profile, so they straddle the old
welt rather than spanning a range front; treat them as the right order of magnitude and not as a table to
tune against.)
They are drawn on `map_uplift.png` in cyan — the line rather than the rate it contributes, because a scarp is
a couple of hundred metres wide and that map is a hundred kilometres across. The geometry is in the bake's
`meta.json`, so "which fault made that valley" is answerable afterwards.
### Making a desert
A desert is not a low uplift rate — a wet lowland has one of those too. At the geology grid the only lever is
`k_mult`, and below 1 it means less water doing less work, so the ground comes out steeper and more angular
and further from being worn down. That is right, and it is nowhere near enough: at eight metres a desert and
a meadow are the same picture.
The difference is at two metres, in the `detail` block:
```jsonc
{ "name": "desert", "rgb": [238,221,153], "uplift_mm_yr": 0.10, "k_mult": 0.5,
"detail": { "droplets_per_cell": 0.035, "strata_contrast": 0.92, "amplitude_m": [7, 14] } }
```
- **`droplets_per_cell`** is the load-bearing one. A fifth of the running water turns a dendritic gully
network into a few isolated wadis, which is most of what "arid" looks like from the air.
- **`strata_contrast`** near 1 keeps the ledges sharp: mesas and benches survive because nothing is rounding
them off.
- **`amplitude_m`** raised on flat ground is dune. Two metres on a plain is a plain; ten is a sand sea.
Anything left out keeps the manifest's own number, and a legend that overrides nothing costs nothing — the
class raster is not even carried through the detail passes.
### Putting the range inland
A uniformly painted island comes out at the angle of repose right down to the water, and that is arithmetic
rather than a tuning miss: for `n = 1` the uplift rate alone fixes the hillslope angle, so the rivers cut down
to sea level but the ground between them has no idea how far from the coast it is. Fjords, end to end.
`coastal_plain_km` ramps the rate up from the waterline, so there is a plain in front of the range:
```jsonc
{ "name": "highland", "rgb": [68,170,102], "uplift_mm_yr": 0.25,
"massif": { "floor_mm_yr": 0.045, "fraction": 0.30 },
"coastal_plain_km": 4.0, "coastal_floor_mm_yr": 0.03 }
```
```
distance inland 0km 1km 2km 3km 4km 5km+
uplift mm/yr 0.03 0.06 0.13 0.20 0.24 0.25
at a divide 4° 9° 18° 27° 31° 32°
typical ground 1° 3° 6° 9° 11° 12°
```
(on a massif cell — the ramp runs up to whatever rate the massif field left there, and it only ever ramps
*down* towards the sea, so a cell of plain between two massifs keeps its floor rather than being raised into a
rim of hills round the edge of the continent.)
It never goes to zero at the water, and that matters: an earlier version of the generator tapered the uplift
to nothing at the shore as a hidden side effect, which flattened the hundred-metre strip the surf works in and
moved every cliff inland (D-52). This is you saying where the range starts.
### Craters
An impact is an event, not a rate, and it cannot be painted as one — a closed basin built from negative uplift
is filled in by the priority-flood within a hundred steps, because the flood raises every depression to its
spill level on every one of them. So a crater class is **stamped onto the finished terrain after the solve**,
and its shape comes from the blob you painted: distance inward from its own shoreline, normalised by its
widest point, so the same four numbers describe a small crater and a large one.
```jsonc
{ "name": "crater", "rgb": [124,117,111], "uplift_mm_yr": 0.15,
"crater": { "rim_m": 340, "floor_m": 60, "rim_at": 0.30, "wall_at": 0.62 } }
```
```
sea ~~~~\ /‾‾‾\_________/‾‾‾\ /~~~~ sea
\ / rim rim \ /
\____/ \____/
t: 0 0.30 0.62 .. 1 floor
```
`rim_at` is where the crest sits and `wall_at` where the inner wall reaches the floor, both as a fraction of
the way in from the shore. `floor_m` above zero keeps the basin dry; the floor is a closed basin, so expect it
to pond in the detail passes unless there is a gap in the rim. A crater that straddles the seam is one crater.
The class's `uplift_mm_yr` still matters a little — it is what the solve does to the flanks before the stamp
lands — so keep it low. It is not what makes the crater.
**Uplift is the number that matters and the one to turn first.** Steady-state slope is `U/(K·A^m)`, applied
down to a single cell, so at an 8 m cell and `K` 5e-5 a rate of 0.03 mm/yr is about a 4° hillslope, 0.08 about
8°, and anything past roughly 0.45 is above the angle of repose and shaped by landsliding rather than by
rivers. Raising the rate makes the summits *higher*; it does not make the ground steeper past that point.
### The white problem
Every hand-painted world map draws white twice: the polar caps, and an outline stroke around each island.
Exactly one class can own that colour, and it has to be the stroke, because the stroke is the one that must be
recognised wherever it appears. What the caps become is then a `derived` class:
```jsonc
{ "name": "ice", "derived": true, "rgb": [250, 250, 250], "uplift_mm_yr": 0.05 },
{ "name": "stroke", "rgb": [238, 238, 238], "stroke": true, "edge_class": "ice" }
```
A white region touching the top or bottom row is a cap and becomes `ice`; every other white dissolves into
whichever real class is nearest, split down its middle rather than given wholly to one side — which is the
only answer that does not move the coastline by the width of the artist's brush.
The plan's two counters are how you check it landed: `Map3.jpg` reports **2,084,442 px rescued at the poles
and 0 dissolved**, which is the evidence that on this template white is only ever the caps.
## The palette
How a preview is *drawn* is a separate file from what the painting *means*, and deliberately so: the legend is
about the world, the palette is about the picture. Nothing in a palette changes a height — two bakes of the
same world under two palettes are the same terrain — so swapping one is cheap and reversible.
```bash
# write the defaults out to start from
Tools/Terrain/bin/terrain.exe palette RawContent/World/Templates/mine.palette.json
```
Then point the planet manifest at it with `"palette": "Templates/mine.palette.json"`. Leaving the key out
uses the same numbers, so the file is a starting point rather than a requirement.
It holds the hypsometric ramp (`land_stops`, sea level at `t` 0 to the top of the land at `t` 1), the water
(`sea_shallow`, `sea_deep`), the rivers, the ice, where the ramp's top is taken from
(`land_top_percentile` — a percentile rather than the maximum, so one high summit cannot push a whole
continent into the bottom of the ramp), and the light (`sun_azimuth_deg`, `sun_altitude_deg`, `ambient`,
`gain`). Anything left out keeps the default; a misspelt key is an error rather than a setting that silently
does nothing. Keys beginning with an underscore are comments.
## Reading a plan
`terrain plan` writes the maps and `plan.json` into `RawContent/World/Plan/`, in about four seconds. In
order of what they answer:
- **`map_class.png`** — did the legend read the painting? If this is not the picture you drew, nothing
downstream can be. The match report beside it counts pixels no class was near.
- **`map_regions.png`** — how the planet was cut up. Each region is one hue, its land saturated and the ocean
margin it carries the same hue dimmed. Check that a landmass straddling the seam is one colour and not two,
and that nothing has swallowed the ocean.
- **`map_uplift.png`** — the field everything else is a consequence of, the massif fabric included, so a class
with a `massif` block shows as plain with upland in it rather than as one flat colour across the landmass.
The one to read when the ground comes out the same everywhere.
- **`map_erodibility.png`** — where texture inside a range will come from.
- **`map_overlay.png`** — the annotation layer over a dimmed class map, when there is one. A mark means
nothing on its own and everything against the coastline or the range it was drawn along.
And the class table above them, which is the four seconds best spent. It prints every class's uplift rate and
then **two** angles, and which of the two you read is the difference between a legend that is right and one
that is three times too hot:
```
class share cells uplift mm/yr K depth m divide typical
lowland 14.4% 10967451 0.080 1.00 - 11.3 deg 3.8 deg rolling
massif over 16% of it; the other 84% is 0.012 mm/yr, 1.7 deg at a divide and 0.6 typical - plain
```
The same two angles are in World Orogen's class table, beside the inputs that set them (D-67), which is the
place to read them while you are still typing the numbers.
`divide` is the steepest ground the rate can make: steady state is `S = U/(K·A^m)`, `A` is smallest at the top
of a catchment, and that is where the number comes from. `typical` is the median slope over the class, and it
is about **a third** of the divide angle in tangent — measured on a 600² grid of 8 m cells over a factor of
twenty in rate, where the ratio came out 0.34, 0.33, 0.33 and 0.32. Almost none of a map is divide. Read the
second column; it is the ground somebody stands on, and `reads as` is taken from it.
Then the block that says what a re-roll would move: the rock provinces and the fault set, with a trace count
per class. And if there is an overlay, how much of the world each mark covers, how many pieces it is in, and
what it asked of the coastline.
Then the region table, which is where the bake time goes. **The margin is the lever.** It decides which
landmasses are solved together: two within twice the margin share a box. At 0.5 km this template gives 18
regions; scattered islets on some other template would each cost a whole region, and `--margin-km` is how to
find out before spending the afternoon.
**Read the time estimate as a floor.** It is calibrated on lowland, and mountains cost about five times as
much per cell — steep ground drives the hillslope law to its full sub-step budget every step while a plain
leaves it idle. Measured on this template: a 14.0 M cell lowland region took 1014 s and a 4.0 M cell highland
region took 1394 s. So raising an `uplift_mm_yr` does not only change the terrain, it changes the bake time.
## The detail bake
`terrain tiles` runs the detail passes over a geology bake and writes the ground somebody can stand on. It
reads the bake's heightmap off disk rather than solving anything, which is what makes it batchable: the
geology is hours, a 5 km tile is about twelve seconds, and the islands you care about can be baked first.
```bash
# every tile, about forty minutes for a 100 km world. --bake defaults to the newest Bake_NNN.
Tools/Terrain/bin/terrain.exe tiles
# a rectangle of them, by tile index, from a particular bake
Tools/Terrain/bin/terrain.exe tiles --bake RawContent/World/Bake_001 --only 11,7,13,8
# the geology upsampled and nothing else: is what you are looking at the solve or the detail passes?
Tools/Terrain/bin/terrain.exe tiles --only 16,6,16,6 --no-detail
```
Each tile gets `Planet_x11_y07.png` (16-bit height, 2500 x 2500 at 2 m), `_shade.png` (a hillshade at the same
resolution, because a 16-bit grey PNG of a hundred metres of relief is a flat grey rectangle to look at), and
`_flow`, `_wear` and `_deposit` as 8-bit maps of what the droplets did. `tiles.json` beside them carries the
grid, the cell size, each tile's world origin and its elevation range.
If the planet has an overlay, `_overlay.png` comes too — 8-bit, one mark index a detail cell — with
`overlay.json` beside `tiles.json` as its key and the feature list. The mask is sampled through world metres
rather than through a tile-local index, so a cell reads the same mark whichever tile reaches it.
The tiles are seamless and that is measured, not asserted: a tile carries a margin of three droplet lifetimes
which is discarded afterwards, and every hash and noise lattice in the passes is keyed on absolute world
position, so a cell in one tile's interior comes out as it would have in one whole-world run. The margin costs
about five per cent of a tile on each side.
**What is not there yet:** the shore, at *detail* resolution. The geology bake has a shelf, a surf-cut
platform and a beach as of D-60, but the detail passes do not refine any of it: a tile's coastline is the
8 m coastal pass upsampled, with no sand, no boulder field and no plan-view crenulation at 2 m. Everything
inland is finished.
## What a bake writes
### `preview.png` is relative, and that matters more than it sounds
The hypsometric ramp's top is `land_top_percentile` of **the land in the world being drawn**, not a fixed
height. So bare rock and snow on a preview mean "the highest ground here" and say nothing at all about scale.
Measured on this template's central lowland: it bakes to **0..47 m** with a median hillslope of **0.61°**,
4.4 % of it over three degrees and nothing anywhere over eight — a plain by any reading. Drawn against its own
32 m ceiling it comes out with tan uplands and white caps on its 40 m hills, which is exactly what a 2800 m
range looks like, and it is the single most misleading thing the generator produces. Redrawn at a fixed 400 m
ceiling the same heightmap is flat green with four pale massifs on it.
Every run prints which ceiling its preview used, so read that line before reading the picture:
```
preview the hypsometric ramp tops out at 32 m - the 99.5% percentile of *this* world's land, so rock and
snow mean "the highest ground here" and nothing about scale. Set palette.land_top_m for an
absolute ramp
```
`palette.land_top_m` is a height in metres; `0` keeps the percentile. The percentile stays the default on
purpose — an absolute ramp over a world with no mountains is a flat green shape with nothing legible on it,
and "is there drainage here" is a question the contrast has to answer. When what you want to know is *how
steep is this ground*, read `map_slope.png` and the plan's `typical` column, not the tint.
`planet_height.png` at the geology grid (12500 × 6076 at 8 m for this world), plus quarter and tenth
overviews; `preview.png` with hypsometric tint, hillshade and rivers; the plan's maps again, now over real
terrain; `map_slope.png` and `map_flow.png`; and `meta.json` with the resolved manifest, the legend, the
match report and every region's statistics.
And **the two coastal maps**, which are the whole of what the pass did:
- **`map_coast.png`** — what it moved, cool where the surf cut and warm where the sediment landed, on a
±30 m scale. The deep sea floor is masked out of it on purpose: the ocean drops five hundred metres in
this one pass and would otherwise set the scale for the few metres the shore processes move, which is the
thing the map exists to show.
- **`map_exposure.png`** — how open each stretch of shore is to the sea, drawn only within a kilometre of
the water. Not tidiness: exposure is measured *on* the waterline and carried inland by "the nearest stretch
of shore to you", so past a few hundred metres it stops being a coastal quantity and becomes a picture of
the continent's medial axis.
The pass is one line in the log and one block in `meta.json`, and it runs **once over the whole cylinder**
rather than per region — fetch crosses straits, and a sediment budget split in two is not a budget:
```
coast: 971 km of shoreline, 65% sea, shelf 60% of it; surf planed 3.5 km2 and cut 34.18 Mm3,
6 river mouths delivered 1.15 Mm3, 35.32 Mm3 laid (0% unplaced) as 4.14 km2 of new beach
```
`0% unplaced` is the line to read: it is the sediment the budget could not find a home for, and a figure that
is not near zero means the drift ran out of shore before it ran out of sand. On a `--only` run the backshore
figures read 0 m, correctly — most of the planet's land is unsolved and sitting at sea level.
It is also the memory peak of a bake, at about **8.3 GB** of working set against the solve's 3.6, and it is
worth knowing that before starting one on a smaller machine.
And **the verdict block**, which a bake prints for the first time as of D-59: the slope distribution, the
slope-area fit, the hypsometric integral, the drainage density, and the per-uplift-class breakdown that is
the one to read. It is pooled from the regions rather than computed on a grid that never exists - a histogram
adds, so summing the pieces and taking a quantile of the sum is exactly what one pass over the whole world
would have given.
A run with `--only` marks itself **PARTIAL**: its extent is the whole cylinder while its ground statistics
cover just the landmasses that were solved, and the two are not comparable.
```
field -543..340 m; land 0..9 m (relief 9 m), 36% land, 0.00% clipped
PARTIAL: the line above is the whole world; everything below is the 3% of its land that
was actually solved (842682 of 27455176 cells). The two are not comparable.
slopes: 100% under 15 deg, 100% under 30, 0.0% over 50, median 0.5 deg
...
by uplift class:
plain 0.00..0.10 mm/yr 100% of land slope 0.5 deg median, 0.8 P90 relief 3 m/500 m ...
```
The polar pad never appears in any of them. It is a few rows of synthetic ocean above and below the painted
map, added so that a cap touching the top row has a shore to drain into — without it the solver would treat
the map edge as an outlet and freeze the ice at its initial relief.
## Limits worth knowing
- **A landmass cannot ring the planet.** A region is flattened into a rectangle with water on both sides,
because a grid edge is an outlet; one that goes all the way round has no such edges. The run refuses it
rather than silently freezing the coast. Break it with a strait.
- **The seed alone no longer names a world.** The margin and the minimum landmass size change how the planet
is cut up, and the priority-flood's traversal across a flat depends on the box it is flooding. All three are
in `Planet.json` and all three are recorded in `meta.json`.
- **The elevation range is a hard clip.** A run whose clip fraction is above 0.1 % is a failed run, not a
rounded one, and painted uplift makes it easy to ask for more relief than the range holds. `U/K` is the one
relief knob.
## The same painting in World Orogen
`Tools/Orogen/` (D-66) reads exactly these files. Serve the folder with any static server and open the
import page's **Painted Map** source:
```bash
npx serve Tools/Orogen # then http://localhost:3000/import
```
Choose `Map3_002.png` as the painting and `Map3.legend.json` as the legend, press **Solve Terrain**, and in
about half a minute the painting is a globe with rivers, climate and a satellite view, plus class, uplift,
erodibility, drainage, slope and basin layers to inspect and export. The table under the legend edits the
classes' numbers and **Download legend JSON** writes them back into a copy of this file with the commentary
intact, so a legend tuned there runs here unchanged.
What it is for is the thirty-second question - does this painting make the rivers I meant, is that lowland
a plain with massifs in it or a slab - before the two-hour bake answers the eight-metre one. What it is not:
the globe is Earth-sized (legend distances are scaled by the ratio of circumferences), the cells are ~44 km,
and faults, craters, the plates, the repose clamp and the detail passes are not carried because nothing at
that cell size could show them.
**It reads this planet's own files now, and the studio serves them.** Point it at `Planet.json` and the
planet block, the lithology multipliers, the seed and the geology grid's constants travel without being
retyped; it outranks a legend's own `planet` block, because it is the file the bake reads. The **overlay**
is carried too - as a texture over the globe and the map rather than voted onto the mesh, since a road is
a few pixels wide and a cell there is tens of kilometres - and its `coast_jitter` marks pin or roughen the
shore on the sphere exactly as they do here.
Easiest of all, while `terrain studio` is running, its **From terrain studio** button loads the painting,
both legends and the manifest in one step, including strokes made since the last save, because the studio
holds the painting in memory. The studio shares reads and only reads: its CORS header is on GET alone and it
answers no preflight, so a browser tab can look at the planet and can never start a bake.
**What it still cannot tell you** is the ground at eight metres. The bake owns the geology grid, the coast
pass, the faults, the craters and the detail tiles, and only they produce ground a player can stand on.