Engine · guide
Terrain brushes
One brush — a shape, a falloff, a radius in metres and a spacing — answering one question for three different tools.
Edit this page on GitHubDocuments
What it is
TerrainBrush answers one question — *for this world-space sample, what is the weight of this
stamp?* — and it does not know whether the answer will scale a height, a layer weight or a scatter
probability. It carries a shape, a falloff curve and fraction, a radius in metres, a strength, a stamp
spacing and a rotation mode. BrushStamp is one landing of it; BrushStroke turns a path into evenly
spaced stamps; BrushFootprint is the region a stamp can reach.
What it is for
Three consumers, one implementation, one settings panel section, one set of tests. Unreal implements the sculpt brush, the paint brush and the foliage brush three times, so a soft edge sculpted at strength 0.3 and a soft edge painted at strength 0.3 are different shapes there. Here they are the same shape by construction.
You do not want it for anything whose falloff is not radial — a ramp is defined by two picked points
and a width, which is why TerrainSculpt.Ramp does not take a brush at all.
Using it
var brush = TerrainBrush.Default with { Radius = 8f, Strength = 0.5f, Falloff = 0.5f };var weight = brush.WeightAt(sample, new BrushStamp(centre));⚠ Falloff is the fraction of the radius that falls off, not where the falloff starts. The two
read almost the same on a slider and are inverses of each other. Zero is a hard disc; one is falloff
all the way in, with no plateau at all.
⚠ A half-falloff brush has a plateau, and that surprises the tools that read slopes. Every sample within the inner half is at full weight, so a mound built with the default brush is a mesa — the 3×3 neighbourhood at its top is flat, a smooth changes nothing there and an erosion finds no slope to exceed the talus angle. That is correct behaviour for all three; a test of any of them has to build a cone.
⚠ Spacing is a fraction of the radius, not a distance. Making it a distance means that turning
the radius up thins the stroke out until it is a row of discs. Unity and every paint application spell
it this way.
Strokes
BrushStroke accumulates a drag one pointer move at a time and hands back stamps:
var path = new BrushStroke(brush);var stamps = new List<BrushStamp>();path.MoveTo(start, stamps);path.MoveTo(next, stamps);⚠ The first call always stamps, because an artist who clicks without dragging expects one stamp rather than none — which is what makes the same type the Single tool as well as the Paint one.
⚠ Spacing is by distance travelled, with the leftover carried across pointer events. Stamping per event ties the density of a stroke to the frame rate and to how fast the pointer was moving, so the same gesture leaves a different mark on a fast machine.
⚠ A random rotation is a hash of the stamp index, not a draw from a generator. The angle of stamp N depends only on N, so a stroke can be undone and redone to the same result — the same property the scatter needs, for the same reason.
Masks
IBrushMask is a function from the unit square to a weight. It is an interface rather than a texture
so that this assembly needs no image type; the editor hands it something backed by an alpha texture,
and a test hands it a lambda.
The four falloffs
BrushFalloff.Evaluate is arithmetic on one number, and the four curves are Unreal's:
Smooth | Smoothstep — the default, and what a landform wants |
Linear | A straight ramp, for a cut with a visible edge |
Spherical | Bulges outward; a dome rather than a mound |
Tip | Falls away fast; a spike with a soft skirt |
Examples
The same brush, three consumers — which is the whole argument:
TerrainSculpt.Sculpt(terrain, layer, brush, stamp, metres: 2f);TerrainSculpt.Paint(terrain, paintLayer: 1, brush, stamp, amount: 40);A soft edge sculpted at strength 0.3 and a soft edge painted at strength 0.3 are the same shape,
because both went through WeightAt.
A hard-edged brush, for a cut that has to read as deliberate:
var hard = TerrainBrush.Default with { Radius = 4f, Falloff = 0f, Curve = BrushFalloffKind.Linear };⚠ Falloff 0 is a disc with a step at its rim, which on a heightfield is a wall one sample thick. That is sometimes exactly what a road cut wants and is never what a landform does.
See also
- The terrain heightfield — what the weights are applied to.
- Sculpting a heightfield — the kernels that consume a stamp.
- docs/plan/31 § D12 — why the brush is one service, and what having three of them costs.