Terrain · interface
HeightfieldPatchOptions
One square of a heightfield, as geometry, at a level of detail — and matched to its neighbours.
Explained in Terrain.
interface HeightfieldPatchOptionsimport type { HeightfieldPatchOptions } from '@driftengine/terrain';In depth
A patch is the unit of detail, and the seam between two of them is the whole problem. A heightfield drawn at one resolution is either too coarse underfoot or too fine at the horizon, so it is drawn in squares and the distant squares skip samples. Where a square that skips none meets one that skips every other, the fine edge has vertices the coarse edge does not — and those vertices sit on the field while the coarse edge cuts the chord beneath them. The gap between the two is a crack, and a crack in terrain is a hole through to the sky.
Matched rather than hidden. The usual remedy is a skirt: a vertical curtain dropped from every patch edge, which fills the hole with something the wrong colour and lit the wrong way, and costs geometry along every boundary in the world for ever. What this does instead is move the fine edge's odd vertices onto the coarse edge's own straight segment, so the two edges are the same polyline and there is nothing to fill. It costs one interpolation per boundary vertex, at build time, and no geometry at all.
The cost of matching, stated because it is real: along a matched edge the drawn surface is
the coarse chord rather than the field, so Terrain.heightAt and the picture differ there by up
to the sag of one coarse cell. That is the same error the coarse patch itself carries over its
whole area, arriving one cell early — which is why it is the right trade and why it is written
down rather than left to be discovered.
Properties
| Name | Type | Description |
|---|---|---|
xreadonly | number | Grid index of the patch's first sample, along x. |
zreadonly | number | Grid index of the patch's first sample, along z. |
cellsreadonly | number | How many field cells the patch spans. |
stepreadonlyoptional | number | How many field cells one drawn cell spans. 1 is full resolution.MoreMust divide |
neighboursreadonlyoptional | { readonly minusX?: number; readonly plusX?: number; readonly minusZ?: number; readonly plusZ?: number; } | The step each neighbouring patch is drawn at, where it is coarser than this one.MoreOnly the coarser side needs naming: the finer patch is the one that moves. A neighbour at the same step or a finer one changes nothing, so a caller may pass what it knows and leave the rest out. |
colorreadonlyoptional | ReadonlyVec3 | One colour for the whole patch. Ignored where materials is given.MoreTwo ways to say the same thing would be a mistake, and this is not one: a patch with a single colour is what a caller writes before they have a weight map, and a map is what they write once they have several materials. The second wins where both are present, said here so it is not discovered. |
emissivereadonlyoptional | number | |
materialsreadonlyoptional | TerrainMaterials | Several materials blended across the field, rather than one colour for this patch.MoreRead in the field's own coordinates and never the patch's, which is what makes two patches agree along the edge they share. Shading from a patch-local coordinate would restart the blend at every boundary and draw a grid of squares over the world. |