FERRETGame engine over Babylon.js
Ferret

@ferret/waves

Waves on open water, in the style of @ferret/clouds: a pure field of position, time and the wind, the same on every peer, no Babylon. The picture stays buildWater of @ferret/levels (Gerstner wav…

Waves on open water, in the style of @ferret/clouds: a pure field of position, time and the wind, the same on every peer, no Babylon. The picture stays buildWater of @ferret/levels (Gerstner waves in the vertex shader, which Unreal's Water plugin also feeds); this package gives it what it lacks: a sea state made from the wind (seaState returns the WaterWave[] a WaterDef takes), the height of the surface where a ship floats (the shader moves points sideways too, so the height at a world point is found by undoing that move), the slope, the speed of the water, and where the crests break into foam.

10 exports · source in packages/waves/src

Classes

SeaFromFlow class

class SeaFromFlow

A sea that follows the wind of a @ferret/flow field. update(t) reads the wind and returns a new list of waves when the wind has changed enough (a front arrives, a storm builds) and null while the sea stays as it is; hand the list to WaterSurfaceView.setWaves. The result is a pure function of the field and the time, so every peer that updates with the same times gets the same sea. The wave phases jump when the list changes, so keep the steps large rather than small.

waves: Wave[]
constructor(private field: FlowField, o: SeaFromFlowOptions = {})
wind(t: number): { speed: number; dir: number }the wind now: speed m/s and the direction the waves go, degrees (0 = +x)
update(t: number): Wave[] | null

packages/waves/src/index.ts:138

Wake class

class Wake

The trail of ships on the water: white water that spreads and fades behind each hull. A ship calls add every frame with its position, speed and beam; foam(x, z, t) reads 0..1 for the picture. The trail widens at the Kelvin angle (about 19.5 degrees) and fades over life seconds. A pure function of what was added, so peers that add the same samples see the same wake. push also stirs a WaterSurface of

constructor(private o: { life?: number; spacing?: number; max?: number } = {})
get life(): number
add(x: number, z: number, t: number, speed: number, width = 4): voida hull at (x, z) moving at speed m/s, width m across; samples closer than spacing m to the last are dropped
prune(t: number): voiddrop what has faded; call now and then
get samples(): number
foam(x: number, z: number, t: number): numberwhite water at a point, 0..1
push(surface: { disturb(x: number, z: number, volume: number, radius?: number): void }, x: number, z: number, speed: number, width: number, dt: number): voidpush the hull's displacement into a harbour surface: the volume a hull of this size moving at speed sweeps aside in dt seconds

packages/waves/src/index.ts:177

WaveField class

class WaveField
constructor(waves: Wave[])
get waves(): number
displacement(x: number, z: number, t: number): V3the move of the rest point (x, z) at time t: sideways (x, z) and up (y), as the shader of buildWater does it
height(x: number, z: number, t: number): numberthe height of the surface above the rest level over the world point (x, z): the rest point whose move lands there is found first
normal(x: number, z: number, t: number): V3the unit normal of the surface at the rest point (x, z)
velocity(x: number, z: number, t: number): V3the velocity of the water particles at the surface (orbital motion), m/s: what carries a floating thing and rocks a boat
foam(x: number, z: number, t: number): numberHow much the crest breaks here, 0 (smooth) .. 1 (white water): the Jacobian of the sideways move is 1 on a calm surface and falls towards zero where the water piles up; foam comes where it falls below about 0.6.

packages/waves/src/index.ts:56

Functions

seaScale function

function seaScale(wind: number): { peak: number; hs: number }

The peak wavelength (m) and the significant wave height (m) of a fully developed sea at a wind speed (m/s): Pierson-Moskowitz.

packages/waves/src/index.ts:26

seaState function

function seaState(wind: number, windDir = 0, o: SeaOptions = {}): Wave[]

The waves of a sea driven by a wind of wind m/s from the direction windDir (degrees, 0 = +x: where the waves go). The components are sampled from the Pierson-Moskowitz spectrum, log-spaced from short chop to the long waves, each with the amplitude the spectrum gives it and a direction spread around the wind; the result is a deterministic function of its arguments. Calm wind gives a flat sea.

packages/waves/src/index.ts:36

surfaceFn function

function surfaceFn(field: WaveField, level = 0, local?: { height(x: number, z: number): number; level?: number }): (x: number, z: number, t: number) => number

The height function a ship rides (Environment.surface of

packages/waves/src/index.ts:165

Interfaces

SeaFromFlowOptions interface

interface SeaFromFlowOptions extends SeaOptions
at?: [number, number]where (x, z) the wind is read: the middle of the water
height?: numberheight of the reading above the surface, m (the standard 10 m wind)
speedStep?: numberthe sea is made again only when the wind has changed this much: m/s ...
turnStep?: number... or turned this much, degrees. A real sea answers a changing wind slowly, so small changes leave it as it is.

packages/waves/src/index.ts:121

SeaOptions interface

interface SeaOptions
count?: numberhow many wave components (the shader takes up to 16)
seed?: number
spread?: numberhow widely the waves spread around the wind, degrees either side (a young sea is wide, a swell narrow)
sharpness?: number0..1 how sharp the crests are; the sum is held below the point where a crest loops over

packages/waves/src/index.ts:15

Wave interface

interface Wave

the same shape as WaterWave of

wavelength: number
amplitude: number
steepness?: number
direction?: number

packages/waves/src/index.ts:10

Types

V3 type

type V3 = [number, number, number]

Waves on open water, in the style of

packages/waves/src/index.ts:8