forked from Minestom/Minestom
-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathBlockLight.java
More file actions
215 lines (187 loc) · 8.93 KB
/
BlockLight.java
File metadata and controls
215 lines (187 loc) · 8.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package net.minestom.server.instance.light;
import it.unimi.dsi.fastutil.shorts.ShortArrayFIFOQueue;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockFace;
import net.minestom.server.instance.palette.Palette;
import org.jetbrains.annotations.ApiStatus;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import static net.minestom.server.instance.light.LightCompute.*;
final class BlockLight implements Light {
private byte[] content;
private byte[] contentPropagation;
private byte[] contentPropagationSwap;
private volatile boolean isValidBorders = true;
private final AtomicBoolean needsSend = new AtomicBoolean(false);
@Override
public void flip() {
if (this.contentPropagationSwap != null)
this.contentPropagation = this.contentPropagationSwap;
this.contentPropagationSwap = null;
}
static ShortArrayFIFOQueue buildInternalQueue(Palette blockPalette) {
ShortArrayFIFOQueue lightSources = new ShortArrayFIFOQueue();
// Apply section light
blockPalette.getAllPresent((x, y, z, stateId) -> {
final Block block = Block.fromStateId((short) stateId);
if (block == null) {
throw new IllegalStateException("Block cannot be null for stateId: " + stateId);
}
final byte lightEmission = (byte) block.registry().lightEmission();
final int index = x | (z << 4) | (y << 8);
if (lightEmission > 0) {
lightSources.enqueue((short) (index | (lightEmission << 12)));
}
});
return lightSources;
}
private ShortArrayFIFOQueue buildExternalQueue(Palette blockPalette,
Point[] neighbors, byte[] content,
LightLookup lightLookup,
PaletteLookup paletteLookup) {
ShortArrayFIFOQueue lightSources = new ShortArrayFIFOQueue();
for (int i = 0; i < neighbors.length; i++) {
final BlockFace face = BlockFace.getValues()[i];
Point neighborSection = neighbors[i];
if (neighborSection == null) continue;
Palette otherPalette = paletteLookup.palette(neighborSection.blockX(), neighborSection.blockY(), neighborSection.blockZ());
Light otherLight = lightLookup.light(neighborSection.blockX(), neighborSection.blockY(), neighborSection.blockZ());
for (int bx = 0; bx < 16; bx++) {
for (int by = 0; by < 16; by++) {
final int k = switch (face) {
case WEST, BOTTOM, NORTH -> 0;
case EAST, TOP, SOUTH -> 15;
};
final byte lightEmission = (byte) Math.max(switch (face) {
case NORTH, SOUTH -> (byte) otherLight.getLevel(bx, by, 15 - k);
case WEST, EAST -> (byte) otherLight.getLevel(15 - k, bx, by);
default -> (byte) otherLight.getLevel(bx, 15 - k, by);
} - 1, 0);
final int posTo = switch (face) {
case NORTH, SOUTH -> bx | (k << 4) | (by << 8);
case WEST, EAST -> k | (by << 4) | (bx << 8);
default -> bx | (by << 4) | (k << 8);
};
if (content != null) {
final int internalEmission = (byte) (Math.max(getLight(content, posTo) - 1, 0));
if (lightEmission <= internalEmission) continue;
}
final Block blockTo = switch (face) {
case NORTH, SOUTH -> getBlock(blockPalette, bx, by, k);
case WEST, EAST -> getBlock(blockPalette, k, bx, by);
default -> getBlock(blockPalette, bx, k, by);
};
final Block blockFrom = (switch (face) {
case NORTH, SOUTH -> getBlock(otherPalette, bx, by, 15 - k);
case WEST, EAST -> getBlock(otherPalette, 15 - k, bx, by);
default -> getBlock(otherPalette, bx, 15 - k, by);
});
if (blockTo == null && blockFrom != null) {
if (blockFrom.registry().collisionShape().isOccluded(Block.AIR.registry().collisionShape(), face.getOppositeFace()))
continue;
} else if (blockTo != null && blockFrom == null) {
if (Block.AIR.registry().collisionShape().isOccluded(blockTo.registry().collisionShape(), face))
continue;
} else if (blockTo != null && blockFrom != null) {
if (blockFrom.registry().collisionShape().isOccluded(blockTo.registry().collisionShape(), face.getOppositeFace()))
continue;
}
if (lightEmission > 0) {
final int index = posTo | (lightEmission << 12);
lightSources.enqueue((short) index);
}
}
}
}
return lightSources;
}
@Override
public void invalidate() {
this.needsSend.set(true);
this.isValidBorders = false;
this.contentPropagation = null;
}
@Override
public boolean requiresUpdate() {
return !isValidBorders;
}
@Override
@ApiStatus.Internal
public void set(byte[] copyArray) {
this.content = copyArray.clone();
this.contentPropagation = this.content;
this.isValidBorders = true;
this.needsSend.set(true);
}
@Override
public boolean requiresSend() {
return needsSend.getAndSet(false);
}
@Override
public byte[] array() {
if (content == null) return new byte[0];
if (contentPropagation == null) return content;
var res = LightCompute.bake(contentPropagation, content);
if (res == EMPTY_CONTENT) return new byte[0];
return res;
}
@Override
public int getLevel(int x, int y, int z) {
if (content == null) return 0;
int index = x | (z << 4) | (y << 8);
if (contentPropagation == null) return LightCompute.getLight(content, index);
return Math.max(LightCompute.getLight(contentPropagation, index), LightCompute.getLight(content, index));
}
@Override
public Set<Point> calculateInternal(Palette blockPalette,
int chunkX, int chunkY, int chunkZ,
int[] heightmap, int maxY,
LightLookup lightLookup) {
this.isValidBorders = true;
// Update single section with base lighting changes
ShortArrayFIFOQueue queue = buildInternalQueue(blockPalette);
this.content = LightCompute.compute(blockPalette, queue);
// Propagate changes to neighbors and self
Set<Point> toUpdate = new HashSet<>();
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
for (int k = -1; k <= 1; k++) {
final int neighborX = chunkX + i;
final int neighborY = chunkY + j;
final int neighborZ = chunkZ + k;
if (!(lightLookup.light(neighborX, neighborY, neighborZ) instanceof BlockLight blockLight))
continue;
blockLight.contentPropagation = null;
}
}
}
toUpdate.add(new Vec(chunkX, chunkY, chunkZ));
return toUpdate;
}
@Override
public Set<Point> calculateExternal(Palette blockPalette,
Point[] neighbors,
LightLookup lightLookup,
PaletteLookup paletteLookup) {
if (!isValidBorders) {
return Set.of();
}
ShortArrayFIFOQueue queue = buildExternalQueue(blockPalette, neighbors, content, lightLookup, paletteLookup);
final byte[] contentPropagationTemp = LightCompute.compute(blockPalette, queue);
this.contentPropagationSwap = LightCompute.bake(contentPropagationSwap, contentPropagationTemp);
// Propagate changes to neighbors and self
Set<Point> toUpdate = new HashSet<>();
for (int i = 0; i < neighbors.length; i++) {
final Point neighbor = neighbors[i];
if (neighbor == null) continue;
final BlockFace face = BlockFace.getValues()[i];
if (!LightCompute.compareBorders(content, contentPropagation, contentPropagationTemp, face)) {
toUpdate.add(neighbor);
}
}
return toUpdate;
}
}