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:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user