Skip to content
This repository has been archived by the owner on Nov 19, 2018. It is now read-only.

Lighting

NSDex edited this page May 9, 2015 · 26 revisions

Terms

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).

Analysis

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.

Clone this wiki locally