procedural-rock
Rocks made from a seed, with no model file. rockGeometry returns the
positions, normals and indices of one rock. The same seed always gives the same
rock, so a scattered field is reproducible and needs nothing from the network.
b3d-decorator uses it for every boulder and stone: a rule
names a model rock:boulder:3 and the decorator builds it instead of looking
it up in a library. It shades them with the terrain's own biome colours, so a
rock on Mars is Mars-coloured and a rock above the snow line carries snow.
import { rockGeometry } from 'tosijs-3d'
const rock = rockGeometry({ seed: 7, cuts: 9, squash: 0.75 })
// rock.positions, rock.normals, rock.indices
Winding is counter-clockwise (the glTF convention), and Babylon's front face is the other way round. Handed to Babylon as they are, the triangles draw inside out: you see the far wall of the rock through the near one. Swap two indices of each triangle first:
const indices = new Uint16Array(rock.indices.length)
for (let i = 0; i < indices.length; i += 3) {
indices[i] = rock.indices[i]
indices[i + 1] = rock.indices[i + 2]
indices[i + 2] = rock.indices[i + 1]
}
const data = new BABYLON.VertexData()
data.positions = rock.positions
data.normals = rock.normals
data.indices = indices
data.applyToMesh(mesh)
The decorator does this for you.
Names a scatter rule can use
rock:<kind>:<n>, where the kind is one of boulder, tall, stone or
slab (ROCK_KINDS) and n picks the seed. rockNames('boulder', 6) lists
six of them.
Under the decorator, a rock's own sink does nothing: the decorator seats
every model by its lowest point and then buries it by the RULE's sink
(ScatterRule.sink), so that is the one to change.
How a rock is made
- Start from a sphere.
- Cut it with a handful of random planes. This is what makes it read as rock: flat faces meeting at blunt edges, where noise alone gives a potato.
- Push the surface in and out with a little low-frequency noise.
- Stretch it unevenly and squash it, so no two share a silhouette.
- Sink the base a little below
y = 0, so it sits IN the ground.
The result is about one unit across, with its origin on the ground under it: the same convention as a library model, so scatter rules scale it the same way.
Options
| Option | Default | Description |
|---|---|---|
seed |
1 |
Same seed, same rock |
detail |
2 |
Subdivisions of the starting sphere: 1 = 42 vertices, 2 = 162, 3 = 642 |
cuts |
8 |
How many planes cut it. More cuts, more faces |
roughness |
0.07 |
Depth of the noise, as a fraction of the radius |
squash |
0.7 |
Height relative to width. Low values give slabs |
sink |
0.15 |
Fraction of the height that sits below the ground |