AreaLight Component
Adds a rectangular light source to an entity. The rect's centre is the
entity's world position and its facing normal is the entity's world rotation
applied to the `face` direction (defaults to "Front", the entity-local -Z
that `transform.forward` reads and that `entity:lookAt` aims); the rect spans
`width` by `height` in the plane perpendicular to that normal, and a rect
under a carrier moves and turns with it.
A rect lights differently from a point or a spot. Its light arrives from a
surface rather than a single spot, so shading falls off gradually across a
wall instead of radiating from a dot, highlights stretch to the shape of the
source, and shadows carry a penumbra that widens with the rect's size and
with how far the caster stands from what it shadows.
Config is the native Light ECS component with `lightType = "Area"` — area,
point, and spot lights share one GPU buffer, so the per-scene cap is their
combined count.
Usage:
entity.find("ceilingPanel").component.add("AreaLight", {
color = {1, 0.95, 0.85},
brightness = 6,
width = 6,
height = 8,
range = 30, -- how far the light reaches
face = "Bottom", -- the rect points down at the room
shadows = true,
})
contributionWithheld( ) → void
A baked static light contributes nothing live: the baked artifacts already
carry all of it, so realtime light would double it and its shadow map would
re-render every frame for nothing. A mixed light keeps its direct light and
shadows — only its bounce is baked.
areaValue( ) → void
Build the typed ecs.Light value for this rect. A withheld light keeps its
row and zeroes what the row carries, so the scene still reads as having a
light here.
componentIsLive( ) → boolean
Bring the live row to match the component. Idempotent, so re-entrant
property notifications can't double-insert.
Whether this component is running: its own switch is on, and the entity
carrying it is active in the hierarchy. A component that is not running
holds no row — `onDisable` and the active cascade each take it out — so a
write that lands while it is off changes the authored value and nothing
else, and `onEnable` builds the row back from whatever the fields hold by
then. Read guarded: an entity mid-teardown answers nothing.
applyAuthored( ) → void
Apply an authored change, un-baking first. Public setters route through
this so editing a baked light restores its live contribution.
onPropertyChanged(key: ?, value: ?, oldValue: ?) → void
| arg | type | description |
|---|
| key | ? | |
| value | ? | |
| oldValue | ? | |
resolveMobility( ) → string
Resolve this light's mobility to `"static"`, `"mixed"` or
`"dynamic"` — how GI baking treats it. An explicit `mobility` field
wins; `"auto"` resolves to `"dynamic"` for a light something carries
(non-world participation, an animated or physics-driven entity) and
`"mixed"` for the rest.
`"static"` bakes the light whole — direct light and bounce — and
withholds its live contribution, so it costs nothing per frame and
lights nothing that was not there at bake time.
`"mixed"` bakes only its bounce and keeps its direct light and shadows
live, so it still lights and shadows anything that moves. This is what
`"auto"` picks, because a light that stands still still shines on
characters walking under it.
`"dynamic"` keeps the light out of the bake entirely.
examples
local mob = panel:resolveMobility()
lightMobility( ) → string
How GI baking should treat this light, resolved to `"static"`,
`"mixed"` or `"dynamic"`. Every component that puts a light in the
scene answers this, which is how a bake finds the lights it has to
account for without knowing what component authored them.
examples
if light:lightMobility() == "dynamic" then ... end
setColor(color: table) → void
Set the light color. Values >1 are auto-scaled from 0..255.
| arg | type | description |
|---|
| color | table | `{r, g, b}` array or `{r=, g=, b=}` map. |
examples
panel:setColor({1, 0.95, 0.85})setBrightness(b: number) → void
Set the brightness multiplier.
| arg | type | description |
|---|
| b | number | Brightness scalar. |
examples
panel:setBrightness(6)
setSize(w: number, h: number) → void
Set the rect's size in world units.
| arg | type | description |
|---|
| w | number | Width across the face. |
| h | number | Height across the face. |
examples
panel:setSize(6, 8)
setRange(r: number) → void
Set how far the light reaches.
| arg | type | description |
|---|
| r | number | Range in world units. |
examples
panel:setRange(30)
setFace(face: string) → void
Set the entity-local face the rect points along. One of Front, Back,
Left, Right, Top, Bottom — the world-space normal is `transform.rotation *
face`, and "Front" is the entity's own forward, so a rect left on it
follows wherever the entity is aimed.
| arg | type | description |
|---|
| face | string | Face name string. |
examples
panel:setFace("Bottom")setTwoSided(both: boolean) → void
Emit from both faces of the rect rather than only the one its normal
points along.
| arg | type | description |
|---|
| both | boolean | Whether the rect is two-sided. |
examples
panel:setTwoSided(true)
setShadows(cast: boolean) → void
Enable or disable shadow casting. A shadow-casting rect consumes one
layer of the engine's shared shadow array; past the cap it lights but
stops casting.
| arg | type | description |
|---|
| cast | boolean | Whether the rect should cast shadows. |
examples
panel:setShadows(true)