What it does
terrain
System-level entry point for terrainSystem. Published as the global
module via require("~terrainSystem.modules.terrainSystem") —
user code. Wraps the lower-level terrain.* substrate (heightmap →
mesh handle) with entity ergonomics: spawn + attach component, fold
material into asset round-trips, resolve handles by entity id or name.
Exports
Build verbs:
terrainSystem.create(opts) -> TerrainHandle— heightmap → handle. Optionalsave: pathpersists to a.terrainasset.terrainSystem.fromNoise(opts) -> TerrainHandle— deterministic FBM noise → handle. Optionalsave: path.terrainSystem.fromAsset(source) -> TerrainHandle?— load a.terrainasset; nil when unreadable.terrainSystem.toAsset(handle, path, opts?) -> (boolean, string?)— persist a handle to a.terrainasset.
Entity lifecycle:
terrainSystem.spawn(opts) -> entityId: string— spawn an entity with a Terrain component bound to an asset, a handle, or a freshly built procedural terrain. Returns the entity id.terrainSystem.destroy(entityRef) -> boolean— despawn a terrain entity (the on-disk asset is not touched).
Queries on a live terrain entity:
terrainSystem.get(entityRef) -> TerrainHandle?— handle cached on the component.terrainSystem.heightAt(entityRef, x, z) -> number?— bilinear height.terrainSystem.normalAt(entityRef, x, z) -> (nx?, ny?, nz?)— surface normal.terrainSystem.slopeAt(entityRef, x, z) -> number?— slope in radians.
Brush ops (in-place handle mutation; mesh rebuilt):
terrainSystem.raise(handle, opts)/terrainSystem.lower(handle, opts)— radial brush.terrainSystem.flatten(handle, opts)— pull toward a target height.terrainSystem.smooth(handle, opts)— 3x3 box-blur.terrainSystem.setHeights(handle, heights)— wholesale replacement.
Procedural authoring verbs:
terrainSystem.erode(handle, opts)— pseudo-hydraulic erosion.terrainSystem.ridge(handle, opts)— additive mountain ridge along a segment.terrainSystem.plateau(handle, opts)— flatten inside a radius to a target.terrainSystem.ramp(handle, opts)— linear ramp between two altitudes.terrainSystem.valley(handle, opts)— U-shaped channel along a segment.terrainSystem.cone(handle, opts)— radial cone / mountain.terrainSystem.terrace(handle, opts)— quantize heights into stepped levels.terrainSystem.noise(handle, opts)— additive FBM detail overlay.
Instancing:
terrainSystem.scatter(target, opts) -> entityId[]— place mesh instances by slope / altitude / noise / density rules.
Types
TerrainHandle = { mesh, heights, width, depth, sizeX, sizeZ, originX, originY, originZ, name?, material? }EntityRef = string | { entityId?, id?, __id? }ScatterRegion = { box?, disc? }
Usage
Spawn a procedural island and place trees on it:
local eid = terrainSystem.spawn({
name = "ground",
width = 96, depth = 96, sizeX = 80, sizeZ = 80,
seed = 11, octaves = 5, amplitude = 8, ridge = true,
material = "@me::materials.grass",
save = "/source/terrain/island.terrain",
})
terrainSystem.scatter("ground", {
mesh = "@me::props.pine_tree", count = 80,
slopeMax = math.rad(35), altitudeMin = 0, altitudeMax = 18,
scaleRange = { 0.8, 1.4 }, seed = 7,
})
Snap a player to the surface every frame:
local y = terrainSystem.heightAt("ground", px, pz)
if y then entity.find("player").position = { x = px, y = y + 1, z = pz } end
Sculpt a handle programmatically:
local h = terrainSystem.fromNoise({ width = 64, depth = 64, seed = 1 })
terrainSystem.erode(h, { iterations = 8 })
terrainSystem.cone(h, { x = 0, z = 0, radius = 12, height = 8 })
terrainSystem.ridge(h, { x1 = -20, z1 = 0, x2 = 20, z2 = 5, height = 4 })
terrainSystem.toAsset(h, "/source/terrain/sculpted.terrain")
Notes
terrainSystem.spawnis the only function that creates an entity; every other verb either builds a handle (no entity), mutates a handle, or queries an existing terrain entity.- The brush ops accept handles, not entity refs. To brush-edit a live
terrain entity, fetch the handle with
terrainSystem.get(eid), mutate it, then save viaterrainSystem.toAsset(handle, eid_asset_path)so the component reloads from disk. terrainSystem.scatterspawns one entity per accepted point — at very large instance counts prefer baking a single mesh viarenderer.mesh.create.
Discussion
Scoped to this part · feeds back into the world's score.
Sign in to post.sign in
No comments yet. Be the first.