326 lines
9.8 KiB
Java
326 lines
9.8 KiB
Java
package net.minecraft.world.level.lighting;
|
|
|
|
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
|
import it.unimi.dsi.fastutil.longs.LongIterator;
|
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
|
import java.util.Arrays;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.DataLayer;
|
|
import net.minecraft.world.level.chunk.LightChunk;
|
|
import net.minecraft.world.level.chunk.LightChunkGetter;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public abstract class LightEngine<M extends DataLayerStorageMap<M>, S extends LayerLightSectionStorage<M>> implements LayerLightEventListener {
|
|
public static final int MAX_LEVEL = 15;
|
|
protected static final int MIN_OPACITY = 1;
|
|
protected static final long PULL_LIGHT_IN_ENTRY = LightEngine.QueueEntry.decreaseAllDirections(1);
|
|
private static final int MIN_QUEUE_SIZE = 512;
|
|
protected static final Direction[] PROPAGATION_DIRECTIONS = Direction.values();
|
|
protected final LightChunkGetter chunkSource;
|
|
protected final S storage;
|
|
private final LongOpenHashSet blockNodesToCheck = new LongOpenHashSet(512, 0.5F);
|
|
private final LongArrayFIFOQueue decreaseQueue = new LongArrayFIFOQueue();
|
|
private final LongArrayFIFOQueue increaseQueue = new LongArrayFIFOQueue();
|
|
private static final int CACHE_SIZE = 2;
|
|
private final long[] lastChunkPos = new long[2];
|
|
private final LightChunk[] lastChunk = new LightChunk[2];
|
|
|
|
protected LightEngine(LightChunkGetter chunkSource, S storage) {
|
|
this.chunkSource = chunkSource;
|
|
this.storage = storage;
|
|
this.clearChunkCache();
|
|
}
|
|
|
|
public static boolean hasDifferentLightProperties(BlockState state1, BlockState state2) {
|
|
return state2 == state1
|
|
? false
|
|
: state2.getLightBlock() != state1.getLightBlock()
|
|
|| state2.getLightEmission() != state1.getLightEmission()
|
|
|| state2.useShapeForLightOcclusion()
|
|
|| state1.useShapeForLightOcclusion();
|
|
}
|
|
|
|
public static int getLightBlockInto(BlockState state1, BlockState state2, Direction direction, int defaultReturnValue) {
|
|
boolean bl = isEmptyShape(state1);
|
|
boolean bl2 = isEmptyShape(state2);
|
|
if (bl && bl2) {
|
|
return defaultReturnValue;
|
|
} else {
|
|
VoxelShape voxelShape = bl ? Shapes.empty() : state1.getOcclusionShape();
|
|
VoxelShape voxelShape2 = bl2 ? Shapes.empty() : state2.getOcclusionShape();
|
|
return Shapes.mergedFaceOccludes(voxelShape, voxelShape2, direction) ? 16 : defaultReturnValue;
|
|
}
|
|
}
|
|
|
|
public static VoxelShape getOcclusionShape(BlockState state, Direction direction) {
|
|
return isEmptyShape(state) ? Shapes.empty() : state.getFaceOcclusionShape(direction);
|
|
}
|
|
|
|
protected static boolean isEmptyShape(BlockState state) {
|
|
return !state.canOcclude() || !state.useShapeForLightOcclusion();
|
|
}
|
|
|
|
protected BlockState getState(BlockPos pos) {
|
|
int i = SectionPos.blockToSectionCoord(pos.getX());
|
|
int j = SectionPos.blockToSectionCoord(pos.getZ());
|
|
LightChunk lightChunk = this.getChunk(i, j);
|
|
return lightChunk == null ? Blocks.BEDROCK.defaultBlockState() : lightChunk.getBlockState(pos);
|
|
}
|
|
|
|
protected int getOpacity(BlockState state) {
|
|
return Math.max(1, state.getLightBlock());
|
|
}
|
|
|
|
protected boolean shapeOccludes(BlockState state1, BlockState state2, Direction direction) {
|
|
VoxelShape voxelShape = getOcclusionShape(state1, direction);
|
|
VoxelShape voxelShape2 = getOcclusionShape(state2, direction.getOpposite());
|
|
return Shapes.faceShapeOccludes(voxelShape, voxelShape2);
|
|
}
|
|
|
|
@Nullable
|
|
protected LightChunk getChunk(int x, int z) {
|
|
long l = ChunkPos.asLong(x, z);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
if (l == this.lastChunkPos[i]) {
|
|
return this.lastChunk[i];
|
|
}
|
|
}
|
|
|
|
LightChunk lightChunk = this.chunkSource.getChunkForLighting(x, z);
|
|
|
|
for (int j = 1; j > 0; j--) {
|
|
this.lastChunkPos[j] = this.lastChunkPos[j - 1];
|
|
this.lastChunk[j] = this.lastChunk[j - 1];
|
|
}
|
|
|
|
this.lastChunkPos[0] = l;
|
|
this.lastChunk[0] = lightChunk;
|
|
return lightChunk;
|
|
}
|
|
|
|
private void clearChunkCache() {
|
|
Arrays.fill(this.lastChunkPos, ChunkPos.INVALID_CHUNK_POS);
|
|
Arrays.fill(this.lastChunk, null);
|
|
}
|
|
|
|
@Override
|
|
public void checkBlock(BlockPos pos) {
|
|
this.blockNodesToCheck.add(pos.asLong());
|
|
}
|
|
|
|
public void queueSectionData(long sectionPos, @Nullable DataLayer data) {
|
|
this.storage.queueSectionData(sectionPos, data);
|
|
}
|
|
|
|
public void retainData(ChunkPos chunkPos, boolean retainData) {
|
|
this.storage.retainData(SectionPos.getZeroNode(chunkPos.x, chunkPos.z), retainData);
|
|
}
|
|
|
|
@Override
|
|
public void updateSectionStatus(SectionPos pos, boolean isQueueEmpty) {
|
|
this.storage.updateSectionStatus(pos.asLong(), isQueueEmpty);
|
|
}
|
|
|
|
@Override
|
|
public void setLightEnabled(ChunkPos chunkPos, boolean lightEnabled) {
|
|
this.storage.setLightEnabled(SectionPos.getZeroNode(chunkPos.x, chunkPos.z), lightEnabled);
|
|
}
|
|
|
|
@Override
|
|
public int runLightUpdates() {
|
|
LongIterator longIterator = this.blockNodesToCheck.iterator();
|
|
|
|
while (longIterator.hasNext()) {
|
|
this.checkNode(longIterator.nextLong());
|
|
}
|
|
|
|
this.blockNodesToCheck.clear();
|
|
this.blockNodesToCheck.trim(512);
|
|
int i = 0;
|
|
i += this.propagateDecreases();
|
|
i += this.propagateIncreases();
|
|
this.clearChunkCache();
|
|
this.storage.markNewInconsistencies(this);
|
|
this.storage.swapSectionMap();
|
|
return i;
|
|
}
|
|
|
|
private int propagateIncreases() {
|
|
int i;
|
|
for (i = 0; !this.increaseQueue.isEmpty(); i++) {
|
|
long l = this.increaseQueue.dequeueLong();
|
|
long m = this.increaseQueue.dequeueLong();
|
|
int j = this.storage.getStoredLevel(l);
|
|
int k = LightEngine.QueueEntry.getFromLevel(m);
|
|
if (LightEngine.QueueEntry.isIncreaseFromEmission(m) && j < k) {
|
|
this.storage.setStoredLevel(l, k);
|
|
j = k;
|
|
}
|
|
|
|
if (j == k) {
|
|
this.propagateIncrease(l, m, j);
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
private int propagateDecreases() {
|
|
int i;
|
|
for (i = 0; !this.decreaseQueue.isEmpty(); i++) {
|
|
long l = this.decreaseQueue.dequeueLong();
|
|
long m = this.decreaseQueue.dequeueLong();
|
|
this.propagateDecrease(l, m);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
protected void enqueueDecrease(long packedPos1, long packedPos2) {
|
|
this.decreaseQueue.enqueue(packedPos1);
|
|
this.decreaseQueue.enqueue(packedPos2);
|
|
}
|
|
|
|
protected void enqueueIncrease(long packedPos1, long packedPos2) {
|
|
this.increaseQueue.enqueue(packedPos1);
|
|
this.increaseQueue.enqueue(packedPos2);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasLightWork() {
|
|
return this.storage.hasInconsistencies() || !this.blockNodesToCheck.isEmpty() || !this.decreaseQueue.isEmpty() || !this.increaseQueue.isEmpty();
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public DataLayer getDataLayerData(SectionPos sectionPos) {
|
|
return this.storage.getDataLayerData(sectionPos.asLong());
|
|
}
|
|
|
|
@Override
|
|
public int getLightValue(BlockPos levelPos) {
|
|
return this.storage.getLightValue(levelPos.asLong());
|
|
}
|
|
|
|
public String getDebugData(long sectionPos) {
|
|
return this.getDebugSectionType(sectionPos).display();
|
|
}
|
|
|
|
public LayerLightSectionStorage.SectionType getDebugSectionType(long sectionPos) {
|
|
return this.storage.getDebugSectionType(sectionPos);
|
|
}
|
|
|
|
protected abstract void checkNode(long packedPos);
|
|
|
|
protected abstract void propagateIncrease(long packedPos, long queueEntry, int lightLevel);
|
|
|
|
protected abstract void propagateDecrease(long packedPos, long lightLevel);
|
|
|
|
public static class QueueEntry {
|
|
private static final int FROM_LEVEL_BITS = 4;
|
|
private static final int DIRECTION_BITS = 6;
|
|
private static final long LEVEL_MASK = 15L;
|
|
private static final long DIRECTIONS_MASK = 1008L;
|
|
private static final long FLAG_FROM_EMPTY_SHAPE = 1024L;
|
|
private static final long FLAG_INCREASE_FROM_EMISSION = 2048L;
|
|
|
|
public static long decreaseSkipOneDirection(int level, Direction direction) {
|
|
long l = withoutDirection(1008L, direction);
|
|
return withLevel(l, level);
|
|
}
|
|
|
|
public static long decreaseAllDirections(int level) {
|
|
return withLevel(1008L, level);
|
|
}
|
|
|
|
public static long increaseLightFromEmission(int level, boolean fromEmptyShape) {
|
|
long l = 1008L;
|
|
l |= 2048L;
|
|
if (fromEmptyShape) {
|
|
l |= 1024L;
|
|
}
|
|
|
|
return withLevel(l, level);
|
|
}
|
|
|
|
public static long increaseSkipOneDirection(int level, boolean fromEmptyShape, Direction direction) {
|
|
long l = withoutDirection(1008L, direction);
|
|
if (fromEmptyShape) {
|
|
l |= 1024L;
|
|
}
|
|
|
|
return withLevel(l, level);
|
|
}
|
|
|
|
public static long increaseOnlyOneDirection(int level, boolean fromEmptyShape, Direction direction) {
|
|
long l = 0L;
|
|
if (fromEmptyShape) {
|
|
l |= 1024L;
|
|
}
|
|
|
|
l = withDirection(l, direction);
|
|
return withLevel(l, level);
|
|
}
|
|
|
|
public static long increaseSkySourceInDirections(boolean down, boolean north, boolean south, boolean west, boolean east) {
|
|
long l = withLevel(0L, 15);
|
|
if (down) {
|
|
l = withDirection(l, Direction.DOWN);
|
|
}
|
|
|
|
if (north) {
|
|
l = withDirection(l, Direction.NORTH);
|
|
}
|
|
|
|
if (south) {
|
|
l = withDirection(l, Direction.SOUTH);
|
|
}
|
|
|
|
if (west) {
|
|
l = withDirection(l, Direction.WEST);
|
|
}
|
|
|
|
if (east) {
|
|
l = withDirection(l, Direction.EAST);
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
public static int getFromLevel(long entry) {
|
|
return (int)(entry & 15L);
|
|
}
|
|
|
|
public static boolean isFromEmptyShape(long entry) {
|
|
return (entry & 1024L) != 0L;
|
|
}
|
|
|
|
public static boolean isIncreaseFromEmission(long entry) {
|
|
return (entry & 2048L) != 0L;
|
|
}
|
|
|
|
public static boolean shouldPropagateInDirection(long entry, Direction direction) {
|
|
return (entry & 1L << direction.ordinal() + 4) != 0L;
|
|
}
|
|
|
|
private static long withLevel(long entry, int level) {
|
|
return entry & -16L | level & 15L;
|
|
}
|
|
|
|
private static long withDirection(long entry, Direction direction) {
|
|
return entry | 1L << direction.ordinal() + 4;
|
|
}
|
|
|
|
private static long withoutDirection(long entry, Direction direction) {
|
|
return entry & ~(1L << direction.ordinal() + 4);
|
|
}
|
|
}
|
|
}
|