RegionIO/internal/world/light.go

120 lines
2.9 KiB
Go

package world
import "regionio/internal/protocol"
// lightSections is the number of light subchunks: one below the world and one
// above, plus one per block section.
const lightSections = SectionCount + 2
// writeLight computes and emits simple lighting data. It does a vertical pass
// for sky light (sunlight propagating downward) and a single-block pass for
// block light (emissive blocks), without horizontal flood-fill.
func (c *Chunk) writeLight(w *protocol.Writer) {
skyLight := make([]*[2048]byte, lightSections)
blockLight := make([]*[2048]byte, lightSections)
// Section lightSections-1 is above the world, fully lit by the sky.
skyLight[lightSections-1] = new([2048]byte)
for i := range skyLight[lightSections-1] {
skyLight[lightSections-1][i] = 0xFF
}
for lx := 0; lx < 16; lx++ {
for lz := 0; lz < 16; lz++ {
// Sky light pass
currentSky := byte(15)
for y := MinY + WorldHeight - 1; y >= MinY; y-- {
block := c.GetBlock(lx, y, lz)
op := blockOpacity[block]
if op >= currentSky {
currentSky = 0
} else {
currentSky -= op
}
if currentSky > 0 {
si := (y - MinY) >> 4
lsi := si + 1
if skyLight[lsi] == nil {
skyLight[lsi] = new([2048]byte)
}
idx := blockIndex(lx, y, lz)
if idx%2 == 0 {
skyLight[lsi][idx/2] |= currentSky
} else {
skyLight[lsi][idx/2] |= currentSky << 4
}
}
// Block light pass
em := blockEmission[block]
if em > 0 {
si := (y - MinY) >> 4
lsi := si + 1
if blockLight[lsi] == nil {
blockLight[lsi] = new([2048]byte)
}
idx := blockIndex(lx, y, lz)
if idx%2 == 0 {
blockLight[lsi][idx/2] |= em
} else {
blockLight[lsi][idx/2] |= em << 4
}
}
}
}
}
var skyMask, blockMask, emptySkyMask, emptyBlockMask uint64
var skyCount, blockCount int
for i := 0; i < lightSections; i++ {
if skyLight[i] != nil {
skyMask |= 1 << i
skyCount++
} else {
emptySkyMask |= 1 << i
}
if blockLight[i] != nil {
blockMask |= 1 << i
blockCount++
} else {
emptyBlockMask |= 1 << i
}
}
writeBitSet(w, []uint64{skyMask})
writeBitSet(w, []uint64{blockMask})
writeBitSet(w, []uint64{emptySkyMask})
writeBitSet(w, []uint64{emptyBlockMask})
w.VarInt(int32(skyCount))
for i := 0; i < lightSections; i++ {
if skyLight[i] != nil {
w.VarInt(2048)
w.Raw(skyLight[i][:])
}
}
w.VarInt(int32(blockCount))
for i := 0; i < lightSections; i++ {
if blockLight[i] != nil {
w.VarInt(2048)
w.Raw(blockLight[i][:])
}
}
}
// allSectionsMask returns a bitset (as longs) with the low lightSections bits set.
func allSectionsMask() []uint64 {
return []uint64{(uint64(1) << lightSections) - 1}
}
// writeBitSet emits a length-prefixed array of longs.
func writeBitSet(w *protocol.Writer, longs []uint64) {
w.VarInt(int32(len(longs)))
for _, v := range longs {
w.Int64(int64(v))
}
}