Fix incorrect forAllBoxes implementation

The implementation of FlatBitsetUtil#clearRange was
incorrect, as it did not properly clear long values
inbetween the range and the bitmasks for keeping the first
and last bits were off by one.

The implementation of forAllBoxes did not properly account
for multiple ranges of bits to be set on the Z direction
for a given X and Y, as it would only use the first range.
This commit is contained in:
Spottedleaf
2023-09-10 17:52:07 -07:00
parent a1e89e9515
commit b8935d1dd1
3 changed files with 125 additions and 41 deletions

View File

@@ -79,8 +79,8 @@ public final class FlatBitsetUtil {
final int fromBitsetIdx = from >>> LOG2_LONG;
final int toBitsetIdx = to >>> LOG2_LONG;
final long keepFirst = ALL_SET >>> ((BITS_PER_LONG - 1) ^ from);
final long keepLast = ALL_SET << to;
final long keepFirst = ~(ALL_SET << from);
final long keepLast = ~(ALL_SET >>> ((BITS_PER_LONG - 1) ^ to));
Objects.checkFromToIndex(fromBitsetIdx, toBitsetIdx, bitset.length);
@@ -91,7 +91,7 @@ public final class FlatBitsetUtil {
bitset[fromBitsetIdx] &= keepFirst;
for (int i = fromBitsetIdx + 1; i < toBitsetIdx; ++i) {
bitset[toBitsetIdx] = 0L;
bitset[i] = 0L;
}
bitset[toBitsetIdx] &= keepLast;

View File

@@ -8,20 +8,100 @@ import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Unique;
import java.util.ArrayList;
import java.util.List;
@Mixin(BitSetDiscreteVoxelShape.class)
public abstract class BitSetDiscreteVoxelShapeMixin extends DiscreteVoxelShape {
@Unique
private static final boolean DEBUG_ALL_BOXES = false;
protected BitSetDiscreteVoxelShapeMixin(int i, int j, int k) {
super(i, j, k);
}
@Unique
private static void forAllBoxesVanilla(DiscreteVoxelShape discreteVoxelShape, DiscreteVoxelShape.IntLineConsumer intLineConsumer, boolean bl) {
BitSetDiscreteVoxelShape bitSetDiscreteVoxelShape = new BitSetDiscreteVoxelShape(discreteVoxelShape);
for(int i = 0; i < bitSetDiscreteVoxelShape.getYSize(); ++i) {
for(int j = 0; j < bitSetDiscreteVoxelShape.getXSize(); ++j) {
int k = -1;
for(int l = 0; l <= bitSetDiscreteVoxelShape.getZSize(); ++l) {
if (bitSetDiscreteVoxelShape.isFullWide(j, i, l)) {
if (bl) {
if (k == -1) {
k = l;
}
} else {
intLineConsumer.consume(j, i, l, j + 1, i + 1, l + 1);
}
} else if (k != -1) {
int m = j;
int n = i;
bitSetDiscreteVoxelShape.clearZStrip(k, l, j, i);
while(bitSetDiscreteVoxelShape.isZStripFull(k, l, m + 1, i)) {
bitSetDiscreteVoxelShape.clearZStrip(k, l, m + 1, i);
++m;
}
while(bitSetDiscreteVoxelShape.isXZRectangleFull(j, m + 1, k, l, n + 1)) {
for(int o = j; o <= m; ++o) {
bitSetDiscreteVoxelShape.clearZStrip(k, l, o, n + 1);
}
++n;
}
intLineConsumer.consume(j, i, k, m + 1, n + 1, l);
k = -1;
}
}
}
}
}
@Unique
private static void chkForAll(final DiscreteVoxelShape shape, final boolean mergeAdjacent) {
record Range(int sx, int sy, int sz, int ex, int ey, int ez) {}
if (new Throwable().getStackTrace()[2].getMethodName().contains("chkForAll")) {
return;
}
final List<Range> vanillaRanges = new ArrayList<>();
final List<Range> moonriseRanges = new ArrayList<>();
forAllBoxesVanilla(shape, (x1, y1, z1, x2, y2, z2) -> {
vanillaRanges.add(new Range(x1, y1, z1, x2, y2, z2));
}, mergeAdjacent);
forAllBoxes(shape, (x1, y1, z1, x2, y2, z2) -> {
moonriseRanges.add(new Range(x1, y1, z1, x2, y2, z2));
}, mergeAdjacent);
if (!vanillaRanges.equals(moonriseRanges)) {
forAllBoxesVanilla(shape, (x1, y1, z1, x2, y2, z2) -> {}, mergeAdjacent);
forAllBoxes(shape, (x1, y1, z1, x2, y2, z2) -> {}, mergeAdjacent);
throw new IllegalStateException();
}
}
/**
* @reason avoid creating temporary shape, interact directly with the bitset
* @author Spottedleaf
*/
@Overwrite
public static void forAllBoxes(final DiscreteVoxelShape shape, final DiscreteVoxelShape.IntLineConsumer consumer, final boolean mergeAdjacent) {
if (DEBUG_ALL_BOXES) {
chkForAll(shape, mergeAdjacent);
}
// called with the shape of a VoxelShape, so we can expect the cache to exist
final CachedShapeData cache = ((CollisionDiscreteVoxelShape)shape).getOrCreateCachedShapeData();
@@ -65,56 +145,58 @@ public abstract class BitSetDiscreteVoxelShapeMixin extends DiscreteVoxelShape {
for (int y = 0; y < sizeY; ++y, indexY += incY) {
indexX = indexY;
for (int x = 0; x < sizeX; ++x, indexX += incX) {
final int startIndex = indexX;
final int endIndex = indexX + sizeZ;
for (int zIdx = indexX, endIndex = indexX + sizeZ; zIdx < endIndex;) {
final int firstSetZ = FlatBitsetUtil.firstSet(bitset, zIdx, endIndex);
final int firstSetZ = FlatBitsetUtil.firstSet(bitset, startIndex, endIndex);
if (firstSetZ == -1) {
break;
}
if (firstSetZ == -1) {
continue;
}
int lastSetZ = FlatBitsetUtil.firstClear(bitset, firstSetZ, endIndex);
if (lastSetZ == -1) {
lastSetZ = endIndex;
}
int lastSetZ = FlatBitsetUtil.firstClear(bitset, firstSetZ, endIndex);
if (lastSetZ == -1) {
lastSetZ = endIndex;
}
FlatBitsetUtil.clearRange(bitset, firstSetZ, lastSetZ);
FlatBitsetUtil.clearRange(bitset, firstSetZ, lastSetZ);
// try to merge neighbouring on the X axis
int endX = x + 1; // exclusive
for (int neighbourIdxStart = firstSetZ + incX, neighbourIdxEnd = lastSetZ + incX;
endX < sizeX && FlatBitsetUtil.isRangeSet(bitset, neighbourIdxStart, neighbourIdxEnd);
neighbourIdxStart += incX, neighbourIdxEnd += incX) {
// try to merge neighbouring on the X axis
int endX = x + 1; // exclusive
for (int neighbourIdxStart = firstSetZ + incX, neighbourIdxEnd = lastSetZ + incX;
endX < sizeX && FlatBitsetUtil.isRangeSet(bitset, neighbourIdxStart, neighbourIdxEnd);
neighbourIdxStart += incX, neighbourIdxEnd += incX) {
++endX;
FlatBitsetUtil.clearRange(bitset, neighbourIdxStart, neighbourIdxEnd);
}
++endX;
FlatBitsetUtil.clearRange(bitset, neighbourIdxStart, neighbourIdxEnd);
}
// try to merge neighbouring on the Y axis
// try to merge neighbouring on the Y axis
int endY; // exclusive
int firstSetZY, lastSetZY;
y_merge:
for (endY = y + 1, firstSetZY = firstSetZ + incY, lastSetZY = lastSetZ + incY; endY < sizeY;
firstSetZY += incY, lastSetZY += incY) {
int endY; // exclusive
int firstSetZY, lastSetZY;
y_merge:
for (endY = y + 1, firstSetZY = firstSetZ + incY, lastSetZY = lastSetZ + incY; endY < sizeY;
++endY, firstSetZY += incY, lastSetZY += incY) {
// test the whole XZ range
for (int testX = x, start = firstSetZY, end = lastSetZY; testX < endX;
++testX, start += incX, end += incX) {
if (!FlatBitsetUtil.isRangeSet(bitset, start, end)) {
break y_merge;
}
}
// test the whole XZ range
for (int testX = x, start = firstSetZY, end = lastSetZY; testX < endX;
++testX, start += incX, end += incX) {
if (!FlatBitsetUtil.isRangeSet(bitset, start, end)) {
break y_merge;
++endY;
// passed, so we can clear it
for (int testX = x, start = firstSetZY, end = lastSetZY; testX < endX;
++testX, start += incX, end += incX) {
FlatBitsetUtil.clearRange(bitset, start, end);
}
}
// passed, so we can clear it
for (int testX = x, start = firstSetZY, end = lastSetZY; testX < endX;
++testX, start += incX, end += incX) {
FlatBitsetUtil.clearRange(bitset, start, end);
}
consumer.consume(x, y, firstSetZ - indexX, endX, endY, lastSetZ - indexX);
zIdx = lastSetZ;
}
consumer.consume(x, y, firstSetZ - startIndex, endX, endY, lastSetZ - startIndex);
}
}
}

View File

@@ -92,7 +92,9 @@ accessible field net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape xMax I
accessible field net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape yMax I
accessible field net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape zMax I
accessible method net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape join (Lnet/minecraft/world/phys/shapes/DiscreteVoxelShape;Lnet/minecraft/world/phys/shapes/DiscreteVoxelShape;Lnet/minecraft/world/phys/shapes/IndexMerger;Lnet/minecraft/world/phys/shapes/IndexMerger;Lnet/minecraft/world/phys/shapes/IndexMerger;Lnet/minecraft/world/phys/shapes/BooleanOp;)Lnet/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape;
accessible method net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape isZStripFull (IIII)Z
accessible method net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape isXZRectangleFull (IIIII)Z
accessible method net/minecraft/world/phys/shapes/BitSetDiscreteVoxelShape clearZStrip (IIII)V
# IndexMerger
accessible class net/minecraft/world/phys/shapes/IndexMerger