Skip to main content

Class: LightClusterGrid

The world-space clustered light layer: selects the highest-importance registered lights around the camera (deterministically, with hysteresis), bins them into a camera-centered world-aligned cell grid, and packs both into two small data textures every chunk material samples.

A cell keeps the lights that light it best — ranked by their falloff at the cell, not by distance to the camera — so walking around never changes which lights a corner is lit by. Whatever does change a cell's lights (a light entering or leaving the selection, a rival winning its slot, the resolution gate) fades per slot over slotFadeMs instead of popping. Cells live in toroidal storage (world cell mod dims), so a cell keeps its slots and their fades while the window scrolls around it.

All per-frame work runs on preallocated scratch; a frame in which neither the registry nor the camera cell changed and nothing is fading does nothing at all.

Constructors​

constructor​

• new LightClusterGrid(registry, options): LightClusterGrid

Parameters​

NameType
registryLightSourceRegistry
optionsLightClusterGridOptions

Returns​

LightClusterGrid

Properties​

packedCount​

• packedCount: number = 0


packedIndices​

• Readonly packedIndices: Uint32Array<ArrayBufferLike>

Registry slot per packed data row: the selection in rank order, then the lights that left it but are still fading out of their cells.


selectedCount​

• selectedCount: number = 0


selectedIndices​

• Readonly selectedIndices: Uint32Array<ArrayBufferLike>

Selected registry slot per rank; selectedCount entries are live.


shadowProvider​

• shadowProvider: (index: number) => ShadowTexelRecord = null

Shadow-slot data source, wired by the facade once the shadow scheduler exists. Null keeps every record unshadowed (Engine PR A behavior).

Type declaration​

▸ (index): ShadowTexelRecord

Parameters​
NameType
indexnumber
Returns​

ShadowTexelRecord


uniforms​

• Readonly uniforms: Object

Type declaration​

NameTypeDescription
clusteredCount{ value: number = 0 }-
clusteredCount.valuenumber-
debugMode{ value: number = 0 }-
debugMode.valuenumber-
emissiveLevels{ value: Vector4 }-
emissiveLevels.valueVector4-
gridCellSize{ value: number = 8 }-
gridCellSize.valuenumber-
gridCenter{ value: Vector4 }The point the window is centred on (xyz), written every frame, and the rim fade width in blocks (w). The rim fade is continuous in it, so a world point's analytic light never steps when the window scrolls.
gridCenter.valueVector4-
gridDims{ value: Vector3 }-
gridDims.valueVector3-
gridHalf{ value: Vector3 }Half extent the window covers around its centre on every frame.
gridHalf.valueVector3-
gridOrigin{ value: Vector3 }-
gridOrigin.valueVector3-
gridStorageOffset{ value: Vector3 }The window origin's cell mod the dims: its storage cell, per axis.
gridStorageOffset.valueVector3-
lightData{ value: DataTexture }-
lightData.valueDataTexture-
lightGrid{ value: DataTexture }-
lightGrid.valueDataTexture-
maskKnee{ value: number }-
maskKnee.valuenumber-
ownership{ value: number = 1 }0..1: how strongly analytic claims suppress the baked flood term.
ownership.valuenumber-
specularStrength{ value: number = 1 }-
specularStrength.valuenumber-
stable{ value: number = 1 }1: the temporally stable layer. 0: the legacy frame, for A/B.
stable.valuenumber-

Accessors​

isHighResolutionGate​

• get isHighResolutionGate(): boolean

Returns​

boolean


isTemporallyStable​

• get isTemporallyStable(): boolean

Returns​

boolean

Methods​

dispose​

▸ dispose(): void

Returns​

void


markTexturesDirty​

▸ markTexturesDirty(): void

Re-upload GPU state after a restored context; CPU data is authoritative.

Returns​

void


refreshShadowTexels​

▸ refreshShadowTexels(stats): void

Rewrite only the shadow-facing data (flags bit 2 + texels 4–5) of every packed record. Runs when shadow slots change on a frame where the main pack did not — a ≤ 32 KB re-upload, counted in stats.

Parameters​

NameType
statsLocalLightStats

Returns​

void


resetHysteresis​

▸ resetHysteresis(): void

A camera jump larger than the analytic radius means the previous selection belongs to somewhere else entirely; hysteresis must not drag it across the map, and nothing there should fade in from black.

Returns​

void


sampleIrradiance​

▸ sampleIrradiance(point, out, options?): number

CPU mirror of the shader's light response, for entities and particles: accumulates the falloff-weighted color of every light the point's cell holds, scaled by each slot's fade, with the same spot/capsule shaping, shader-matched flicker, and — when the caller supplies its local flood level — the same occlusion mask the world surfaces use, so an entity behind a wall stops tinting from the light the wall blocks. Mirrors the chunk shader's per-fragment structure exactly: only lights present in the point's grid cell contribute — color and claim alike — so a point outside the window or in an overflowed cell keeps its flood look on entities just as it does on blocks, and both the color and out.claim (the unoccluded luminance claim that drives the flood remainder) carry the same window fade the shader applies, keeping the combined block light continuous across the rim. Zero allocation; the caller owns out and may reuse one options scratch object across calls (floodMask is the knee-mapped local flood level, 1 = fully open; timeMs drives the same flicker curve the shader evaluates).

Parameters​

NameType
point[number, number, number]
outLocalLightSample
options?Object
options.floodMask?number
options.timeMs?number

Returns​

number


setRenderPixels​

▸ setRenderPixels(pixels): void

The drawing buffer's pixel count, fed every frame. Crossing the high-resolution gate (with hysteresis) changes how many steady lights a cell keeps; the cells that lose or gain one fade like any other change.

Parameters​

NameType
pixelsnumber

Returns​

void


setTemporalStability​

▸ setTemporalStability(isStable): void

Stable layer on (default) or the legacy frame, for A/B captures. Either way the next pass rebuilds every cell from scratch.

Parameters​

NameType
isStableboolean

Returns​

void


setTierCaps​

▸ setTierCaps(caps): void

Parameters​

NameType
capsObject
caps.analyticRadiusnumber
caps.blockLightOwnershipnumber
caps.fluidSpecularStrengthnumber
caps.maxClusteredLightsnumber
caps.maxLightsPerCellnumber

Returns​

void


update​

▸ update(cameraX, cameraY, cameraZ, stats, nowMs?): void

Selection + binning + packing when the registry or the camera's grid cell moved since the last pass, then this frame's slot fades.

Parameters​

NameType
cameraXnumber
cameraYnumber
cameraZnumber
statsLocalLightStats
nowMsnumber

Returns​

void