-
Notifications
You must be signed in to change notification settings - Fork 313
Lighting
Light Opacity is a value between [0, 255] which describes how opaque a block is. Opaque blocks (including lava) have a light opacity of 255. Other values for light opacity are close to zero (and I suspect they won't be above 15).
Each chunk maintains a height map (a 2D array, byte[Z][X]). The value for each index is the y-coordinate of the first block (starting from the top, y=127) in that column for which the block below does not have a Light Opacity equal to zero. A separate per-chunk variable stores the y-coordinate of the lowest block but I'm currently unsure what it is used for.
The actual (re)lighting is handled by distinct LightingOperations objects which are essentially a bounding box + lighting logic. These objects are created by the World object in response to some other object scheduling a lighting update for a given bounding box, and enqueued for later execution. For instance, when a block in a chunk is modified, the chunk schedules are lighting update with a 1x1x1 bounding box for the modified coordinate.
A LightingOperation can be for either Block or Skylight. I shall refer to these distinct cases as BlockLightingOperation and SkyLightingOperation (though Notch used the same class for both, with conditional checks littered throughout).
World::ScheduleLightingUpdate(Kind, BoundingBox) -> Void
{
// No idea
if blockExists(BoundingBox.centerX, 64, BoundingBox.centerY) == false then
return
// Iterate over the last five scheduled lighting updates, but ignore SkyLightingOperations.
for (lightingUpdate in scheduledLightingUpdates.reverseEnumerator[0..<5])
if lightingUpdate is BlockLightingOperation then
// Merge will only succeed the intersection of BoundingBox and lightingUpdate.BoundingBox
// is two or fewer blocks along each axis AND
// MergedBoundingBox.Volume - lightingUpdate.BoundingBox.Volume <= 2.
if lightingUpdate.tryToMergeWith(BoundingBox) then
return
if Kind == .Sky then
lightingUpdate.enqueue( SkyLightingOperation(BoundingBox) )
else if Kind == .Block then
lightingUpdate.enqueue( BlockLightingOperation(BoundingBox) )
}
(Re)Lighting is not a one step process. Rather, it occurs over many iterations (ticks?) until a new state of equilibrium has been reached.