Render layers
Everything here speaks layer names. No caller builds a bitmask.
The renderLayer toolbox is the whole surface: set and get for membership,
camera and screen for what draws them, list for what exists. From Luau a
tool is invoked as tools.use("<toolbox>", "<tool>", ...); the same calls read
as zero renderLayer set … in the engine shell.
Putting entities on a layer
renderLayer.set writes membership. Targets take an entity name or id, an array
of them, scene.find records, or a query resolved for you, so a selection
can be re-layered without looking its ids up first. Layers are a single name, an
array of names, or a space-separated string.
tools.use("renderLayer", "set", "frontWall", "shell")
tools.use("renderLayer", "set", { "marker_a", "marker_b" }, "debug")
tools.use("renderLayer", "set", { name = { "wall" } }, "shell")
Setting replaces an entity's membership rather than adding to it.
A hierarchy moves in one call, and the reply says how far it reached
A prop, a building or a character is a tree of entities, and the thing you
usually want on a layer is the whole tree rather than its root mesh. tree = true
writes the target and every descendant, in one call:
tools.use("renderLayer", "set", "Mansion", "stage_props", { tree = true })
The reply carries the extent of the write, one record per target:
tools.use("renderLayer", "set", "Mansion", "stage_props")
--> { { id = "ent_7be0…", name = "Mansion", layers = { "stage_props" }, moved = 1, skipped = 46 } }
tools.use("renderLayer", "set", "Mansion", "stage_props", { tree = true })
--> { { id = "ent_7be0…", name = "Mansion", layers = { "stage_props" }, moved = 47, skipped = 0 } }
moved counts the entities that write reached. skipped counts the descendants
it left where they were, so a skipped above zero is the part of the hierarchy
still drawing where it was. The two numbers are what tell a parent-only write
from a whole-building write, which is the question a re-layered hierarchy
usually raises — the reply answers it instead of a walk over the subtree
afterwards. An entity two targets both span is written once and counted once, so
the moved counts sum to the entities the call moved.
Reading membership back
renderLayer.get answers with layer names and takes the same tree option —
one row per entity, which is how a subtree write is checked:
tools.use("renderLayer", "get", "Mansion")
--> { { id = "ent_7be0…", name = "Mansion", layers = { "stage_props" }, skipped = 46 } }
tools.use("renderLayer", "get", "Mansion", { tree = true }) -- 47 rows, one per entity
skipped reads the same way it does on set: the descendants this read left
out. An entity nothing has re-layered reads back as default.
On an entity proxy the same answer is the read-only .renderLayer property, as
a space-separated string, and entity(id):setRenderLayer(names) is the raw
single-entity write behind the tool.
Which layers a camera draws
renderLayer.camera sets a camera's filter, and reads it back when the filter
argument is omitted. The same spec is the Camera component's renderLayers
field: a space-separated list where all seeds every layer, name adds one and
!name drops one. default, ui, debug, sky and EditorUI are built-in
layers.
tools.use("renderLayer", "camera", "stageCam", "all !stage_props") -- hide the props from this camera
tools.use("renderLayer", "camera", "stageCam") -- read the filter back
That is what makes a layer worth having: one write puts a whole prop on
stage_props, and one filter per camera decides which shots contain it.
renderLayer.screen does the same for a UI screen — the layers that screen
draws into — so a HUD stays out of a camera whose filter excludes them.
tools.use("renderLayer", "screen", "hud", "ui")
A prop on its own layer, end to end
-- one prop, spawned as a hierarchy
local prop = entity.spawn("Mansion")
local wing = entity.spawn("wing", { parent = prop })
for i = 1, 3 do
entity.spawn("wall_" .. i, { parent = wing })
end
-- the whole thing onto its own layer, in one call
tools.use("renderLayer", "set", "Mansion", "stage_props", { tree = true })
--> { { name = "Mansion", layers = { "stage_props" }, moved = 5, skipped = 0 } }
-- the stage camera drops it
tools.use("renderLayer", "camera", "stageCam", "all !stage_props")
-- and the read-back names every entity carrying the layer
tools.use("renderLayer", "get", "Mansion", { tree = true }) -- 5 rows, all { "stage_props" }
moved = 5, skipped = 0 is what says the write covered the prop. The same call
without tree answers moved = 1, skipped = 4, and those four entities keep
drawing in the shot the camera excludes.
Seeing what exists
Referencing a layer name that does not exist yet creates it, so "shel"
instead of "shell" produces a real but empty layer that renders nothing and
raises no error. renderLayer.list is where that shows up — every layer in bit
order, the entities on each, and every camera with its filter.
tools.use("renderLayer", "list")
Render layers are not scene layers
wld.layers is a different concept: additive scene composition, the
mechanism for merging one scene's content into another. Render layers govern
what draws.