FERRETGame engine over Babylon.js
Ferret

@ferret/clouds

15 exports · source in packages/clouds/src

Classes

CloudField class

class CloudField
cover: number
readonly layers: LayerKind[]
constructor(o: CloudFieldOptions = {})
setCover(c: number)
setWind(w: WindFn)
drift(kind: LayerKind, t: number): [number, number]how far a layer's frame has drifted by time t (summed over whole seconds, cached)
puffsOf(kind: LayerKind, ix: number, iz: number): Puff[]the puffs of one cell of a layer: a deterministic function of the cell's integer coordinates, in the layer's own (drifting) frame
puffsNear(kind: LayerKind, x: number, z: number, t: number, radius: number): Puff[]the puffs of a layer within radius (metres) of a world point; positions are in the layer's frame (add drift for the world)
density(p: V3, t: number): numberHow thick the cloud is at a world point, 0 (clear) .. 1 (inside the heart of one). Each puff is a soft ellipsoid; the game thickens its fog and greys the view with this when a ship flies in, and a wet cloud can add film weight.
shadowAt(x: number, z: number, y: number, sun: V3, t: number): numberHow much of the sun the clouds take from a ground point, 0 (full sun) .. 1 (full shadow): the ray to the sun is crossed with each layer. sun is the unit direction to the sun (y up). With the sun at or below the horizon everything is shadow.
shadowGrid(cx: number, cz: number, y: number, sun: V3, t: number, size: number, n: number): Float32Arrayshadow values around a centre, row-major n*n floats 0..1 over a square of side size m: what a ground shadow texture is made of

packages/clouds/src/field.ts:57

CloudView class

class CloudView
readonly frame: BABYLON.TransformNode
constructor(private scene: BABYLON.Scene, readonly field: CloudField, o: CloudViewOptions = {})
setSun(d: V3)the direction to the sun (unit, y up)
setTint(r: number, g: number, b: number)a warm or grey cast over all clouds (sunset, a storm)
setTier(t: CloudTier)
update(cam: V3, t: number)call each frame with the camera position (world) and the time (s)
dispose()

packages/clouds/src/view.ts:53

Functions

flowWind function

function flowWind(field: FlowField, at: [number, number] = [0, 0]): WindFn

The wind of a @ferret/flow field at the height of the layer, at the position at (x, z): a front brings its clouds with it.

packages/clouds/src/wind.ts:5

hash3 function

function hash3(a: number, b: number, c: number): number

a hash of three integers to 0..1

packages/clouds/src/field.ts:42

puffShade function

function puffShade(up: number, toSun: V3, from: [number, number] = [0, 0]): number

The brightness of a puff for a sun direction (unit, y up): the tops are lighter, the flat base is darker, and the side towards the sun glows. from is the unit horizontal direction from the cloud's centre to the puff; 0..1.

packages/clouds/src/field.ts:169

puffTexture function

function puffTexture(scene: BABYLON.Scene): BABYLON.DynamicTexture

packages/clouds/src/view.ts:16

Interfaces

CloudFieldOptions interface

interface CloudFieldOptions
cover?: number0 clear .. 1 overcast
layers?: LayerKind[]which layers exist (default all); a cheap tier leaves cirrus out
wind?: WindFn
seed?: number

packages/clouds/src/field.ts:48

CloudViewOptions interface

interface CloudViewOptions
tier?: CloudTier
fogDensity?: number
fogColor?: V3

packages/clouds/src/view.ts:51

Puff interface

interface Puff
p: V3
r: numberradius, m
up: number0..1 height in its cloud (the top is lighter)
flat: numbervertical squash of the volume: 1 round, small for a veil
alpha: numberopacity multiplier

packages/clouds/src/field.ts:16

Types

CloudTier type

type CloudTier = 'high' | 'medium' | 'low'

The picture of a CloudField (Babylon is the engine's global BABYLON). One mesh per layer, one draw call each: every puff is a quad billboarded in the vertex shader, lit by the sun (setSun), fading out close to the camera and into the haze far away. The cells within the tier's radius are made again when the camera crosses a cell, the cover changes or the sun has moved enough to change the shading. No texture file: the puff is drawn into a texture here.

packages/clouds/src/view.ts:9

LayerKind type

type LayerKind = 'cumulus' | 'cirrus' | 'stratus'

packages/clouds/src/field.ts:14

V3 type

type V3 = [number, number, number]

The cloud field: clouds as a pure function of position, time and the cover, like terrain, so every peer and every start sees the same sky. No Babylon here: the picture is view.ts, and the same numbers answer the questions of the game (how thick is the cloud at the ship, is this ground point in a cloud's shadow).

Three layers by weather: fair-weather cumulus (clusters of puffs with a flat base and a lighter top), a high thin cirrus veil, a low stratus deck that comes with overcast. Each layer lives in a frame that drifts with the wind at the layer's height (the wind is a function; flowWind in wind.ts gives one from a @ferret/flow field, so a front brings its clouds). The drift is the wind summed over whole seconds, so two peers asking for the same time get the same offset whatever their frame rates.

packages/clouds/src/field.ts:11

WindFn type

type WindFn = (height: number, t: number) => [number, number]

the horizontal wind (m/s) at a height and time

packages/clouds/src/field.ts:13

Constants

LAYERS const

const LAYERS: Record<LayerKind, LayerSpec>

packages/clouds/src/field.ts:35

TIERS const

const TIERS: Record<CloudTier, { radius: number; maxPuffs: number; layers: LayerKind[]; }>

packages/clouds/src/view.ts:10