Skip to main content

Class: LocalLights

Local light emitters: the engine-owned registry, selection, clustering, and GPU packing behind world.localLights. The game declares semantic block profiles and dynamic sources; chunk scanning, diffing, aggregation, culling, and rendering state are owned here.

A world that registers no lights and declares no profiles pays one uniform compare per fragment and nothing per frame on the CPU.

Constructors

constructor

new LocalLights(options, getWorldConfig, getBlocks): LocalLights

Parameters

NameType
optionsPartial<LocalLightsOptions>
getWorldConfig() => LocalLightsWorldConfig
getBlocks() => Iterable<EmitterBlock, any, any>

Returns

LocalLights

Properties

getLoadedChunk

getLoadedChunk: (cx: number, cz: number) => ScannableChunk

Chunk lookup for late profile changes. Populated by the world adapter; null keeps late declarations working for future loads only.

Type declaration

▸ (cx, cz): ScannableChunk

Parameters
NameType
cxnumber
cznumber
Returns

ScannableChunk


grid

Readonly grid: LightClusterGrid


options

Readonly options: LocalLightsOptions


registry

Readonly registry: LightSourceRegistry


shadowLedger

Readonly shadowLedger: ShadowFrameLedger

The per-frame face-unit budget CSM and local shadows share.


shadows

Readonly shadows: LocalShadowScheduler

The L3 shadow tier: slot selection, cached faces, atlas renders.


stats

Readonly stats: LocalLightStats

Mutated in place; never reallocated.

Accessors

blockLightOwnership

get blockLightOwnership(): number

Flood-ownership weight of the current tier: 1 on every tier that renders clustered lights (the analytic layer owns visible block-source lighting exclusively — nothing double-lights), 0 at off/potato (the exact legacy flood frame). This is an invariant, not configuration — read-only introspection for CPU consumers (LightShined) that mirror the chunk shader's uLocalOwnership uniform; hybrid visible stacking is not a supported state.

Returns

number


getIsOpaqueAt

set getIsOpaqueAt(fn): void

Voxel opacity oracle for mount-aware shadow-face skipping, wired by the world adapter. Null disables the skip (all six faces render).

Parameters

NameType
fn(vx: number, vy: number, vz: number) => boolean

Returns

void


uniformBindings

get uniformBindings(): Object

The shared uniform objects every chunk material binds. One set for the whole world; updates are zero-copy.

Returns

Object

NameType
uClusteredLightCount{ value: number = 0 }
uClusteredLightCount.valuenumber
uEmissiveLevels{ value: Vector4 }
uEmissiveLevels.valueVector4
uLightData{ value: DataTexture }
uLightData.valueDataTexture
uLightGrid{ value: DataTexture }
uLightGrid.valueDataTexture
uLightGridCellSize{ value: number = 8 }
uLightGridCellSize.valuenumber
uLightGridDims{ value: Vector3 }
uLightGridDims.valueVector3
uLightGridOrigin{ value: Vector3 }
uLightGridOrigin.valueVector3
uLocalLightDebugMode{ value: number = 0 }
uLocalLightDebugMode.valuenumber
uLocalMaskKnee{ value: number }
uLocalMaskKnee.valuenumber
uLocalOwnership{ value: number = 1 }
uLocalOwnership.valuenumber
uLocalShadowAtlas{ value: Texture<unknown> }
uLocalShadowAtlas.valueTexture<unknown>
uLocalShadowParams{ value: Vector4 }
uLocalShadowParams.valueVector4
uLocalShadowParams2{ value: Vector4 }
uLocalShadowParams2.valueVector4
uLocalSpecularStrength{ value: number = 1 }
uLocalSpecularStrength.valuenumber

Methods

add

add(descriptor, position): number

Parameters

NameType
descriptorLocalLightDescriptor
positionVector3

Returns

number


beginShadowFrame

beginShadowFrame(entities?): void

Open this frame's shadow budget and reserve units for dynamic faces (moving hero lights, entity overlays) before the CSM cascades spend.

Parameters

NameType
entities?Object3D<Object3DEventMap>[]

Returns

void


clearBlockProfile

clearBlockProfile(block): void

Parameters

NameType
blockstring | number

Returns

void


dispose

dispose(): void

Returns

void


getDebugMode

getDebugMode(): number

Returns

number


getQualityTier

getQualityTier(): LightQualityTier

Returns

LightQualityTier


handleBlockUpdate

handleBlockUpdate(edit): void

A voxel changed. Only edits that touch an emitter queue a section rescan; everything else costs one AABB test per active shadow slot.

Takes the raw voxel words, not bare ids: rotating a torch in place changes only the rotation bits, yet must re-anchor its light (the scan signatures carry rotation) and refresh cached shadow maps (the stick occludes differently) exactly like swapping the block would.

Parameters

NameTypeDescription
editObject-
edit.chunkScannableChunk-
edit.newValuenumber-
edit.oldValuenumberRaw voxel words: id in the low 16 bits, rotation/stage above.
edit.voxel[number, number, number]-

Returns

void


handleChunkLoaded

handleChunkLoaded(cx, cz, chunk): void

A chunk's data arrived or re-arrived: queue every section for a scan.

Parameters

NameType
cxnumber
cznumber
chunkScannableChunk

Returns

void


handleChunkMeshed

handleChunkMeshed(cx, cz): void

A chunk mesh (re)built: refresh cached maps that reach into it.

Parameters

NameType
cxnumber
cznumber

Returns

void


handleChunkUnloaded

handleChunkUnloaded(cx, cz): void

A chunk left the render distance: its registrations release now.

Parameters

NameType
cxnumber
cznumber

Returns

void


hideDebugOverlay

hideDebugOverlay(): void

Returns

void


invalidateShadowRegion

invalidateShadowRegion(region): void

Invalidate every cached shadow map intersecting [min, max) — for game systems that alter occluding geometry outside the block-update stream.

Parameters

NameType
regionObject
region.maxVector3
region.minVector3

Returns

void


onContextRestored

onContextRestored(): void

GPU context restored: CPU-side light state is authoritative, so all GPU textures simply re-upload. Wire this to the canvas's webglcontextrestored event.

Returns

void


queryLocalLights

queryLocalLights(position, out, options?): void

CPU sample of the selected lights' combined irradiance at a point, for entities, held items, and particles. Writes into out; no allocation (per-frame callers may reuse one options scratch object).

options.floodMask is the caller's local flood-light level mapped through the mask knee (1 = fully open); masked and shadow-fallback lights multiply by it so an entity behind a wall stops tinting from a blocked light. options.timeMs drives the same flicker curve the shader evaluates.

out.claim and out.windowFade mirror the chunk shader's flood-ownership term: consumers that also apply a baked flood tint scale that tint by blockLightFloodRemainder({ scaledClaim: out.claim × {@link blockLightOwnership}, floodLevel, windowFade: out.windowFade }) so a point covered by analytic lights is never lit by both models and the window-rim crossfade matches the ground (LightShined does exactly this).

Parameters

NameType
positionVector3
outLocalLightSample
options?Object
options.floodMask?number
options.timeMs?number

Returns

void


remove

remove(handle): boolean

Parameters

NameType
handlenumber

Returns

boolean


renderShadows

renderShadows(renderer, scene, entities?, instancePools?, skipShadowObjects?, poolBounds?): void

Render whatever local shadow faces this frame's remaining budget grants: moving-light refreshes and entity overlays first, then the invalidated static FIFO. A frame with zero shadow slots returns immediately.

Parameters

NameTypeDefault value
rendererWebGLRendererundefined
sceneScene<Object3DEventMap>undefined
entities?Object3D<Object3DEventMap>[]undefined
instancePools?Group<Object3DEventMap>[]undefined
skipShadowObjectsreadonly Object3D<Object3DEventMap>[][]
poolBounds?readonly Box3[]undefined

Returns

void


resetPeakStats

resetPeakStats(): void

Start a fresh peak-cost measurement window (benchmark harnesses).

Returns

void


setBlockProfile

setBlockProfile(block, profile): void

Declare (or replace) the semantic light profile for a block id or name. Affects every present and future emitter of that block: all tracked sections rescan through the amortized queue.

Parameters

NameType
blockstring | number
profileBlockLightProfile

Returns

void


setColor

setColor(handle, color): boolean

Parameters

NameType
handlenumber
color[number, number, number]

Returns

boolean


setDebugMode

setDebugMode(mode): void

0 off, 1 cell occupancy heatmap, 2 isolated contribution, 3 leak mask, 4 shadow-slot tint, 5 isolated local-shadow visibility, 6 flood- ownership remainder (white = legacy flood renders, black = analytic owns).

Parameters

NameType
mode0 | 1 | 2 | 3 | 4 | 5 | 6

Returns

void


setDirection

setDirection(handle, direction): boolean

Parameters

NameType
handlenumber
directionVector3

Returns

boolean


setEnabled

setEnabled(handle, isEnabled): boolean

Parameters

NameType
handlenumber
isEnabledboolean

Returns

boolean


setIntensity

setIntensity(handle, intensity): boolean

Parameters

NameType
handlenumber
intensitynumber

Returns

boolean


setPosition

setPosition(handle, position): boolean

Parameters

NameType
handlenumber
positionVector3

Returns

boolean


setQualityTier

setQualityTier(tier): void

Parameters

NameType
tierLightQualityTier

Returns

void


setRange

setRange(handle, range): boolean

Parameters

NameType
handlenumber
rangenumber

Returns

boolean


showDebugOverlay

showDebugOverlay(parent): void

Wireframe bounds of every selected light, colored by state. Attached to the given parent (typically the world); allocated on first use only.

Parameters

NameType
parentObject
parent.add(object: object) => void

Returns

void


update

update(position): void

Per-frame work: drain the bounded scan queue, then run selection and packing (which no-op when nothing changed). Called from World.update.

Parameters

NameType
positionVector3

Returns

void