Skip to content

Documentation / scene / WaterSurfaceSampler

Class: WaterSurfaceSampler ​

Defined in: libs/scene/src/scene/water_surface_sampler.ts:92

Height queries against a water surface, driven by the material's own evaluation of it.

Why this and not a CPU-side reconstruction of the waves: the displacement the material draws with is produced on the GPU, and any second implementation of it - re-deriving the spectrum, decoding the height textures, summing the dominant waves - is a different function that has to be kept in step by hand. Water.getSurfacePoint asks the material instead, so it is exact by construction, and it includes whatever else displaces the surface, such as a WaterInteraction field. What it costs is one point-list draw and one readback per batch, which is why queries here are batched and throttled rather than issued per call.

The queries are placed on a lattice at construction and never move. A frame asks for the same small set of positions every time, so the whole set costs one draw and one readback - the same as a single query would - and the answer is interpolated across the lattice on demand. Positions that move (a hull's own footprint, say) ride along through addExtraPoints.

The motion ​

The reported height eases towards the newest measurement, exponentially, and nothing else is done to it. Compensating the query lag is tempting - the measurement describes the surface a round trip before it can be used - but every attempt at it bought its smoothness back by other means: predicting from a velocity needs that velocity smoothed, extrapolating a wave diverges as the square of the distance, and rate limiting the result reintroduces the stepping it was meant to remove. A plain ease has none of those failure modes, and the lag it leaves is the lag a floating body has anyway. A rigid body should read sampleWorldYRaw instead and supply its own inertia.

Consequences a caller has to accept:

  • Resolution is the lattice spacing. A grid at h metres resolves nothing shorter than about 2 * h.
  • A probe outside the lattice is answered by the nearest cell rather than by a wrapped one; this is a window on the water, not a repeating field.
  • Nothing is known until the first batch lands; until then queries return the still-water level.

Constructors ​

Constructor ​

new WaterSurfaceSampler(water, options?): WaterSurfaceSampler

Defined in: libs/scene/src/scene/water_surface_sampler.ts:126

Creates a sampler over a body of water.

Parameters ​

water ​

WaterSurfaceSource

The water to sample.

options? ​

WaterSurfaceSamplerOptions = {}

Lattice and rate options.

Returns ​

WaterSurfaceSampler

Accessors ​

waveTime ​

Get Signature ​

get waveTime(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:171

Water clock of the newest accepted batch, in seconds.

Returns ​

number


updateHz ​

Get Signature ​

get updateHz(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:181

Upper bound on the query rate in hertz.

A ceiling, not a schedule: a batch is issued as soon as the previous one has landed if that is sooner. Raising it past what a batch costs changes nothing.

Returns ​

number

Set Signature ​

set updateHz(val): void

Defined in: libs/scene/src/scene/water_surface_sampler.ts:184

Parameters ​
val ​

number

Returns ​

void


timeConstant ​

Get Signature ​

get timeConstant(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:192

Seconds for the reported height to close most of the gap to a new measurement. In seconds rather than per frame so that the behaviour does not change with frame rate.

Returns ​

number

Set Signature ​

set timeConstant(val): void

Defined in: libs/scene/src/scene/water_surface_sampler.ts:195

Parameters ​
val ​

number

Returns ​

void


readCount ​

Get Signature ​

get readCount(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:199

Batches completed since construction.

Returns ​

number


failCount ​

Get Signature ​

get failCount(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:203

Batches that failed.

Returns ​

number


pending ​

Get Signature ​

get pending(): boolean

Defined in: libs/scene/src/scene/water_surface_sampler.ts:207

True while a batch is in flight.

Returns ​

boolean


pendingSeconds ​

Get Signature ​

get pendingSeconds(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:211

Seconds the batch in flight has been outstanding, or 0 when none is.

Returns ​

number


lastLatency ​

Get Signature ​

get lastLatency(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:215

Batch latency in seconds, as of the last one that landed.

Returns ​

number


batchFrames ​

Get Signature ​

get batchFrames(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:219

Frames the newest batch took to come back.

Returns ​

number


batchSize ​

Get Signature ​

get batchSize(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:223

Points evaluated per batch, the cost driver of this sampler.

Returns ​

number


ready ​

Get Signature ​

get ready(): boolean

Defined in: libs/scene/src/scene/water_surface_sampler.ts:227

True once a batch has landed and queries return surface heights.

Returns ​

boolean


spacing ​

Get Signature ​

get spacing(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:231

World metres between lattice samples.

Returns ​

number


waterLevel ​

Get Signature ​

get waterLevel(): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:235

World Y of the still-water level.

Returns ​

number

Methods ​

addExtraPoints() ​

addExtraPoints(inputs, onResult): () => void

Defined in: libs/scene/src/scene/water_surface_sampler.ts:251

Register world positions to evaluate alongside the lattice, every batch.

The array is held by reference and read when a batch is issued, so a caller updates the vectors in place each frame and the next batch picks them up. The listener receives the surface positions for those inputs when the batch lands, in the same order. Making these a second call to the water instead would put two feedback renders in one frame.

Parameters ​

inputs ​

Vector3[]

World positions; only X and Z are used.

onResult ​

(results, inputs) => void

Called per batch with the surface positions.

Returns ​

A function that unregisters the points.

() => void


update() ​

update(deltaSeconds): void

Defined in: libs/scene/src/scene/water_surface_sampler.ts:268

Advance the sampler. Call once per frame with the frame's delta.

Parameters ​

deltaSeconds ​

number

Frame delta in seconds.

Returns ​

void


sampleWorldY() ​

sampleWorldY(x, z): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:317

Surface world Y at a world XZ, eased.

Bilinear over the lattice, clamped at the edges rather than wrapped. The table this reads is advanced once a frame by update, so this is a pure lookup and can be called as often as a caller likes.

For something placed directly at the height it reads, which has no inertia of its own and needs the height to move smoothly.

Parameters ​

x ​

number

z ​

number

Returns ​

number


sampleWorldYRaw() ​

sampleWorldYRaw(x, z): number

Defined in: libs/scene/src/scene/water_surface_sampler.ts:327

Surface world Y at a world XZ, from the newest measurement with no easing.

For a rigid body. Feeding it the eased height adds a second lag on top of the one the query already carries, and a body a quarter second behind a falling surface is a body in the air.

Parameters ​

x ​

number

z ​

number

Returns ​

number


sampleNormal() ​

sampleNormal(x, z, out?): Vector3

Defined in: libs/scene/src/scene/water_surface_sampler.ts:336

Surface normal from the eased lattice, by central differences one cell apart.

Parameters ​

x ​

number

z ​

number

out? ​

Vector3

Destination, or a new vector.

Returns ​

Vector3

Unit normal, Y up.

Released under the MIT License.