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.
All per-frame work runs on preallocated scratch; a frame in which neither the registry nor the camera cell changed does nothing at all.
Constructors
constructor
• new LightClusterGrid(registry, options): LightClusterGrid
Parameters
| Name | Type |
|---|---|
registry | LightSourceRegistry |
options | Object |
options.analyticRadius | number |
options.fluidSpecularStrength | number |
options.gridCellSize | number |
options.gridDims | [number, number, number] |
options.maskKnee | number |
options.maxClusteredLights | number |
options.maxLightsPerCell | number |
options.selectionHysteresis | number |
Returns
Properties
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
| Name | Type |
|---|---|
index | number |
Returns
uniforms
• Readonly uniforms: Object
Type declaration
| Name | Type | Description |
|---|---|---|
clusteredCount | { value: number = 0 } | - |
clusteredCount.value | number | - |
debugMode | { value: number = 0 } | - |
debugMode.value | number | - |
emissiveLevels | { value: Vector4 } | - |
emissiveLevels.value | Vector4 | - |
gridCellSize | { value: number = 8 } | - |
gridCellSize.value | number | - |
gridDims | { value: Vector3 } | - |
gridDims.value | Vector3 | - |
gridOrigin | { value: Vector3 } | - |
gridOrigin.value | Vector3 | - |
lightData | { value: DataTexture } | - |
lightData.value | DataTexture | - |
lightGrid | { value: DataTexture } | - |
lightGrid.value | DataTexture | - |
maskKnee | { value: number } | - |
maskKnee.value | number | - |
ownership | { value: number = 1 } | 0..1: how strongly analytic claims suppress the baked flood term. |
ownership.value | number | - |
specularStrength | { value: number = 1 } | - |
specularStrength.value | number | - |
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
| Name | Type |
|---|---|
stats | LocalLightStats |
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.
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 selected light in range
of the point, 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
outer-two-cell 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
| Name | Type |
|---|---|
point | [number, number, number] |
out | LocalLightSample |
options? | Object |
options.floodMask? | number |
options.timeMs? | number |
Returns
number
setTierCaps
▸ setTierCaps(caps): void
Parameters
| Name | Type |
|---|---|
caps | Object |
caps.analyticRadius | number |
caps.blockLightOwnership | number |
caps.fluidSpecularStrength | number |
caps.maxClusteredLights | number |
caps.maxLightsPerCell | number |
Returns
void
update
▸ update(cameraX, cameraY, cameraZ, stats): void
Selection + binning + packing. Returns immediately when neither the registry nor the camera's grid cell moved since the last pass.
Parameters
| Name | Type |
|---|---|
cameraX | number |
cameraY | number |
cameraZ | number |
stats | LocalLightStats |
Returns
void