Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

README.md

reflectionProbe Module

Reflection-probe system — multiple proximity-blended reflection probes in a scene. Each probe bakes the scene into its own cube slot from its position; surfaces reflect the probes covering them, blended by proximity and rank (the renderer's per-fragment probe blend), with the scene's sky under whatever coverage the probes leave. Auto-injected as _G.reflectionProbe via the prelude.

Purpose

Policy lives here: slot assignment (max 8 cube slots) and the active-probe list pushed to the renderer. The engine only provides the generic per-slot capture + the proximity-blend buffer (via the environment global). The ReflectionProbe component drives this registry from its lifecycle; agents/editors can also drive it directly (add / bake / bakeAll / list).

Rank and the sky under it

Probes are gathered one rank at a time, highest priority first, and each rank takes the coverage the ranks above it left. A small interior probe ranked above the large exterior one it sits inside therefore wins outright wherever it reaches full weight, while probes of equal rank crossfade by proximity. Coverage no probe claims reflects the scene's sky, captured into its own slot by ensureSkyFallback() (which bakeAll runs, and the ReflectionProbe component runs once its probe holds a cube), so a reflective surface outside every probe's radius shows the sky rather than black. The capture follows the sky from there: change the sky and the slot is retaken from where it was captured.

A probe reaches surfaces only once it holds a cube, baked by bake / bakeAll or loaded from its persisted bake by loadBaked. An unbaked probe keeps its slot and is listed with baked = false, and adds nothing to the frame; the ReflectionProbe component arms its sky fallback once the probe holds a cube. lighting.get reports the same state in its environment block, beside whether the sky fallback is armed, so a surface that stays lit with every light at zero names what it is reflecting.

Each probe persists its baked cube at probe_<key>.texture (key = the probe's probeId, falling back to the entity id) and loads that asset at runtime instead of re-baking — baking is an authoring step.

Usage

-- One-liner authoring: spawn a probe entity (does NOT bake on add).
local id = reflectionProbe.add(0, 3, 0, { radius = 15, probeId = "lobby" })

-- One-call bake: bake EVERY registered probe once the scene is built.
-- Yields a few frames (geometry must be GPU-resident) — call from a
-- task/coroutine/execute context.
reflectionProbe.bakeAll()

-- Inspect / tweak
for _, p in ipairs(reflectionProbe.list()) do print(p.id, p.slot, p.radius, p.baked) end
reflectionProbe.setRadius(id, 20)
reflectionProbe.setPriority(id, 1)   -- outranks the probes it overlaps
reflectionProbe.apply()   -- re-push live positions/radii after moving a probe

Exports

  • reflectionProbe.add(x, y, z, opts?) -> string — spawn a probe entity (opts { radius = 12, probeId = "stable-key", name }); returns the entity id
  • reflectionProbe.bakeAll() -> { baked, failed, errors } — bake + persist every registered probe
  • reflectionProbe.bake(id) -> (string?, string?) — bake one probe from its position + persist; returns the asset path or (nil, err)
  • reflectionProbe.loadBaked(id) -> boolean — load the persisted baked cube into the probe's slot (the runtime path; false when none exists)
  • reflectionProbe.register(id, radius, key?) -> number? — assign a free cube slot (idempotent; called by the component's awake)
  • reflectionProbe.unregister(id) — free the probe's slot
  • reflectionProbe.setRadius(id, radius) — update influence radius
  • reflectionProbe.setPriority(id, priority) — set the probe's blend rank against the probes it overlaps (default 0)
  • reflectionProbe.ensureSkyFallback() -> boolean — put the scene's sky in the fallback slot: capture it when the slot holds none, re-arm it when it does
  • reflectionProbe.apply() -> number — push the current active-probe blend data to the renderer
  • reflectionProbe.isBaked(id) -> boolean - whether the probe holds a cube it reflects
  • reflectionProbe.baking(id?) -> boolean - whether a bake under way will reach probe id (its own bake, or a bakeAll with it still to reach); with no id, whether any bake is under way
  • reflectionProbe.list() -> { { id, slot, radius, priority, baked, asset, position } }
  • reflectionProbe.count() -> number