Physics · interface
HeightSurfaceOptions
Any function that answers "how high is the ground at this column?", as a GroundSurface.
Explained in Your first game, Rigid bodies.
interface HeightSurfaceOptionsimport type { HeightSurfaceOptions } from '@driftengine/physics';In depth
The hundred lines every consumer with terrain writes, written once. A world that is not flat
has to answer that question somewhere, and the two things that already implement GroundSurface
answer different ones: RibbonSurface is a route with a width and a bank, and BoxSurface is a
set of flat pads. Neither is a hillside, a ditch, a heightmap or a dune. A consumer with one of
those has a heightAt(x, z) of their own within an afternoon, and then spends the rest of the
week turning it into a surface: a normal by differences, a band query, the route fields nothing
asked for, and finally the discovery that the character controller and everything else that
stands on ground each need their own copy of the wiring.
That was reported from outside as "there is no terrain height a controller can ask about", beside a note that the answer they wrote is a hundred lines and that every consumer with a road will write it again.
This is a seam and not terrain. It renders nothing, stores nothing, and has no level of
detail: heightAt is the consumer's, and what this adds is the surface contract around it, so
one object can be handed to a character controller, to a vehicle, and to whatever places props —
and the floor is one thing rather than three copies of it. Terrain proper is its own track.
A heightAt that returns a non-finite number means there is no ground in that column, and
that is the contract a layered world is built out of: a bridge deck answers its height over the
span and NaN everywhere else, so it is a floor exactly where it is drawn and nothing anywhere
else. Until 2026-08-30 this was said only in a private comment inside this file, which is no use
to somebody deciding how to model a flyover.
Properties
| Name | Type | Description |
|---|---|---|
stepMreadonlyoptional | number | How far apart the two samples that measure the slope are, metres.MoreThis is the scale the ground is measured at, and it is a real choice. The normal comes from
a central difference, so a feature narrower than twice this is smoothed away — a kerb sampled
at half a metre is a gentle ramp — while a step far below the resolution of A caller who can differentiate their own field should pass |
boundsreadonlyoptional | { readonly minX: number; readonly maxX: number; readonly minZ: number; readonly maxZ: number; } | Where the surface exists at all. Outside it, there is no ground and a query says so.MoreAbsent means everywhere, which is right for a field defined by arithmetic and wrong for one backed by a finite array — a sample off the end of a heightmap is not ground at height zero. |
Methods
normalAtoptional
normalAt?(x: number, z: number, out: { x: number; y: number; z: number; }): voidThe exact unit normal, when the caller has one.
| Parameter | Type | Description |
|---|---|---|
x | number | |
z | number | |
out | { x: number; y: number; z: number; } |
More
An analytic profile — a trapezoid ditch, a cone, a sine dune — knows its own derivative, and
four extra calls to heightAt a tick to rediscover it numerically is both slower and less
accurate. Must be unit length: nothing normalises it afterwards, and a controller compares it
against a slope cosine.