github.com/df-mc/dragonfly@v0.9.13/server/world/chunk/light_area.go (about) 1 package chunk 2 3 import ( 4 "bytes" 5 "container/list" 6 "github.com/df-mc/dragonfly/server/block/cube" 7 "math" 8 ) 9 10 // lightArea represents a square area of N*N chunks. It is used for light calculation specifically. 11 type lightArea struct { 12 baseX, baseZ int 13 c []*Chunk 14 w int 15 r cube.Range 16 } 17 18 // LightArea creates a lightArea with the lower corner of the lightArea at baseX and baseY. The length of the Chunk 19 // slice must be a square of a number, so 1, 4, 9 etc. 20 func LightArea(c []*Chunk, baseX, baseY int) *lightArea { 21 w := int(math.Sqrt(float64(len(c)))) 22 if len(c) != w*w { 23 panic("area must have a square chunk area") 24 } 25 return &lightArea{c: c, w: w, baseX: baseX << 4, baseZ: baseY << 4, r: c[0].r} 26 } 27 28 // Fill executes the light 'filling' stage, where the lightArea is filled with light coming only from the 29 // individual chunks within the lightArea itself, without light crossing chunk borders. 30 func (a *lightArea) Fill() { 31 a.initialiseLightSlices() 32 queue := list.New() 33 a.insertBlockLightNodes(queue) 34 a.insertSkyLightNodes(queue) 35 36 for queue.Len() != 0 { 37 a.propagate(queue) 38 } 39 } 40 41 // Spread executes the light 'spreading' stage, where the lightArea has light spread from every Chunk into the 42 // neighbouring chunks. The neighbouring chunks must have passed the light 'filling' stage before this 43 // function is called for an lightArea that includes them. 44 func (a *lightArea) Spread() { 45 queue := list.New() 46 a.insertLightSpreadingNodes(queue, BlockLight) 47 a.insertLightSpreadingNodes(queue, SkyLight) 48 49 for queue.Len() != 0 { 50 a.propagate(queue) 51 } 52 } 53 54 // light returns the light at a cube.Pos with the light type l. 55 func (a *lightArea) light(pos cube.Pos, l light) uint8 { 56 return l.light(a.sub(pos), uint8(pos[0]&0xf), uint8(pos[1]&0xf), uint8(pos[2]&0xf)) 57 } 58 59 // light sets the light at a cube.Pos with the light type l. 60 func (a *lightArea) setLight(pos cube.Pos, l light, v uint8) { 61 l.setLight(a.sub(pos), uint8(pos[0]&0xf), uint8(pos[1]&0xf), uint8(pos[2]&0xf), v) 62 } 63 64 // neighbours returns all neighbour lightNode of the one passed. If one of these nodes would otherwise fall outside the 65 // lightArea, it is not returned. 66 func (a *lightArea) neighbours(n lightNode) []lightNode { 67 nodes := make([]lightNode, 0, 6) 68 for _, f := range cube.Faces() { 69 nn := lightNode{pos: n.pos.Side(f), lt: n.lt} 70 if nn.pos[1] <= a.r.Max() && nn.pos[1] >= a.r.Min() && nn.pos[0] >= a.baseX && nn.pos[2] >= a.baseZ && nn.pos[0] < a.baseX+a.w*16 && nn.pos[2] < a.baseZ+a.w*16 { 71 nodes = append(nodes, nn) 72 } 73 } 74 return nodes 75 } 76 77 // iterSubChunks iterates over all blocks of the lightArea on a per-SubChunk basis. A filter function may be passed to 78 // specify if a SubChunk should be iterated over. If it returns false, it will not be iterated over. 79 func (a *lightArea) iterSubChunks(filter func(sub *SubChunk) bool, f func(pos cube.Pos)) { 80 for cx := 0; cx < a.w; cx++ { 81 for cz := 0; cz < a.w; cz++ { 82 baseX, baseZ, c := a.baseX+(cx<<4), a.baseZ+(cz<<4), a.c[a.chunkIndex(cx, cz)] 83 84 for index, sub := range c.sub { 85 if !filter(sub) { 86 continue 87 } 88 baseY := int(c.SubY(int16(index))) 89 a.iterSubChunk(func(x, y, z int) { 90 f(cube.Pos{x + baseX, y + baseY, z + baseZ}) 91 }) 92 } 93 } 94 } 95 } 96 97 // iterEdges iterates over all chunk edges within the lightArea and calls the function f with the cube.Pos at either 98 // side of the edge. 99 func (a *lightArea) iterEdges(filter func(a, b *SubChunk) bool, f func(a, b cube.Pos)) { 100 minY, maxY := a.r[0]>>4, a.r[1]>>4 101 // First iterate over chunk X, Y and Z, so we can filter out a complete 16x16 sheet of blocks if the 102 // filter function returns false. 103 for cu := 1; cu < a.w; cu++ { 104 u := cu << 4 105 for cv := 0; cv < a.w; cv++ { 106 v := cv << 4 107 for cy := minY; cy < maxY; cy++ { 108 baseY := cy << 4 109 110 xa, za := cube.Pos{a.baseX + u, baseY, a.baseZ + v}, cube.Pos{a.baseX + v, baseY, a.baseZ + u} 111 xb, zb := xa.Side(cube.FaceWest), za.Side(cube.FaceNorth) 112 113 addX, addZ := filter(a.sub(xa), a.sub(xb)), filter(a.sub(za), a.sub(zb)) 114 if !addX && !addZ { 115 continue 116 } 117 // The order of these loops allows us to take care of block spreading over both the X and Z axis by 118 // just swapping around the axes. 119 for addV := 0; addV < 16; addV++ { 120 for y := 0; y < 16; y++ { 121 // Finally, iterate over the 16x16 sheet and actually do the per-block checks. 122 if addX { 123 f(xa.Add(cube.Pos{0, y, addV}), xb.Add(cube.Pos{0, y, addV})) 124 } 125 if addZ { 126 f(za.Add(cube.Pos{addV, y}), zb.Add(cube.Pos{addV, y})) 127 } 128 } 129 } 130 } 131 } 132 } 133 } 134 135 // iterHeightmap iterates over the height map of the lightArea and calls the function f with the height map value, the 136 // height map value of the highest neighbour and the Y value of the highest non-empty SubChunk. 137 func (a *lightArea) iterHeightmap(f func(x, z int, height, highestNeighbour, highestY, lowestY int)) { 138 m, highestY := a.c[0].HeightMap(), a.c[0].Range().Min() 139 lowestY := highestY 140 for index := range a.c[0].sub { 141 if a.c[0].sub[index].Empty() { 142 continue 143 } 144 highestY = int(a.c[0].SubY(int16(index))) + 15 145 } 146 for x := uint8(0); x < 16; x++ { 147 for z := uint8(0); z < 16; z++ { 148 f(int(x)+a.baseX, int(z)+a.baseZ, int(m.At(x, z)), int(m.HighestNeighbour(x, z)), highestY, lowestY) 149 } 150 } 151 } 152 153 // iterSubChunk iterates over the coordinates of a SubChunk (0-15 on all axes) and calls the function f for each of 154 // those coordinates. 155 func (a *lightArea) iterSubChunk(f func(x, y, z int)) { 156 for y := 0; y < 16; y++ { 157 for x := 0; x < 16; x++ { 158 for z := 0; z < 16; z++ { 159 f(x, y, z) 160 } 161 } 162 } 163 } 164 165 // highest looks up through the blocks at first and second layer at the cube.Pos passed and runs their runtime IDs 166 // through the slice m passed, finding the highest value in this slice between those runtime IDs and returning it. 167 func (a *lightArea) highest(pos cube.Pos, m []uint8) uint8 { 168 x, y, z, sub := uint8(pos[0]&0xf), uint8(pos[1]&0xf), uint8(pos[2]&0xf), a.sub(pos) 169 storages, l := sub.storages, len(sub.storages) 170 171 switch l { 172 case 0: 173 return 0 174 case 1: 175 return m[storages[0].At(x, y, z)] 176 default: 177 level := m[storages[0].At(x, y, z)] 178 if v := m[storages[1].At(x, y, z)]; v > level { 179 return v 180 } 181 return level 182 } 183 } 184 185 var ( 186 fullLight = bytes.Repeat([]byte{0xff}, 2048) 187 fullLightPtr = &fullLight[0] 188 noLight = make([]uint8, 2048) 189 noLightPtr = &noLight[0] 190 ) 191 192 // initialiseLightSlices initialises all light slices in the sub chunks of all chunks either with full light if there is 193 // no sub chunk with any blocks above it, or with empty light if there is. The sub chunks with empty light are then 194 // ready to be properly calculated. 195 func (a *lightArea) initialiseLightSlices() { 196 for _, c := range a.c { 197 index := len(c.sub) - 1 198 for index >= 0 { 199 sub := c.sub[index] 200 if !sub.Empty() { 201 // We've hit the topmost empty SubChunk. 202 break 203 } 204 sub.skyLight = fullLight 205 sub.blockLight = noLight 206 index-- 207 } 208 for index >= 0 { 209 // Fill up the rest of the sub chunks with empty light. We will do light calculation for these sub chunks 210 // later on. 211 c.sub[index].skyLight = noLight 212 c.sub[index].blockLight = noLight 213 index-- 214 } 215 } 216 } 217 218 // sub returns the SubChunk corresponding to a cube.Pos. 219 func (a *lightArea) sub(pos cube.Pos) *SubChunk { 220 return a.chunk(pos).SubChunk(int16(pos[1])) 221 } 222 223 // chunk returns the Chunk corresponding to a cube.Pos. 224 func (a *lightArea) chunk(pos cube.Pos) *Chunk { 225 x, z := pos[0]-a.baseX, pos[2]-a.baseZ 226 return a.c[a.chunkIndex(x>>4, z>>4)] 227 } 228 229 // chunkIndex finds the index in the chunk slice of an lightArea for a Chunk at a specific x and z. 230 func (a *lightArea) chunkIndex(x, z int) int { 231 return x + (z * a.w) 232 }