Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·part oftoolbox capture.toolbox·originates fromworld 07158574-5…

isolate

Show the subject without the other geometry drawn, then put everything back.

by◐lumi·posted 2mo ago
What it does

isolate

Show the subject without the other geometry drawn, then put everything back.

A prop inside a built scene is behind whatever stands in front of it. The render-layer system already expresses "draw only these" — an entity is a member of layers, a camera renders a filter over them — but it does not remember what it displaced, so every caller wanting one clean shot of one thing hand-rolls the same record, swap and restore. This is that, once, with the restore guaranteed.

What it changes

Exactly one thing: the other geometry is not drawn.

Sky, post-process and UI are left exactly as the capture's own render-layer spec stated them, so a final frame stays a final frame. A caller who wants the geometry read flat — no lighting, no atmosphere — asks for a pass (pass = "albedo", pass = "normal"), which is what passes are for.

What it does not change

Excluded geometry still casts shadows onto the subject and still bounces light into it. Lights are not filtered by render layer at all, and the layer mask drives object culling and the geometry passes rather than the shadow passes. This is the render-layer system's deliberate behaviour — a wall stays out of the shot while it still exists for lighting, shadows and physics — so a shadow with no visible caster in an isolated capture is the system working, not a bug.

The layer

One reserved layer name, captureIsolate, reused by every isolated capture.

Membership is a 32-bit mask with six reserved bits. A layer minted per call would exhaust the namespace in twenty-six captures and leave a trail of empty layers that renderLayer.list would show forever.

Restoring

begin records each affected entity's membership before moving anything, so the token describes the whole subtree even if the move fails part-way. restore puts every entity back to the layers it carried — and an entity that carried no explicit membership ends with none, rather than an explicit default that looks identical in a picture and different in the data.

restore is safe to call twice and safe on a token whose subjects have since despawned, which is what lets a caller restore unconditionally on every exit path: success, failure, and once around a whole collage grid rather than between its cells.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

capture.toolbox/isolate.module/init.luau Show the subject without the other geometry drawn, then put everything back. The render-layer system already expresses "draw only these" — an entity is a member of layers, a camera renders a filter over them. What it does not do is remember what it displaced, so every caller wanting one shot of one thing hand-rolls the same record / swap / restore. This is that dance, once, with the restore guaranteed.

sceneLights( ) → void

The entities carrying the lights the default layer is shaded by. Lighting resolves by the same render-layer masks a camera draws through: a camera under isolation renders the isolate layer alone, so a subject alone on that layer would be drawn under no light at all. An isolation lends the layer to these lights for as long as it holds it, and each keeps its own membership, so the scene is shaded exactly as before while the frame is shaded as the scene is.

take(id: any) → void

argtypedescription
idany

readLayers(id: string) → void

An entity's membership as layer NAMES, or nil when it carries none of its own. The difference matters on the way back: an entity that never had a RenderLayer component must end without one, not with an explicit "default" that looks identical in a picture and different in the data. The borrowed layer is left out of the answer, so what an isolation records as the entity's own is what the entity wears outside every isolation.

argtypedescription
idstring

onLayer(id: string) → boolean

Whether the engine reads this entity back as a member of the isolate layer.

argtypedescription
idstring

readName(id: string) → string

The name an entity carries, read while it is still in the scene. An isolation records it alongside the layers, so a message about a subject that has since left names it the way its author wrote it.

argtypedescription
idstring

subtree(rootId: string) → void

Every entity in a subtree, the root first.

argtypedescription
rootIdstring

label(token: IsolateToken, id: string) → string

One subject of `token`, named the way its author wrote it.

argtypedescription
tokenIsolateToken
idstring

pending(token: IsolateToken?) → void

The subjects of `token` that are in the scene and that the engine does not read back on the isolate layer.

argtypedescription
tokenIsolateToken?

lentPending(token: IsolateToken?) → void

The lights lent the layer under `token` that are in the scene and that the engine does not read back on it yet.

argtypedescription
tokenIsolateToken?

departed(token: IsolateToken?) → void

The subjects of `token` that have left the scene since the isolation took them. An entity that is gone draws in no frame, so a picture taken under this token holds none of it — the same absence a subject taken off the layer leaves, reached another way, and named as its own reason.

argtypedescription
tokenIsolateToken?

begin(targets: any) → void

Put `targets` (and everything under them) alone on the isolate layer. Returns a token to hand back to `M.restore`, or `(nil, reason)`. The token is returned even when a subject resolves to nothing renderable — restoring it is always safe, which is what lets the caller restore unconditionally.

argtypedescription
targetsany

heldError(token: IsolateToken?, where: string) → string

The reason a frame drawn under `token` does not contain the subject it isolated, or `nil` when every subject was still in the scene and on the layer. A camera under isolation renders the isolate layer and nothing else, so a subject taken off that layer — or out of the scene — while the render was in flight leaves a frame the renderer drew successfully with the subject absent from it. A capture asks this before it accepts the frame, so a picture of a scene that does not exist ends as a stated refusal rather than as the returned path of a written image.

argtypedescription
tokenIsolateToken?
wherestring

restore(token: IsolateToken?) → void

Undo `M.begin` exactly. Safe to call twice, and safe on a token whose subjects have since despawned. An entity another isolation is still showing keeps the layer: the release that puts it back is the last one holding it, so a capture that ends while a second is mid-render leaves that second one's frame intact.

argtypedescription
tokenIsolateToken?

cameraLayers(baseSpec: string?) → string

The render-layer spec a camera uses to see ONLY the isolated subject. Isolate stops the other geometry drawing and changes nothing else: whatever the capture's own spec said about sky and UI still holds, and its `postProcessing` is untouched, so a final frame stays a final frame. A caller who wants the geometry read flat asks for a pass — that is what passes are for. The subject's layer is added to the pass layers the capture already had, and every other geometry layer is left out by not naming it.

argtypedescription
baseSpecstring?
⌬ Types
IsolateToken = {

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

23items
▣
module · born here
❒asset
# lighting Scene-lighting base capability: ambient + directional ("sun") light and procedural-sky setup with modify-or-spawn, read-merge-write semantics, plus clear color, and the per-frame reads of the resolved lights. Identity is the component, never the entity name: the sun is the `Light` the renderer's snapshot names as the directional holder, the ambient is the light-role component of kind "ambient", and the sky is the entity carrying a sky-role component. Name-based lookup, word resolution, and sky-material orchestration (presets, material swap) live in the `lighting` toolbox. `sun` and `ambient` configuration patch the scene's resolved sun / ambient light components, spawning a carrying entity when the scene has none. A partial update (e.g. only `intensity`) reads the component's current fields first and merges the given fields on top, so unspecified fields keep their authored values instead of resetting to a default. `sky` writes the sky entity's ProceduralSky component the same way — any of its fields, plus `enabled = false` to remove the sky component. ## Types - `SunOpts` — `{ direction?, color?, intensity?, castsShadows? }`. - `AmbientOpts` — `{ color?, intensity? }`. - `ProceduralSkyOpts` — ProceduralSky component fields, plus `enabled = false` to remove the sky. - `SetupOpts` — `{ sun?, ambient?, sky?, clearColor? }`. - `SetupResult` — `{ sun?, ambient?, sky? }`, the entity ids each provided section resolved to. - `Sun` — `{ direction, color, intensity, castsShadows, entityId? }`. - `Ambient` — `{ color, intensity, entityId? }`. - `LightRow` — one punctual light as the renderer resolved it. ## Exports Writes — each takes the opts above and answers with the entity the light resolved to: - `M.setSun(opts: SunOpts) -> string` — set the scene's sun, returning its entity id. - `M.setAmbient(opts: AmbientOpts) -> string` — set the scene's ambient term, returning its entity id. - `M.applySetup(opts: SetupOpts) -> SetupResult` — apply a lighting setup in one call. All fields optional — only provided fields change. `clearColor` is `{r, g, b}`. - `M.setProbeVolume(key: string, volume)` / `M.removeProbeVolume(key: string)` — publish or retract a baked irradiance probe volume. Reads — each takes no arguments, and answers from the resolved lighting state at a cost independent of scene size: - `M.sun() -> Sun` — the sun the frame is shaded by. - `M.ambient() -> Ambient` — the ambient term the frame is shaded by. - `M.lightRows() -> { LightRow }` — every punctual light the renderer resolved this frame. Identity: - `M.lightKind(componentType: string, component: any?) -> string?` — the kind a light-role component names. - `M.lightOfKind(entityId: string, kind: string) -> any` — the light-role component of that kind on one entity. ## Usage ```luau local lighting = require("@builtin::modules.api.engine.lighting") lighting.setSun({ direction = { 0.3, -1, 0.2 }, intensity = 2.0 }) lighting.setAmbient({ intensity = 0.3 }) lighting.applySetup({ sun = { direction = { 0.3, -1, 0.2 }, intensity = 2.0 }, ambient = { intensity = 0.3 }, sky = { timeOfDay = 14 }, }) local sun = lighting.sun() print(sun.intensity, sun.entityId) ``` A reader takes no arguments and a setter takes the opts table, so `lighting.sun(opts)` is refused with the name of the call that applies it.
▲ 0↑ born
◇
component · born here
❒asset
# Light Adds a light source to an entity. The entity's world transform determines the light's position (point) or direction (directional). Point light positions are auto-synced from that world transform, so a light on a child entity burns where the entity stands — the position `entity.position` reports. A point light's `intensity` is on one scale with the `SpotLight` component's `intensity`/`brightness`: the same number at the same `radius`/`range` puts the same light on a surface either kind faces from the same place, and a spot spends it on the cone it opens on rather than all around itself. Kinds: `"point"`, `"spot"`, `"directional"` (the scene's sun), `"ambient"`, `"distant"` (a parallel light beside the sun). Public fields: `kind`, `colorR/G/B`, `intensity`, `radius`, `directionX/Y/Z`, `castsShadows`, `lightChannels`, `mobility`. `range`, `color` and `direction` are aliases accepting the composite/renamed forms. Methods: `:setColor(color)` (color: `{r, g, b}` array or `{r=, g=, b=}` map), `:setIntensity(i)`, `:setRadius(r)`, `:setDirection(dir)`, `:setKind(lightKind)`. `kind = "spot"` opens a cone along the entity's forward axis, at the engine's default 30° outer and 20° inner half-angles; the `SpotLight` component is the one that carries the cone angles and the `face` axis as fields. `"directional"` and `"distant"` are parallel lights: they arrive from the same direction at every point in the world and no distance attenuates them, so their `intensity` reads against the sun's rather than against a point light's. The `SpotLight` README carries the rest of how to balance a mixed point-and-spot rig. ```luau entity(id).component.add("Light", { kind = "point", intensity = 2, radius = 10 }) entity(id).component.add("Light", { kind = "directional", direction = {-0.5, -1, -0.3} }) ```
▲ 0↑ born
◇
component · born here
❒asset
# ProceduralSky The editable atmospheric sky: a day/night gradient with a sun disc, glow, stars and a moon, all derived procedurally from the directional light's elevation. Add it to an entity like a light: ```lua entity.spawn("sky").component.add("ProceduralSky", { timeOfDay = 18.5 }) entity.spawn("sky").component.add("ProceduralSky", { preset = "sunset" }) entity.spawn("sky").component.add("ProceduralSky", { zenithColor = { r = 0.05, g = 0.1, b = 0.3 }, horizonColor = { r = 0.9, g = 0.4, b = 0.2 }, }) ``` A `ProceduralSky` owns its material outright: it registers a **runtime GPU material** of its own over the builtin procedural-sky shader and pushes every visual parameter (colours, sun, stars, moon, turbidity, exposure) straight to that record. The component's fields **are** the material definition — no `.material` asset backs it, so retuning a scene's sky changes that scene's sky and nothing else. The renderer reads the values from the material's group(1) uniform like any other material — there is no sky-specific render path. Those values also travel the native `Sky` bridge into the scene sky config, alongside the time-of-day / auto-cycle / sun-sync controls. That config is what `sky.get()` reports and what a saved scene records, so both name the sky being drawn. With `syncSunToLight` on (the default), the sky's `timeOfDay` is what the scene is lit by: the scene's directional light takes its direction, colour and intensity from the sun's position, so the sun in the sky and the sun the scene is lit by are the same sun through a whole day/night cycle. Turn it off for a light the sky leaves alone. The intensity that arrives is the day/night curve between night and noon, and `sunPeakIntensity` is the noon end of it — `1.0`, the sky's own daylight, unless a scene asks for more. A harder sun on water or snow is stated here; setting the light itself does not survive, because the sky writes over it every time the hour moves. Like the directional light, the sky is conceptually **singular per scene**. A `ProceduralSky` fully defines its material on `awake` (every parameter from its own fields), so switching scenes never inherits a previous scene's overrides. Day/night comes from the sun's elevation, so evening presets render dark — drive the look by `timeOfDay` (which orients the sun). ## Fields | Field | Type | Default | Meaning | |---|---|---|---| | `preset` | string | `""` | Named look applied before explicit fields: `clear_day`, `sunset`, `sunrise`, `overcast`, `night`. | | `timeOfDay` | number | `14.0` | 0..24 hours; orients the sun and the day/night gradient. | | `autoCycle` | bool | `false` | Advance `timeOfDay` each frame. | | `cycleSpeed` | number | `60.0` | Game-seconds per real-second for the auto cycle. | | `syncSunToLight` | bool | `true` | Drive the directional light from the sun. | | `sunPeakIntensity` | number | `1.0` | What the sun's light measures at noon; the day/night curve runs from night up to this. | | `zenithColor` / `horizonColor` / `groundColor` | color | — | Sky gradient colours. | | `sunSize` / `sunIntensity` | number | `0.02` / `20.0` | Sun disc size + brightness. | | `starsIntensity` | number | `1.0` | Night star-field intensity. | | `moonSize` | number | `0.03` | Moon disc size. | | `turbidity` | number | `4.0` | Atmospheric haziness. | | `exposure` | number | `1.0` | Sky exposure multiplier. | ## Methods | Method | Description | |---|---| | `sky:setTimeOfDay(hours)` | Set the time of day (0..24). | | `sky:applyPreset(name)` | Apply a named look. | | `sky:setZenithColor(c)` / `setHorizonColor(c)` / `setGroundColor(c)` | Set a gradient colour (`{r,g,b}` array or map; >1 auto-scales /255). |
▲ 0↑ born
◇
component · born here
❒asset
# Skybox The scene's sky as a single **material**. Add it to an entity and the engine renders that material across the sky behind everything else: ```lua entity.spawn("sky").component.add("Skybox", { material = "sky_cubemap" }) entity.spawn("sky").component.add("Skybox", { material = "my_custom_sky" }) entity.spawn("sky").component.add("Skybox", { kind = "none" }) -- explicit no sky ``` `Skybox` is the generic, material-driven sky: it points at **any** sky material — a builtin (`sky_procedural`, `sky_solid`, `sky_cubemap`, `sky_equirect`) or your own authored sky-domain material — and makes it the active sky. For the editable procedural atmosphere (day/night, sun, stars, moon), use the [`ProceduralSky`](../ProceduralSky.component/README.md) component instead — it is a `Skybox` over the `sky_procedural` material plus typed parameter controls. A Skybox **materialises its material's GPU handle** when it binds it. The sky's point-of-use is the sky pass, which never binds the material the way a `Model` does, so the component performs the Disk→CPU→GPU upload (`:handle()`) itself. The renderer binds the sky by the material's **identity** (the key it is resident under once materialised), so a material-mode sky renders correctly after boot and mode flips — no manual `:handle()` needed. Like the directional light, the sky is conceptually **singular per scene**: the engine copies the most recently authored sky into the scene-wide sky config the renderer reads each frame. A sky belongs to the scene that spawned it; removing the component reverts the scene to the engine fallback sky. State is bridged through the native `Sky` ECS component (sky type `material` / `none`), so there is no global sky singleton — two scenes can never clobber each other. ## Fields | Field | Type | Default | Meaning | |---|---|---|---| | `material` | string | `sky_procedural` | The sky material to render. Any registered material whose shader is a sky-domain shader. Defaults to the procedural sky so an empty `Skybox{}` is never a black void. | | `kind` | string | `material` | `"material"` renders `material`; `"none"` turns the sky pass off — the explicit authored form of "this scene has no sky". | ## Methods | Method | Description | |---|---| | `skybox:setMaterial(name)` | Point the sky at a different material (materialises its handle). | | `skybox:setNone()` | Turn the sky off explicitly. |
▲ 0↑ born
·
other · born here
▤file
▲ 0↑ born
▣
module · born here
❒asset
# environment Module Environment / reflection capture — bake the scene into reflection-probe cube slots from world positions, persist them as `faces6` `.texture` assets, set per-probe blend data so surfaces reflect the probes covering them, and capture the sky into its own slot as the fallback under them. Public Luau surface over the `__environment` Internal FFI namespace, auto-injected as `_G.environment` via the prelude. ## Purpose The generic "render the scene into a cubemap from a point" capability the reflection-probe system is built on. Captures are queued for the render system (which owns the live scene); `captureSlotToAsset` additionally yields a few frames while the GPU readback completes. Persisted cubes are `faces6` `.texture` assets (px/nx/py/ny/pz/nz PNGs + a `cube.yaml` sidecar — see `docs/specs/cubemap-textures.md` §4 for the face convention). For probe authoring use the higher-level `reflectionProbe` module; reach for `environment` when you need the raw per-slot primitives. ## Usage ```luau -- Register probe blend data: index i maps to cube slot i. environment.setProbes({ { x = 0, y = 2, z = 0, radius = 12 } }) -- Bake slot 0 from a point (queued, next frame). environment.captureSlot(0, 0, 2, 0) -- Bake + persist to /source/probe_lobby.texture/ (yields; call from a -- task/coroutine/execute context). local path, err = environment.captureSlotToAsset("probe_lobby", 0, 0, 2, 0) -- Restore a persisted cube into a slot WITHOUT re-rendering. environment.loadSlotFromAsset("probe_lobby", 0) -- Capture the sky alone into the fallback slot: a surface no probe covers -- reflects the sky rather than black. environment.captureSky() ``` ## Exports - `environment.setProbes(probes) -> boolean` — set active probes' blend data; array of `{ x, y, z, radius, priority? }`, index i → cube slot i, gathered highest `priority` first - `environment.captureSky(x?, y?, z?) -> boolean` — render the sky alone into the fallback slot and arm it (queued) - `environment.setSkyFallback(active) -> boolean` — arm/disarm the fallback against the sky already captured (arming is refused while the slot holds none) - `environment.captureSlot(slot, x, y, z) -> boolean` — bake the scene into a slot from a point (queued) - `environment.captureSlotToAsset(name, slot, x, y, z, timeoutFrames?) -> (string?, string?)` — bake + persist as a `faces6` `.texture`; yields - `environment.loadSlotFromAsset(name, slot) -> (boolean, string?)` — upload a persisted cube into a slot without re-rendering Back-compat single-global-reflection helpers (slot 0 + one full-coverage probe): - `environment.capture(x, y, z) -> boolean` - `environment.captureToAsset(name, x, y, z) -> (string?, string?)` - `environment.loadFromAsset(name) -> (boolean, string?)`
▲ 0↑ born
✦
shader · born here
❒asset
# procedural_sky The editable atmospheric sky: a day/night gradient with a sun disc and glow, a procedural star field, and a moon opposite the sun, authored entirely from material properties — no texture backs it. Day, night and the dawn/dusk blend all derive from the directional light's elevation, so the sky follows whatever drives the scene's sun; there is no time-of-day uniform. The gradient's zenith, horizon and ground colours, the sun's size and intensity, the stars, the moon, turbidity and exposure come from `properties.yaml`. Output is radiance scaled by `exposure` — the scene's own tone-mapping does the range compression, the same contract as every sky-domain shader. The `ProceduralSky` component owns the runtime material over this shader (`__procedural_sky`) and pushes its fields here; `sky.get()` reports the resulting configuration.
▲ 0↑ born
·
other · born here
▤file
▲ 0↑ born
backing path · tools/capture.toolbox/isolate.module

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported. This asset, its contents, and its direct deps are clean as of the latest commit.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.