Terrain · interface
HeightfieldOptions
A heightfield, and the two questions everything else asks it.
Explained in Terrain.
interface HeightfieldOptionsimport type { HeightfieldOptions } from '@driftengine/terrain';In depth
The whole design of this file is one rule: the answer a query gives is the surface that is drawn. A heightfield is stored as a lattice of samples and drawn as triangles, and those are not the same surface — a bilinear patch through four corners and the two triangles that span them agree only along their shared diagonal and at the corners. Interpolating bilinearly because it is the obvious thing gives a character that floats over half of every cell and sinks into the other half, by up to a quarter of the cell's height range, everywhere, for ever. It is a defect with no symptom a screenshot can show and no test that fails, which is exactly the kind this repository writes down.
So heightAt reads the triangle, and heightfieldPatch builds those same triangles, and a
test asserts each vertex against the query.
And there is now a fourth surface, so the rule extends rather than changes. Wave 4B stores
heights as a DTEX layer, which quantises them — so a renderer reading the layer while a query
reads the samples this class was built from differ by the format's tolerance, everywhere,
permanently. terrainTexture.ts answers it the only way that holds: terrainFromHeightLayer
builds a Terrain from the decoded samples, and everything reads that one object. The rule is
unchanged — there is one surface — and what changed is where the numbers come from.
The normal is the other way round on purpose. A face normal is constant over a triangle and
jumps at every edge, so a field built from them is faceted, and — worse — two patches meeting at
a boundary shade differently along it, which reads as a crack that is not there. normalAt is a
central difference over the samples, continuous by construction, and it is what the mesh carries
as its vertex normals. A caller who wants the plane a wheel actually rests on takes three
heightAt samples; a caller who wants to orient a character takes this.
Properties
| Name | Type | Description |
|---|---|---|
widthreadonly | number | Samples across x. At least two, since one sample is not a cell. |
depthreadonly | number | Samples across z. |
spacingMreadonly | number | Metres between samples, the same both ways. |
heightsreadonly | ArrayLike<number> | width * depth heights in metres, row-major with x running fastest. |
originreadonlyoptional | ReadonlyVec3 | World position of sample (0, 0). The origin's own y is added to every height. |