procNode: one reusable step of procedural generation
MyNode.procNode/
init.luau -- the node
README.md -- what it produces, its inputs, its outputs
.metadata -- description and tags
What a node states
g:describe(text): what the node produces and when to use it. It is the text search, the node catalogue and the Inspector show.g:input(name, default, opts): one input of the node. The default types it (Float,String,Bool,Vec3,Color,List<T>,Record);optsaddstype(for a default that does not state one, or none),description,options,min,maxandoptional. A call names inputs in a table, or passes them in declaration order.- Outputs: the returned table names them (
return { geometry = geometry }), andg:output(name, value)declares one as the node builds. A node with one output answers that value to its caller; a node with several answers a record by output name; a node with none answers nothing. - Written as literals. What a node takes and gives is read from its source without running
it, the way search and description read it here and the way ZeroMind reads it, so each
declaration states its values as written:
g:describeand eachg:inputonce, at the top level of the function, with a literal name, default and options (a file constant holding a literal, orasset.resolve("<identity>", "<type>"), reads as one); outputs named by the table the function returns, or byg:output, the same names on every branch of a condition and never inside a loop.asset.interface(node)answers what the source declares, with each part it cannot read as a problem, and a push refuses a node with such a problem (interface.unreadable). - The body is the node's work: calls to other nodes, arithmetic over sockets,
for, folds, steps and state, as the builder README (proc.module/builder.module/README.md) describes. A node that uses another node is recorded in the graph that calls it, so a node can be built from nodes.
How a graph calls a node
A node inside a <family>.nodebox is called by its box and folder name:
points.nodebox/disc.procNode is g.points.disc({ ... }), and nothing inside the node states
that name. A node is also reached by:
- its path (
g["/zero/source/fx/ring.procNode"]), ~, the nearest enclosing package, effect or world root (g["~/ring"]),- its guid,
- a short name only one node answers to (
g.disc).
A name that matches two nodes and a name that matches none are errors that name the
candidates. Each call is recorded in the calling graph by the node's guid, so the node travels
with every graph that uses it, and a rename or a move keeps the reference. A .procGraph is
called the same way and answers what it returns.
Nodeboxes
A nodebox is the family a node lives in: a namespace folder grouping related nodes (points,
mesh, field, terrain). nodebox.assetType describes it. A node placed in a box takes the
box's name as its prefix; a node outside any box is reached by path, ~ or guid.
asset.create("nodebox", family) makes a box and asset.create("procNode", name, { into = box })
makes a node in it.
Finding nodes
asset.list("procNode") lists every node. The procgen toolbox's searchNodes finds them by
keyword and by meaning and lists them grouped by family, and its describeNode answers one
node's inputs, their types and defaults, and its outputs. Both read what each node's source
declares and run no node. The type's indexing recipe embeds a node's README and its source,
so a search by meaning finds it by its description and by its inputs.
Registration
Every node the asset index lists is known to the procgen registry by what its source declares,
read when it is asked for and read again when the source changes; nothing runs a node to list
it. Its code loads the first time a build runs it. After that, the lifecycle hooks keep it
current: onChange loads it again and hot-reloads on an edit, onDelete removes it. Editing
init.luau cooks again every graph that reaches the node, and only the nodes downstream of the
change do work. init.luau is compiled from its current source on each load, so a module it
pulls in uses an absolute require
(require("@builtin::systems.procgen.proc")). A graph that references a node which is not
registered errors at load, naming the missing guid. A graph that reaches itself, directly or
through other nodes, errors at cook.
Content requirements
README.md carries real text: what the node produces, its inputs and its outputs. .metadata
carries a description (what the node computes and the value type it answers) and tags (the
family, so search finds it).
In the Inspector
Operation: the node's id and what it does, its cost and caching when it declares them, and its inputs and outputs, from the procgen registry's schema for it. A node that did not register says so.