github.com/df-mc/dragonfly@v0.9.13/server/world/chunk/light.go (about)

     1  package chunk
     2  
     3  import (
     4  	"container/list"
     5  	"github.com/df-mc/dragonfly/server/block/cube"
     6  )
     7  
     8  // insertBlockLightNodes iterates over the chunk and looks for blocks that have a light level of at least 1.
     9  // If one is found, a node is added for it to the node queue.
    10  func (a *lightArea) insertBlockLightNodes(queue *list.List) {
    11  	a.iterSubChunks(anyLightBlocks, func(pos cube.Pos) {
    12  		if level := a.highest(pos, LightBlocks); level > 0 {
    13  			queue.PushBack(node(pos, level, BlockLight))
    14  		}
    15  	})
    16  }
    17  
    18  // anyLightBlocks checks if there are any blocks in the SubChunk passed that emit light.
    19  func anyLightBlocks(sub *SubChunk) bool {
    20  	for _, layer := range sub.storages {
    21  		for _, id := range layer.palette.values {
    22  			if LightBlocks[id] != 0 {
    23  				return true
    24  			}
    25  		}
    26  	}
    27  	return false
    28  }
    29  
    30  // insertSkyLightNodes iterates over the chunk and inserts a light node anywhere at the highest block in the
    31  // chunk. In addition, any skylight above those nodes will be set to 15.
    32  func (a *lightArea) insertSkyLightNodes(queue *list.List) {
    33  	a.iterHeightmap(func(x, z int, height, highestNeighbour, highestY, lowestY int) {
    34  		// If we hit a block like water or leaves (something that diffuses but does not block light), we
    35  		// need a node above this block regardless of the neighbours.
    36  		pos := cube.Pos{x, height, z}
    37  		a.setLight(pos, SkyLight, 15)
    38  
    39  		if pos[1] > lowestY {
    40  			if level := a.highest(pos.Sub(cube.Pos{0, 1}), FilteringBlocks); level != 15 && level != 0 {
    41  				queue.PushBack(node(pos, 15, SkyLight))
    42  			}
    43  		}
    44  		for y := pos[1]; y < highestY; y++ {
    45  			// We can do a bit of an optimisation here: We don't need to insert nodes if the neighbours are
    46  			// lower than the current one, on the same Y level, or one level higher, because light in
    47  			// this column can't spread below that anyway.
    48  			if pos[1]++; pos[1] < highestNeighbour {
    49  				queue.PushBack(node(pos, 15, SkyLight))
    50  				continue
    51  			}
    52  			// Fill the rest with full skylight.
    53  			a.setLight(pos, SkyLight, 15)
    54  		}
    55  	})
    56  }
    57  
    58  // insertLightSpreadingNodes inserts light nodes into the node queue passed which, when propagated, will
    59  // spread into the neighbouring chunks.
    60  func (a *lightArea) insertLightSpreadingNodes(queue *list.List, lt light) {
    61  	a.iterEdges(a.nodesNeeded(lt), func(pa, pb cube.Pos) {
    62  		la, lb := a.light(pa, lt), a.light(pb, lt)
    63  		if la == lb || la-1 == lb || lb-1 == la {
    64  			// No chance for this to spread. Don't check for the highest filtering blocks on the side.
    65  			return
    66  		}
    67  		if filter := a.highest(pb, FilteringBlocks) + 1; la > filter && la-filter > lb {
    68  			queue.PushBack(node(pb, la-filter, lt))
    69  		} else if filter = a.highest(pa, FilteringBlocks) + 1; lb > filter && lb-filter > la {
    70  			queue.PushBack(node(pa, lb-filter, lt))
    71  		}
    72  	})
    73  }
    74  
    75  // nodesNeeded checks if any light nodes of a specific light type are needed between two neighbouring SubChunks when
    76  // spreading light between them.
    77  func (a *lightArea) nodesNeeded(lt light) func(sa, sb *SubChunk) bool {
    78  	if lt == SkyLight {
    79  		return func(sa, sb *SubChunk) bool {
    80  			return &sa.skyLight[0] != &sb.skyLight[0]
    81  		}
    82  	}
    83  	return func(sa, sb *SubChunk) bool {
    84  		// Don't add nodes if both sub chunks are either both fully filled with light or have no light at all.
    85  		return &sa.blockLight[0] != &sb.blockLight[0]
    86  	}
    87  }
    88  
    89  // propagate spreads the next light node in the node queue passed through the lightArea a. propagate adds the neighbours
    90  // of the node to the queue for as long as it is able to spread.
    91  func (a *lightArea) propagate(queue *list.List) {
    92  	n := queue.Remove(queue.Front()).(lightNode)
    93  	if a.light(n.pos, n.lt) >= n.level {
    94  		return
    95  	}
    96  	a.setLight(n.pos, n.lt, n.level)
    97  
    98  	for _, neighbour := range a.neighbours(n) {
    99  		filter := a.highest(neighbour.pos, FilteringBlocks) + 1
   100  		if n.level > filter && a.light(neighbour.pos, n.lt) < n.level-filter {
   101  			neighbour.level = n.level - filter
   102  			queue.PushBack(neighbour)
   103  		}
   104  	}
   105  }
   106  
   107  // lightNode is a node pushed to the queue which is used to propagate light.
   108  type lightNode struct {
   109  	pos   cube.Pos
   110  	lt    light
   111  	level uint8
   112  }
   113  
   114  // node creates a new lightNode using the position, level and light type passed.
   115  func node(pos cube.Pos, level uint8, lt light) lightNode {
   116  	return lightNode{pos: pos, level: level, lt: lt}
   117  }