ReadonlytextureThe GPU texture sampled by climate-aware shader materials.
ReadonlywidthReadonlyheightThe options that reproduce the base temperature field, recorded by
generateMap when it fills this instance (including the jitterChannel it
picked at random). Save this object — it is tens of bytes — and
ClimateData.fromMap rebuilds the base tier exactly on load, which
is why serialize does not write the dense field.
Null when the base fields were supplied some other way.
Raw RGBA bytes; index as [(row * width + col) * 4].
True if any channel changed since the last call to update.
StaticfromBuilds climate for a map by running the same computeTemperature the
generator does. Use this on a loaded or hand-authored map that has no
climate attached — pass the same options the map was generated with
(including jitterChannel) and the field comes back bit-identical, which
is why the dense temperature field never needs to be saved.
Writes the base temperature field (0–1, indexed row * width + col).
Writes the base moisture field (0–1, indexed row * width + col).
Base temperature at a cell, 0 (polar) – 1 (equatorial). Out-of-bounds reads 0.
Base moisture at a cell, 0–1. Out-of-bounds reads 0.
Snow lying on a cell, 0 (bare) – 1 (deep). Out-of-bounds reads 0.
The cell's temperature this season — base temperature after the year's
swing, on the same 0–1 scale. This is what a liquid's freezePoint is
compared against, and what the shaders read out of the A channel.
Out-of-bounds reads 0.
Whether a liquid with the given freeze point has frozen at this cell — the
gameplay question ("can the caravan cross here?"). Pass the descriptor's
freezePoint; undefined never freezes, matching how the shaders read a
negative uFreezePoint.
OptionalfreezePoint: numberOverwrite one cell's snow depth (0–1). Use for scripted or gameplay-driven snow.
Overwrite one cell's season-adjusted temperature (0–1).
Marks the texture for re-upload. SeasonCycle.apply calls this for you; call it yourself after a batch of setSnowDepth writes.
Call once per frame before rendering — uploads the texture if anything changed.
Serializes the seasonal tier — snow depth and season-adjusted temperature — as a run-length blob.
The base fields are deliberately not written: they are a deterministic function of the map and its TemperatureModelOptions, so saving the options (tens of bytes) and calling ClimateData.fromMap on load reproduces them exactly, where the dense field would cost a byte per cell. Snow is genuine runtime state that a campaign accumulates, and both channels run-length well — a summer map collapses to a handful of bytes.
Restores a seasonal tier written by serialize, leaving the base temperature and moisture fields untouched.
Throws if the blob is not climate data, is a newer format version, or was saved for a differently-sized map.
The serialize blob as base64 text, for JSON saves and localStorage.
Clears the seasonal tier back to bare ground and open water; base fields are kept.
Clears every channel, base fields included.
Per-cell climate state for a hex map, in one GPU-sampled texture.
Two tiers, mirroring how FogData splits live and remembered state. Base is the map's static climate — the temperature and moisture fields the generator computed, which never change for a given map. Seasonal is the derived state a SeasonCycle rewrites as the year turns: how deep the snow lies and how cold it has become.
The split is what makes seasons cheap. The base fields are written once; the seasonal pass reads them, applies the year's phase, and writes two bytes per cell. Nothing regenerates and no geometry rebuilds.
State is stored as a
DataTexture(RGBA) that shader materials sample by thecellIndexattribute every hex-world geometry already carries: R = base temperature (0–1), G = base moisture (0–1), B = snow depth (0–1), A = season-adjusted temperature (0–1).Note what is not here: whether the water has frozen. Every liquid freezes at its own
freezePoint, so a single "is frozen" byte could only ever be right about one of them. The season writes the temperature instead and each liquid — and isFrozen — reads its own answer out of it.The CPU side of every channel is readable through temperature, moisture, snowDepth and effectiveTemperature — deliberately the same bytes the shaders sample, so a game's "is this hex snowed in" answer and the white pixels on screen can never drift apart.
Example