176 lines
5.5 KiB
Java
176 lines
5.5 KiB
Java
package net.minecraft.world.level.lighting;
|
|
|
|
import it.unimi.dsi.fastutil.longs.Long2ByteMap;
|
|
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
import it.unimi.dsi.fastutil.longs.LongList;
|
|
import java.util.function.LongPredicate;
|
|
import net.minecraft.util.Mth;
|
|
|
|
public abstract class DynamicGraphMinFixedPoint {
|
|
public static final long SOURCE = Long.MAX_VALUE;
|
|
private static final int NO_COMPUTED_LEVEL = 255;
|
|
protected final int levelCount;
|
|
private final LeveledPriorityQueue priorityQueue;
|
|
private final Long2ByteMap computedLevels;
|
|
private volatile boolean hasWork;
|
|
|
|
protected DynamicGraphMinFixedPoint(int firstQueuedLevel, int width, int height) {
|
|
if (firstQueuedLevel >= 254) {
|
|
throw new IllegalArgumentException("Level count must be < 254.");
|
|
} else {
|
|
this.levelCount = firstQueuedLevel;
|
|
this.priorityQueue = new LeveledPriorityQueue(firstQueuedLevel, width);
|
|
this.computedLevels = new Long2ByteOpenHashMap(height, 0.5F) {
|
|
@Override
|
|
protected void rehash(int i) {
|
|
if (i > height) {
|
|
super.rehash(i);
|
|
}
|
|
}
|
|
};
|
|
this.computedLevels.defaultReturnValue((byte)-1);
|
|
}
|
|
}
|
|
|
|
protected void removeFromQueue(long position) {
|
|
int i = this.computedLevels.remove(position) & 255;
|
|
if (i != 255) {
|
|
int j = this.getLevel(position);
|
|
int k = this.calculatePriority(j, i);
|
|
this.priorityQueue.dequeue(position, k, this.levelCount);
|
|
this.hasWork = !this.priorityQueue.isEmpty();
|
|
}
|
|
}
|
|
|
|
public void removeIf(LongPredicate predicate) {
|
|
LongList longList = new LongArrayList();
|
|
this.computedLevels.keySet().forEach(l -> {
|
|
if (predicate.test(l)) {
|
|
longList.add(l);
|
|
}
|
|
});
|
|
longList.forEach(this::removeFromQueue);
|
|
}
|
|
|
|
private int calculatePriority(int oldLevel, int newLevel) {
|
|
return Math.min(Math.min(oldLevel, newLevel), this.levelCount - 1);
|
|
}
|
|
|
|
protected void checkNode(long levelPos) {
|
|
this.checkEdge(levelPos, levelPos, this.levelCount - 1, false);
|
|
}
|
|
|
|
protected void checkEdge(long fromPos, long toPos, int newLevel, boolean isDecreasing) {
|
|
this.checkEdge(fromPos, toPos, newLevel, this.getLevel(toPos), this.computedLevels.get(toPos) & 255, isDecreasing);
|
|
this.hasWork = !this.priorityQueue.isEmpty();
|
|
}
|
|
|
|
private void checkEdge(long fromPos, long toPos, int newLevel, int previousLevel, int propagationLevel, boolean isDecreasing) {
|
|
if (!this.isSource(toPos)) {
|
|
newLevel = Mth.clamp(newLevel, 0, this.levelCount - 1);
|
|
previousLevel = Mth.clamp(previousLevel, 0, this.levelCount - 1);
|
|
boolean bl = propagationLevel == 255;
|
|
if (bl) {
|
|
propagationLevel = previousLevel;
|
|
}
|
|
|
|
int i;
|
|
if (isDecreasing) {
|
|
i = Math.min(propagationLevel, newLevel);
|
|
} else {
|
|
i = Mth.clamp(this.getComputedLevel(toPos, fromPos, newLevel), 0, this.levelCount - 1);
|
|
}
|
|
|
|
int j = this.calculatePriority(previousLevel, propagationLevel);
|
|
if (previousLevel != i) {
|
|
int k = this.calculatePriority(previousLevel, i);
|
|
if (j != k && !bl) {
|
|
this.priorityQueue.dequeue(toPos, j, k);
|
|
}
|
|
|
|
this.priorityQueue.enqueue(toPos, k);
|
|
this.computedLevels.put(toPos, (byte)i);
|
|
} else if (!bl) {
|
|
this.priorityQueue.dequeue(toPos, j, this.levelCount);
|
|
this.computedLevels.remove(toPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected final void checkNeighbor(long fromPos, long toPos, int sourceLevel, boolean isDecreasing) {
|
|
int i = this.computedLevels.get(toPos) & 255;
|
|
int j = Mth.clamp(this.computeLevelFromNeighbor(fromPos, toPos, sourceLevel), 0, this.levelCount - 1);
|
|
if (isDecreasing) {
|
|
this.checkEdge(fromPos, toPos, j, this.getLevel(toPos), i, isDecreasing);
|
|
} else {
|
|
boolean bl = i == 255;
|
|
int k;
|
|
if (bl) {
|
|
k = Mth.clamp(this.getLevel(toPos), 0, this.levelCount - 1);
|
|
} else {
|
|
k = i;
|
|
}
|
|
|
|
if (j == k) {
|
|
this.checkEdge(fromPos, toPos, this.levelCount - 1, bl ? k : this.getLevel(toPos), i, isDecreasing);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected final boolean hasWork() {
|
|
return this.hasWork;
|
|
}
|
|
|
|
protected final int runUpdates(int toUpdateCount) {
|
|
if (this.priorityQueue.isEmpty()) {
|
|
return toUpdateCount;
|
|
} else {
|
|
while (!this.priorityQueue.isEmpty() && toUpdateCount > 0) {
|
|
toUpdateCount--;
|
|
long l = this.priorityQueue.removeFirstLong();
|
|
int i = Mth.clamp(this.getLevel(l), 0, this.levelCount - 1);
|
|
int j = this.computedLevels.remove(l) & 255;
|
|
if (j < i) {
|
|
this.setLevel(l, j);
|
|
this.checkNeighborsAfterUpdate(l, j, true);
|
|
} else if (j > i) {
|
|
this.setLevel(l, this.levelCount - 1);
|
|
if (j != this.levelCount - 1) {
|
|
this.priorityQueue.enqueue(l, this.calculatePriority(this.levelCount - 1, j));
|
|
this.computedLevels.put(l, (byte)j);
|
|
}
|
|
|
|
this.checkNeighborsAfterUpdate(l, i, false);
|
|
}
|
|
}
|
|
|
|
this.hasWork = !this.priorityQueue.isEmpty();
|
|
return toUpdateCount;
|
|
}
|
|
}
|
|
|
|
public int getQueueSize() {
|
|
return this.computedLevels.size();
|
|
}
|
|
|
|
protected boolean isSource(long pos) {
|
|
return pos == Long.MAX_VALUE;
|
|
}
|
|
|
|
/**
|
|
* Computes level propagated from neighbors of specified position with given existing level, excluding the given source position.
|
|
*/
|
|
protected abstract int getComputedLevel(long pos, long excludedSourcePos, int level);
|
|
|
|
protected abstract void checkNeighborsAfterUpdate(long pos, int level, boolean isDecreasing);
|
|
|
|
protected abstract int getLevel(long chunkPos);
|
|
|
|
protected abstract void setLevel(long chunkPos, int level);
|
|
|
|
/**
|
|
* Returns level propagated from start position with specified level to the neighboring end position.
|
|
*/
|
|
protected abstract int computeLevelFromNeighbor(long startPos, long endPos, int startLevel);
|
|
}
|