179 lines
7.1 KiB
Java
179 lines
7.1 KiB
Java
/*
|
|
* Decompiled with CFR 0.152.
|
|
*/
|
|
package net.minecraft.world.level.lighting;
|
|
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.util.BitStorage;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.SimpleBitStorage;
|
|
import net.minecraft.world.level.BlockGetter;
|
|
import net.minecraft.world.level.LevelHeightAccessor;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
import net.minecraft.world.level.lighting.LightEngine;
|
|
import net.minecraft.world.phys.shapes.Shapes;
|
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
|
|
|
public class ChunkSkyLightSources {
|
|
private static final int SIZE = 16;
|
|
public static final int NEGATIVE_INFINITY = Integer.MIN_VALUE;
|
|
private final int minY;
|
|
private final BitStorage heightmap;
|
|
private final BlockPos.MutableBlockPos mutablePos1 = new BlockPos.MutableBlockPos();
|
|
private final BlockPos.MutableBlockPos mutablePos2 = new BlockPos.MutableBlockPos();
|
|
|
|
public ChunkSkyLightSources(LevelHeightAccessor level) {
|
|
this.minY = level.getMinY() - 1;
|
|
int maxY = level.getMaxY() + 1;
|
|
int bits = Mth.ceillog2(maxY - this.minY + 1);
|
|
this.heightmap = new SimpleBitStorage(bits, 256);
|
|
}
|
|
|
|
public void fillFrom(ChunkAccess chunk) {
|
|
int maxSectionIndex = chunk.getHighestFilledSectionIndex();
|
|
if (maxSectionIndex == -1) {
|
|
this.fill(this.minY);
|
|
return;
|
|
}
|
|
for (int z = 0; z < 16; ++z) {
|
|
for (int x = 0; x < 16; ++x) {
|
|
int initialEdgeY = Math.max(this.findLowestSourceY(chunk, maxSectionIndex, x, z), this.minY);
|
|
this.set(ChunkSkyLightSources.index(x, z), initialEdgeY);
|
|
}
|
|
}
|
|
}
|
|
|
|
private int findLowestSourceY(ChunkAccess chunk, int topSectionIndex, int x, int z) {
|
|
int topY = SectionPos.sectionToBlockCoord(chunk.getSectionYFromSectionIndex(topSectionIndex) + 1);
|
|
BlockPos.MutableBlockPos topPos = this.mutablePos1.set(x, topY, z);
|
|
BlockPos.MutableBlockPos bottomPos = this.mutablePos2.setWithOffset((Vec3i)topPos, Direction.DOWN);
|
|
BlockState topState = Blocks.AIR.defaultBlockState();
|
|
for (int sectionIndex = topSectionIndex; sectionIndex >= 0; --sectionIndex) {
|
|
LevelChunkSection section = chunk.getSection(sectionIndex);
|
|
if (section.hasOnlyAir()) {
|
|
topState = Blocks.AIR.defaultBlockState();
|
|
int sectionY = chunk.getSectionYFromSectionIndex(sectionIndex);
|
|
topPos.setY(SectionPos.sectionToBlockCoord(sectionY));
|
|
bottomPos.setY(topPos.getY() - 1);
|
|
continue;
|
|
}
|
|
for (int y = 15; y >= 0; --y) {
|
|
BlockState bottomState = section.getBlockState(x, y, z);
|
|
if (ChunkSkyLightSources.isEdgeOccluded(topState, bottomState)) {
|
|
return topPos.getY();
|
|
}
|
|
topState = bottomState;
|
|
topPos.set(bottomPos);
|
|
bottomPos.move(Direction.DOWN);
|
|
}
|
|
}
|
|
return this.minY;
|
|
}
|
|
|
|
public boolean update(BlockGetter level, int x, int y, int z) {
|
|
BlockState middleState;
|
|
BlockPos.MutableBlockPos middlePos;
|
|
BlockState topState;
|
|
int upperEdgeY = y + 1;
|
|
int index = ChunkSkyLightSources.index(x, z);
|
|
int currentLowestSourceY = this.get(index);
|
|
if (upperEdgeY < currentLowestSourceY) {
|
|
return false;
|
|
}
|
|
BlockPos.MutableBlockPos topPos = this.mutablePos1.set(x, y + 1, z);
|
|
if (this.updateEdge(level, index, currentLowestSourceY, topPos, topState = level.getBlockState(topPos), middlePos = this.mutablePos2.set(x, y, z), middleState = level.getBlockState(middlePos))) {
|
|
return true;
|
|
}
|
|
BlockPos.MutableBlockPos bottomPos = this.mutablePos1.set(x, y - 1, z);
|
|
BlockState bottomState = level.getBlockState(bottomPos);
|
|
return this.updateEdge(level, index, currentLowestSourceY, middlePos, middleState, bottomPos, bottomState);
|
|
}
|
|
|
|
private boolean updateEdge(BlockGetter level, int index, int oldTopEdgeY, BlockPos topPos, BlockState topState, BlockPos bottomPos, BlockState bottomState) {
|
|
int checkedEdgeY = topPos.getY();
|
|
if (ChunkSkyLightSources.isEdgeOccluded(topState, bottomState)) {
|
|
if (checkedEdgeY > oldTopEdgeY) {
|
|
this.set(index, checkedEdgeY);
|
|
return true;
|
|
}
|
|
} else if (checkedEdgeY == oldTopEdgeY) {
|
|
this.set(index, this.findLowestSourceBelow(level, bottomPos, bottomState));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private int findLowestSourceBelow(BlockGetter level, BlockPos startPos, BlockState startState) {
|
|
BlockPos.MutableBlockPos topPos = this.mutablePos1.set(startPos);
|
|
BlockPos.MutableBlockPos bottomPos = this.mutablePos2.setWithOffset((Vec3i)startPos, Direction.DOWN);
|
|
BlockState topState = startState;
|
|
while (bottomPos.getY() >= this.minY) {
|
|
BlockState bottomState = level.getBlockState(bottomPos);
|
|
if (ChunkSkyLightSources.isEdgeOccluded(topState, bottomState)) {
|
|
return topPos.getY();
|
|
}
|
|
topState = bottomState;
|
|
topPos.set(bottomPos);
|
|
bottomPos.move(Direction.DOWN);
|
|
}
|
|
return this.minY;
|
|
}
|
|
|
|
private static boolean isEdgeOccluded(BlockState topState, BlockState bottomState) {
|
|
if (bottomState.getLightBlock() != 0) {
|
|
return true;
|
|
}
|
|
VoxelShape topShape = LightEngine.getOcclusionShape(topState, Direction.DOWN);
|
|
VoxelShape bottomShape = LightEngine.getOcclusionShape(bottomState, Direction.UP);
|
|
return Shapes.faceShapeOccludes(topShape, bottomShape);
|
|
}
|
|
|
|
public int getLowestSourceY(int x, int z) {
|
|
int value = this.get(ChunkSkyLightSources.index(x, z));
|
|
return this.extendSourcesBelowWorld(value);
|
|
}
|
|
|
|
public int getHighestLowestSourceY() {
|
|
int maxValue = Integer.MIN_VALUE;
|
|
for (int i = 0; i < this.heightmap.getSize(); ++i) {
|
|
int value = this.heightmap.get(i);
|
|
if (value <= maxValue) continue;
|
|
maxValue = value;
|
|
}
|
|
return this.extendSourcesBelowWorld(maxValue + this.minY);
|
|
}
|
|
|
|
private void fill(int lowestSourceY) {
|
|
int value = lowestSourceY - this.minY;
|
|
for (int i = 0; i < this.heightmap.getSize(); ++i) {
|
|
this.heightmap.set(i, value);
|
|
}
|
|
}
|
|
|
|
private void set(int index, int value) {
|
|
this.heightmap.set(index, value - this.minY);
|
|
}
|
|
|
|
private int get(int index) {
|
|
return this.heightmap.get(index) + this.minY;
|
|
}
|
|
|
|
private int extendSourcesBelowWorld(int value) {
|
|
if (value == this.minY) {
|
|
return Integer.MIN_VALUE;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private static int index(int x, int z) {
|
|
return x + z * 16;
|
|
}
|
|
}
|
|
|