package net.minecraft.client.renderer.chunk; import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; import it.unimi.dsi.fastutil.ints.IntPriorityQueue; import java.util.BitSet; import java.util.EnumSet; import java.util.Set; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @Environment(EnvType.CLIENT) public class VisGraph { private static final int SIZE_IN_BITS = 4; private static final int LEN = 16; private static final int MASK = 15; private static final int SIZE = 4096; private static final int X_SHIFT = 0; private static final int Z_SHIFT = 4; private static final int Y_SHIFT = 8; private static final int DX = (int)Math.pow(16.0, 0.0); private static final int DZ = (int)Math.pow(16.0, 1.0); private static final int DY = (int)Math.pow(16.0, 2.0); private static final int INVALID_INDEX = -1; private static final Direction[] DIRECTIONS = Direction.values(); private final BitSet bitSet = new BitSet(4096); private static final int[] INDEX_OF_EDGES = Util.make(new int[1352], is -> { int i = 0; int j = 15; int k = 0; for (int l = 0; l < 16; l++) { for (int m = 0; m < 16; m++) { for (int n = 0; n < 16; n++) { if (l == 0 || l == 15 || m == 0 || m == 15 || n == 0 || n == 15) { is[k++] = getIndex(l, m, n); } } } } }); private int empty = 4096; public void setOpaque(BlockPos pos) { this.bitSet.set(getIndex(pos), true); this.empty--; } private static int getIndex(BlockPos pos) { return getIndex(pos.getX() & 15, pos.getY() & 15, pos.getZ() & 15); } private static int getIndex(int x, int y, int z) { return x << 0 | y << 8 | z << 4; } public VisibilitySet resolve() { VisibilitySet visibilitySet = new VisibilitySet(); if (4096 - this.empty < 256) { visibilitySet.setAll(true); } else if (this.empty == 0) { visibilitySet.setAll(false); } else { for (int i : INDEX_OF_EDGES) { if (!this.bitSet.get(i)) { visibilitySet.add(this.floodFill(i)); } } } return visibilitySet; } private Set floodFill(int index) { Set set = EnumSet.noneOf(Direction.class); IntPriorityQueue intPriorityQueue = new IntArrayFIFOQueue(); intPriorityQueue.enqueue(index); this.bitSet.set(index, true); while (!intPriorityQueue.isEmpty()) { int i = intPriorityQueue.dequeueInt(); this.addEdges(i, set); for (Direction direction : DIRECTIONS) { int j = this.getNeighborIndexAtFace(i, direction); if (j >= 0 && !this.bitSet.get(j)) { this.bitSet.set(j, true); intPriorityQueue.enqueue(j); } } } return set; } private void addEdges(int index, Set faces) { int i = index >> 0 & 15; if (i == 0) { faces.add(Direction.WEST); } else if (i == 15) { faces.add(Direction.EAST); } int j = index >> 8 & 15; if (j == 0) { faces.add(Direction.DOWN); } else if (j == 15) { faces.add(Direction.UP); } int k = index >> 4 & 15; if (k == 0) { faces.add(Direction.NORTH); } else if (k == 15) { faces.add(Direction.SOUTH); } } private int getNeighborIndexAtFace(int index, Direction face) { switch (face) { case DOWN: if ((index >> 8 & 15) == 0) { return -1; } return index - DY; case UP: if ((index >> 8 & 15) == 15) { return -1; } return index + DY; case NORTH: if ((index >> 4 & 15) == 0) { return -1; } return index - DZ; case SOUTH: if ((index >> 4 & 15) == 15) { return -1; } return index + DZ; case WEST: if ((index >> 0 & 15) == 0) { return -1; } return index - DX; case EAST: if ((index >> 0 & 15) == 15) { return -1; } return index + DX; default: return -1; } } }