26ed99fda6
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
132 lines
4.7 KiB
Go
132 lines
4.7 KiB
Go
package chunk
|
|
|
|
import "github.com/df-mc/dragonfly/server/block/cube"
|
|
|
|
// insertBlockLightNodes iterates over the chunk and looks for blocks that have a light level of at least 1.
|
|
// If one is found, a node is added for it to the node queue.
|
|
func (a *lightArea) insertBlockLightNodes(queue *lightQueue) {
|
|
a.iterSubChunks(a.anyLightBlocks, func(pos cube.Pos) {
|
|
if level := a.highest(pos, a.br.LightBlock); level > 0 {
|
|
queue.push(node(pos, level, BlockLight))
|
|
}
|
|
})
|
|
}
|
|
|
|
// anyLightBlocks checks if there are any blocks in the SubChunk passed that emit light.
|
|
func (a *lightArea) anyLightBlocks(sub *SubChunk) bool {
|
|
for _, layer := range sub.storages {
|
|
for _, id := range layer.palette.values {
|
|
if a.br.LightBlock(id) != 0 {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// insertSkyLightNodes iterates over the chunk and inserts a light node anywhere at the highest block in the
|
|
// chunk. In addition, any skylight above those nodes will be set to 15.
|
|
func (a *lightArea) insertSkyLightNodes(queue *lightQueue) {
|
|
a.iterHeightmap(func(x, z int, height, highestNeighbour, highestY, lowestY int) {
|
|
pos := cube.Pos{x, height, z}
|
|
if height <= a.r.Max() {
|
|
// Only set light if we're not at the top of the world.
|
|
a.setLight(pos, SkyLight, 15)
|
|
|
|
if pos[1] > lowestY {
|
|
if level := a.highest(pos.Sub(cube.Pos{0, 1}), a.br.FilteringBlock); level != 15 && level != 0 {
|
|
// If we hit a block like water or leaves (something that diffuses but does not block light), we
|
|
// need a node above this block regardless of the neighbours.
|
|
queue.push(node(pos, 15, SkyLight))
|
|
}
|
|
}
|
|
}
|
|
for y := pos[1]; y < highestY; y++ {
|
|
// We can do a bit of an optimisation here: We don't need to insert nodes if the neighbours are
|
|
// lower than the current one, on the same Y level, or one level higher, because light in
|
|
// this column can't spread below that anyway.
|
|
if pos[1]++; pos[1] < highestNeighbour {
|
|
queue.push(node(pos, 15, SkyLight))
|
|
continue
|
|
}
|
|
// Fill the rest with full skylight.
|
|
a.setLight(pos, SkyLight, 15)
|
|
}
|
|
})
|
|
}
|
|
|
|
// insertLightSpreadingNodes inserts light nodes into the node queue passed which, when propagated, will
|
|
// spread into the neighbouring chunks.
|
|
func (a *lightArea) insertLightSpreadingNodes(queue *lightQueue, lt light) {
|
|
a.iterEdges(a.nodesNeeded(lt), func(pa, pb cube.Pos) {
|
|
la, lb := a.light(pa, lt), a.light(pb, lt)
|
|
if la == lb || la-1 == lb || lb-1 == la {
|
|
// No chance for this to spread. Don't check for the highest filtering blocks on the side.
|
|
return
|
|
}
|
|
if filter := a.highest(pb, a.br.FilteringBlock) + 1; la > filter && la-filter > lb {
|
|
queue.push(node(pb, la-filter, lt))
|
|
} else if filter = a.highest(pa, a.br.FilteringBlock) + 1; lb > filter && lb-filter > la {
|
|
queue.push(node(pa, lb-filter, lt))
|
|
}
|
|
})
|
|
}
|
|
|
|
// nodesNeeded checks if any light nodes of a specific light type are needed between two neighbouring SubChunks when
|
|
// spreading light between them.
|
|
func (a *lightArea) nodesNeeded(lt light) func(sa, sb *SubChunk) bool {
|
|
if lt == SkyLight {
|
|
return func(sa, sb *SubChunk) bool {
|
|
return &sa.skyLight[0] != &sb.skyLight[0]
|
|
}
|
|
}
|
|
return func(sa, sb *SubChunk) bool {
|
|
// Don't add nodes if both sub chunks are either both fully filled with light or have no light at all.
|
|
return &sa.blockLight[0] != &sb.blockLight[0]
|
|
}
|
|
}
|
|
|
|
// propagate spreads the next light node in the node queue passed through the lightArea a. propagate adds the neighbours
|
|
// of the node to the queue for as long as it is able to spread.
|
|
func (a *lightArea) propagate(queue *lightQueue) {
|
|
n, ok := queue.pop()
|
|
if !ok {
|
|
return
|
|
}
|
|
if a.light(n.pos, n.lt) >= n.level {
|
|
return
|
|
}
|
|
a.setLight(n.pos, n.lt, n.level)
|
|
|
|
x, y, z := n.pos[0], n.pos[1], n.pos[2]
|
|
a.propagateNeighbour(queue, n.lt, n.level, x+1, y, z)
|
|
a.propagateNeighbour(queue, n.lt, n.level, x-1, y, z)
|
|
a.propagateNeighbour(queue, n.lt, n.level, x, y+1, z)
|
|
a.propagateNeighbour(queue, n.lt, n.level, x, y-1, z)
|
|
a.propagateNeighbour(queue, n.lt, n.level, x, y, z+1)
|
|
a.propagateNeighbour(queue, n.lt, n.level, x, y, z-1)
|
|
}
|
|
|
|
func (a *lightArea) propagateNeighbour(queue *lightQueue, lt light, level uint8, x, y, z int) {
|
|
if y < a.r.Min() || y > a.r.Max() || x < a.baseX || z < a.baseZ || x >= a.baseX+a.w*16 || z >= a.baseZ+a.w*16 {
|
|
return
|
|
}
|
|
pos := cube.Pos{x, y, z}
|
|
filter := a.highest(pos, a.br.FilteringBlock) + 1
|
|
if level > filter && a.light(pos, lt) < level-filter {
|
|
queue.push(node(pos, level-filter, lt))
|
|
}
|
|
}
|
|
|
|
// lightNode is a node pushed to the queue which is used to propagate light.
|
|
type lightNode struct {
|
|
pos cube.Pos
|
|
lt light
|
|
level uint8
|
|
}
|
|
|
|
// node creates a new lightNode using the position, level and light type passed.
|
|
func node(pos cube.Pos, level uint8, lt light) lightNode {
|
|
return lightNode{pos: pos, level: level, lt: lt}
|
|
}
|