Modules / World & ground

scatter3dplugin

Trees, grass and boulders, where each stands a pure function of its cell.

Putting it in a scene

List it after terrain3d, whose ground it stands on, and before render3d. scatter3d has no row: where every instance stands is worked out from its cell, the same on the server and every seat, and a child scene inherits world.scatter3d from the manager scene. Until the land has loaded nothing is drawn.

"plugins": ["loader", "scenemanager", "player", "terrain3d", "scatter3d", "render3d"],
"world": {
  "loader": { "assets": { "firs": { "type": "model", "url": "/games/mygame/assets/firs.glb", "preload": true } } },
  "scatter3d": {
    "mods": { "ground": "terrain3d", "land": "terrain3d" },
    "cutout": 0.5,
    "coverage": 0.3,
    "budget": 3,
    "remember": 50000,
    "packs": {
      "firs": {
        "asset": "firs",
        "rows": { "tall": { "weight": 3, "solid": 0.4, "tall": 14 }, "short": { "weight": 1, "solid": 0.3, "tall": 8 } },
        "cell": 10, "reach": 400, "seed": 11, "lods": [60, 160], "shadow": 120, "leaf": 0.6,
        "density": 0.4,
        "rules": [{ "q": "height", "lo": 105, "hi": 28 }, { "q": "slope", "lo": 0.5, "hi": 0.3 }],
        "clearing": 30, "size": [0.8, 0.5], "vary": 0.15
      },
      "rocks": {
        "shape": { "detail": 2, "lumpy": [0.8, 0.4], "squash": 0.6 },
        "colour": "#7d7a72",
        "rows": { "a": { "weight": 1, "solid": 0.9, "tall": 0.6 }, "b": { "weight": 1, "solid": 0.9, "tall": 0.6 } },
        "cell": 14, "reach": 300, "seed": 4, "lods": [120], "shadow": 80, "leaf": 0,
        "density": 0.15, "rules": [{ "q": "slope", "lo": 0.1, "hi": 0.3 }], "biomes": { "tundra": 1, "forest": 0.4 },
        "clearing": 20, "size": [0.6, 1.4], "vary": 0.1
      }
    }
  },
  "terrain3d": { "mods": { "ready": ["scatter3d"] } }
}

Properties

Read from world.scatter3d. A change to any number rebuilds the field.

.mods : { ground, land, hollows? }

The plugins whose ground the server and the page place on, and the plugins whose boxes clear instances (see Mods).

.cutout : number

The alpha at which a model's blended materials are cut, 0 to 1. Every blended material becomes an alpha-tested card, written to depth. A masked material keeps its own cutoff.

.coverage : number

How much a card's alpha gains per mip level its texture is read at, so far leaves do not thin to sticks as the texture shrinks: alpha × (1 + level × coverage).

.budget : number

Milliseconds a frame the page may spend placing cells newly in reach, at least one cell a frame. Cells nearest the camera go first. Where terrain3d lends its workers, cells are placed there and the budget bounds nothing.

.remember : number

How many cells the server keeps per scene for solids, a whole number above 0. Past it, the one placed longest ago is dropped and placed again, the same, when asked for.

.hollows : number

With a hollows mod, the most boxes cleared at once, the ones nearest the camera. A whole number above 0.

.packs : { [name]: pack }

Each pack is a grid of square cells, at most one instance a cell, drawn round the camera out to its reach.

Packs

What is drawn

KeyMeaning
asset or shapeExactly one. asset is a loader model entry; shape makes boulders (below).
rowsName to { weight, solid?, tall? }: a row of the model, or a variant of the shape. Rows are picked by weight; a row of weight 0 is never drawn.
rows.solidMakes each instance a circle of radius solid × size metres that bodies walk round and cover hides behind.
rows.tallHow high that solid stands over the ground, tall × size metres, for shots along a line. A solid with none stands without end.
colour, surfaceA shape pack's sRGB colour, and its optional rock surface (below).

A model pack's meshes are named <row>-<lod>, or <row>-<lod>-<part> for a LOD in several parts, such as tall-0 and tall-1-leaves. A row that is drawn needs a mesh for every LOD. Every LOD of a row is moved so LOD 0's lowest point is on the ground and its middle on the instance's point.

Where it grows

Cell (i, j) covers [i·cell, (i + 1)·cell) across x and z. The instance's point is jittered over the whole cell. It is kept when a random value for the cell falls under the keep: density × the painted surface's cover × every rule's ramp × the biomes' weight.

KeyMeaning
cellMetres: the width of a cell, above 0.
seedThe seed of the pack's random values. Two packs with one seed and one cell size stand on the same points.
densityThe share of cells kept on open ground, 0 to 1.
rules[{ q, lo, hi }], ramps over terrain3d's land record, each multiplying the keep. An inverted ramp (lo > hi) on height is a treeline. layer:<name> is the weight terrain3d's ground draws that texture layer at, so { "q": "layer:meadow", "lo": 0.5, "hi": 0.85 } places on the meadow that is drawn, and surface:<name> is that surface's painted weight. A surface whose cover is 0, such as a road, keeps nothing.
biomesOptional, biome name to a weight: the pack grows at its weight in each biome it names, none in one it leaves out, and thins across their borders.
clearingMetres from the origin inside which nothing is kept.
Figure 1. A treeline: density 0.3 times the height ramp from lo 105.4 to hi 27.9 metres. Each dot is one of 120 cells along a row at its random value. Filled dots fall under the curve and are kept; open ones are refused.

Each instance

KeyMeaning
size[base, spread]: an instance's scale is base plus a random share of spread.
varyHow far an instance's grey tint strays from 1, either way.

Each instance is turned to a random yaw, tilted toward the ground's slope and sunk until it touches. How far it leans is its foot's width over its height, at most 1: a tussock lies along the slope and a fir stands up straight.

Figure 2. Bedding on a slope, side on. A tuft as wide as it is high leans fully onto the slope and touches at its edge. A fir whose foot is 0.07 of its height stays upright and touches at its foot. Each is sunk until its ring touches the ground on the downhill side.

How it is drawn

KeyMeaning
reachMetres from the camera the pack is drawn out to.
lodsMetres, rising from above 0 to under reach: the pack is drawn in lods.length + 1 square shells round the camera's cell. Shell k reaches ⌈lods[k] / cell⌉ cells and the last ⌈reach / cell⌉, and LOD k is drawn in shell k. An instance changes LOD only when the camera crosses into another cell.
shadowMetres: a shell that ends within this casts shadows. Every shell receives them.
leaf0 to 1: how far a card's normal is bent toward the sky, so a crown of cards is lit, and takes snow, as one mass.
Figure 3. The shells of a pack with cell 10, lods [60] and reach 200. Shell 0 reaches ⌈60 / 10⌉ = 6 cells from the camera's cell and holds 169 cells in LOD 0; shell 1 reaches ⌈200 / 10⌉ = 20 cells and holds 1512 in LOD 1. A cell's distance is the larger of its x and z distances.

Boulders

shape: { detail, lumpy: [base, range], squash }. Each row is a variant: a sphere of radius 1 subdivided detail times, each vertex pushed out to between base and base + range of itself, then its height multiplied by squash, shaded face by face. Its middle is at the ground, so half of it is buried.

Without a surface, it is drawn in colour. A surface mixes two noises, n = a × noise at broad + b × noise at fine, with mix [a, b] and the frequencies in cycles a metre:

KeyMeaning
lift, rangeThe colour is multiplied by 1 + range × (n − 0.5), mixed in by lift.
bumpThe normal bent as by a height of n × bump / fine metres, so a bump looks the same at any fine.
moss{ from, to, amount, up: [lo, hi], colour }: moss of that colour where the noise rises from from to to, times amount, only on faces whose normal points up between lo and hi.
roughThe roughness.

Methods

Rules

From #plugins/scatter3d/scatter3d.sim.ts.

solids( w, x, z, r, ground ) : { x, z, radius, tall? }[]

Every kept instance of a row with solid whose circle reaches within r metres of (x, z). tall is given when the row gives one. A stroke on the ground drops the cells it reaches, so they are placed again on the new ground.

placed( w ) : ground

The ground mods.ground names, to hand to solids or place. Throws when the scene names none.

place( pack, i, j, ground ) : { i, j, x, z, row, size, yaw, tint } | undefined

Cell (i, j)'s instance, its point in metres and its yaw in radians, or undefined when the cell is refused.

On the page

Through plugins.scatter3d.

solids( x, z, r ) : { x, z, radius, tall? }[]

The same as the rule's, over this page's land.

ready( ) : boolean

Whether every cell in reach of terrain3d's prepared place is placed. True with no place in hand.

packs( ) : { [name]: object }

Each built pack: at, the camera's cell; held, its cells; pending, the cells waiting to be placed; looks, its instanced meshes by "<row> <shell>"; models, each drawn row's LODs with its foot, edge, rise and lean; and ahead, the prepared place's cells.

cellKey( i, j ) : number

The key a cell is held under in held.

Mods

Name them in world.scatter3d.mods.

  • terrain3d
    ground on the server and land on the page: the height, the painted cover, the record ramps and what has changed. List terrain3d before scatter3d.
  • box3dvehicle
    hollows: a car's body and wheels, so grass it stands in is not drawn through it.

Used by

  • terrain3d
    Waits for scatter3d's ready before a prepared place is done.
  • cover3d
    Finds cover behind its solid instances.
  • walker
    Walks bodies round its solid instances.
  • hitscan3d
    Stops shots at its solid instances, up to their tall.

In the guide