Avoid creating SliceShape when retrieving Voxelshape faces
SliceShape is not always the ideal shape, as sometimes the face result is empty or full cube - but is represented as SliceShape. Additionally, sometimes SliceShape should be empty but isEmpty() is false.
This commit is contained in:
@@ -9,12 +9,12 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
@Mixin(ClientSuggestionProvider.class)
|
||||
abstract class ClientSuggestionProviderMixin implements CommandClientCommandSource, FabricClientCommandSource {
|
||||
@Override
|
||||
public void moonrise$sendSuccess(final Component message) {
|
||||
public final void moonrise$sendSuccess(final Component message) {
|
||||
this.sendFeedback(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moonrise$sendFailure(final Component message) {
|
||||
public final void moonrise$sendFailure(final Component message) {
|
||||
this.sendError(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package ca.spottedleaf.moonrise.neoforge;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.PlatformHooks;
|
||||
import ca.spottedleaf.moonrise.common.util.CoordinateUtils;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager;
|
||||
import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -15,7 +14,6 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
||||
|
||||
@@ -25,12 +25,12 @@ abstract class ClientCommandSourceStackMixin extends CommandSourceStack implemen
|
||||
public abstract void sendSuccess(Supplier<Component> message, boolean sendToAdmins);
|
||||
|
||||
@Override
|
||||
public void moonrise$sendFailure(final Component message) {
|
||||
public final void moonrise$sendFailure(final Component message) {
|
||||
this.sendFailure(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moonrise$sendSuccess(final Component message) {
|
||||
public final void moonrise$sendSuccess(final Component message) {
|
||||
this.sendSuccess(() -> message, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import org.spongepowered.asm.mixin.Shadow;
|
||||
@Mixin(ServerChunkCache.MainThreadExecutor.class)
|
||||
abstract class ServerChunkCache$MainThreadExecutorMixin extends BlockableEventLoop<Runnable> {
|
||||
|
||||
@Shadow(aliases = "this$0")
|
||||
@Shadow(aliases = "this$0") // Neoforge
|
||||
@Final
|
||||
ServerChunkCache field_18810;
|
||||
ServerChunkCache field_18810; // Fabric
|
||||
|
||||
protected ServerChunkCache$MainThreadExecutorMixin(String string) {
|
||||
super(string);
|
||||
|
||||
@@ -27,7 +27,7 @@ abstract class StructureTemplate$PaletteMixin {
|
||||
value = "RETURN"
|
||||
)
|
||||
)
|
||||
private <K, V> void makeCacheCHM(final CallbackInfo ci) {
|
||||
private void makeCacheCHM(final CallbackInfo ci) {
|
||||
this.cache = new ConcurrentHashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,13 +65,19 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
private AABB constantAABBCollision;
|
||||
|
||||
@Unique
|
||||
private static void initCaches(final VoxelShape shape) {
|
||||
private static void initCaches(final VoxelShape shape, final boolean neighbours) {
|
||||
((CollisionVoxelShape)shape).moonrise$isFullBlock();
|
||||
((CollisionVoxelShape)shape).moonrise$occludesFullBlock();
|
||||
shape.toAabbs();
|
||||
if (!shape.isEmpty()) {
|
||||
shape.bounds();
|
||||
}
|
||||
if (neighbours) {
|
||||
for (final Direction direction : DIRECTIONS_CACHED) {
|
||||
initCaches(Shapes.getFaceShape(shape, direction), false);
|
||||
initCaches(shape.getFaceShape(direction), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,17 +104,13 @@ abstract class BlockStateBaseMixin extends StateHolder<Block, BlockState> implem
|
||||
this.occludesFullBlock = ((CollisionVoxelShape)collisionShape).moonrise$occludesFullBlock();
|
||||
this.emptyCollisionShape = collisionShape.isEmpty();
|
||||
// init caches
|
||||
initCaches(collisionShape);
|
||||
if (collisionShape != Shapes.empty() && collisionShape != Shapes.block()) {
|
||||
for (final Direction direction : DIRECTIONS_CACHED) {
|
||||
// initialise the directional face shape cache as well
|
||||
final VoxelShape shape = Shapes.getFaceShape(collisionShape, direction);
|
||||
initCaches(shape);
|
||||
}
|
||||
initCaches(collisionShape, true);
|
||||
if (this.constantCollisionShape != null) {
|
||||
initCaches(this.constantCollisionShape, true);
|
||||
}
|
||||
if (this.cache.occlusionShapes != null) {
|
||||
for (final VoxelShape shape : this.cache.occlusionShapes) {
|
||||
initCaches(shape);
|
||||
initCaches(shape, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -60,7 +60,7 @@ abstract class DiscreteVoxelShapeMixin implements CollisionDiscreteVoxelShape {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean hasSingleAABB = sizeX == 1 && sizeY == 1 && sizeZ == 1 && !isEmpty && discreteVoxelShape.isFull(0, 0, 0);
|
||||
final boolean hasSingleAABB = sizeX == 1 && sizeY == 1 && sizeZ == 1 && !isEmpty && (voxelSet[0] & 1L) != 0L;
|
||||
|
||||
final int minFullX = discreteVoxelShape.firstFull(Direction.Axis.X);
|
||||
final int minFullY = discreteVoxelShape.firstFull(Direction.Axis.Y);
|
||||
|
||||
@@ -23,7 +23,6 @@ import net.minecraft.world.phys.shapes.BooleanOp;
|
||||
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
||||
import net.minecraft.world.phys.shapes.OffsetDoubleList;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.phys.shapes.SliceShape;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -208,13 +207,13 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
|
||||
if (direction.getAxisDirection() == Direction.AxisDirection.POSITIVE) {
|
||||
if (DoubleMath.fuzzyEquals(this.max(axis), 1.0, CollisionUtil.COLLISION_EPSILON)) {
|
||||
ret = tryForceBlock(new SliceShape((VoxelShape)(Object)this, axis, this.shape.getSize(axis) - 1));
|
||||
ret = CollisionUtil.sliceShape((VoxelShape)(Object)this, axis, this.shape.getSize(axis) - 1);
|
||||
} else {
|
||||
ret = Shapes.empty();
|
||||
}
|
||||
} else {
|
||||
if (DoubleMath.fuzzyEquals(this.min(axis), 0.0, CollisionUtil.COLLISION_EPSILON)) {
|
||||
ret = tryForceBlock(new SliceShape((VoxelShape)(Object)this, axis, 0));
|
||||
ret = CollisionUtil.sliceShape((VoxelShape)(Object)this, axis, 0);
|
||||
} else {
|
||||
ret = Shapes.empty();
|
||||
}
|
||||
@@ -225,24 +224,6 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static VoxelShape tryForceBlock(final VoxelShape other) {
|
||||
if (other == Shapes.block()) {
|
||||
return other;
|
||||
}
|
||||
|
||||
final AABB otherAABB = ((CollisionVoxelShape)other).moonrise$getSingleAABBRepresentation();
|
||||
if (otherAABB == null) {
|
||||
return other;
|
||||
}
|
||||
|
||||
if (((CollisionVoxelShape)Shapes.block()).moonrise$getSingleAABBRepresentation().equals(otherAABB)) {
|
||||
return Shapes.block();
|
||||
}
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private boolean computeOccludesFullBlock() {
|
||||
if (this.isEmpty) {
|
||||
@@ -353,6 +334,115 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
return this.isEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Spottedleaf
|
||||
* @reason Use cached bounds
|
||||
*/
|
||||
@Overwrite
|
||||
public VoxelShape singleEncompassing() {
|
||||
if (this.isEmpty) {
|
||||
return Shapes.empty();
|
||||
}
|
||||
return Shapes.create(this.bounds());
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Spottedleaf
|
||||
* @reason Optimise implementation to avoid indirection
|
||||
*/
|
||||
@Overwrite
|
||||
protected double get(final Direction.Axis axis, final int idx) {
|
||||
switch (axis) {
|
||||
case X: {
|
||||
return this.rootCoordinatesX[idx] + this.offsetX;
|
||||
}
|
||||
case Y: {
|
||||
return this.rootCoordinatesY[idx] + this.offsetY;
|
||||
}
|
||||
case Z: {
|
||||
return this.rootCoordinatesZ[idx] + this.offsetZ;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalStateException("Unknown axis: " + axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Spottedleaf
|
||||
* @reason Optimise implementation to avoid indirection
|
||||
*/
|
||||
@Overwrite
|
||||
public int findIndex(final Direction.Axis axis, final double value) {
|
||||
switch (axis) {
|
||||
case X: {
|
||||
final double[] values = this.rootCoordinatesX;
|
||||
return CollisionUtil.findFloor(
|
||||
values, value - this.offsetX, 0, values.length - 1
|
||||
);
|
||||
}
|
||||
case Y: {
|
||||
final double[] values = this.rootCoordinatesY;
|
||||
return CollisionUtil.findFloor(
|
||||
values, value - this.offsetY, 0, values.length - 1
|
||||
);
|
||||
}
|
||||
case Z: {
|
||||
final double[] values = this.rootCoordinatesZ;
|
||||
return CollisionUtil.findFloor(
|
||||
values, value - this.offsetZ, 0, values.length - 1
|
||||
);
|
||||
}
|
||||
default: {
|
||||
throw new IllegalStateException("Unknown axis: " + axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private VoxelShape calculateFaceDirect(final Direction direction, final Direction.Axis axis, final double[] coords, final double offset) {
|
||||
if (coords.length == 2 &&
|
||||
DoubleMath.fuzzyEquals(coords[0] + offset, 0.0, CollisionUtil.COLLISION_EPSILON) &&
|
||||
DoubleMath.fuzzyEquals(coords[1] + offset, 1.0, CollisionUtil.COLLISION_EPSILON)) {
|
||||
return (VoxelShape)(Object)this;
|
||||
}
|
||||
|
||||
final boolean positiveDir = direction.getAxisDirection() == Direction.AxisDirection.POSITIVE;
|
||||
|
||||
// see findIndex
|
||||
final int index = CollisionUtil.findFloor(
|
||||
coords, (positiveDir ? (1.0 - CollisionUtil.COLLISION_EPSILON) : (0.0 + CollisionUtil.COLLISION_EPSILON)) - offset,
|
||||
0, coords.length - 1
|
||||
);
|
||||
|
||||
return CollisionUtil.sliceShape(
|
||||
(VoxelShape)(Object)this, axis, index
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Spottedleaf
|
||||
* @reason Avoid creating SliceShape
|
||||
*/
|
||||
@Overwrite
|
||||
public VoxelShape calculateFace(final Direction direction) {
|
||||
final Direction.Axis axis = direction.getAxis();
|
||||
switch (axis) {
|
||||
case X: {
|
||||
return this.calculateFaceDirect(direction, axis, this.rootCoordinatesX, this.offsetX);
|
||||
}
|
||||
case Y: {
|
||||
return this.calculateFaceDirect(direction, axis, this.rootCoordinatesY, this.offsetY);
|
||||
}
|
||||
case Z: {
|
||||
return this.calculateFaceDirect(direction, axis, this.rootCoordinatesZ, this.offsetZ);
|
||||
}
|
||||
default: {
|
||||
throw new IllegalStateException("Unknown axis: " + axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Spottedleaf
|
||||
* @reason Route to optimized collision method
|
||||
@@ -382,11 +472,12 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static DoubleList offsetList(final DoubleList src, final double by) {
|
||||
if (src instanceof OffsetDoubleList offsetDoubleList) {
|
||||
return new OffsetDoubleList(offsetDoubleList.delegate, by + offsetDoubleList.offset);
|
||||
private static DoubleList offsetList(final double[] src, final double by) {
|
||||
final DoubleArrayList wrap = DoubleArrayList.wrap(src);
|
||||
if (by == 0.0) {
|
||||
return wrap;
|
||||
}
|
||||
return new OffsetDoubleList(src, by);
|
||||
return new OffsetDoubleList(wrap, by);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -400,10 +491,10 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
}
|
||||
|
||||
final ArrayVoxelShape ret = new ArrayVoxelShape(
|
||||
this.shape,
|
||||
offsetList(this.getCoords(Direction.Axis.X), x),
|
||||
offsetList(this.getCoords(Direction.Axis.Y), y),
|
||||
offsetList(this.getCoords(Direction.Axis.Z), z)
|
||||
this.shape,
|
||||
offsetList(this.rootCoordinatesX, this.offsetX + x),
|
||||
offsetList(this.rootCoordinatesY, this.offsetY + y),
|
||||
offsetList(this.rootCoordinatesZ, this.offsetZ + z)
|
||||
);
|
||||
|
||||
final CachedToAABBs cachedToAABBs = this.cachedToAABBs;
|
||||
@@ -416,10 +507,12 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
|
||||
@Unique
|
||||
private List<AABB> toAabbsUncached() {
|
||||
final List<AABB> ret = new ArrayList<>();
|
||||
final List<AABB> ret;
|
||||
if (this.singleAABBRepresentation != null) {
|
||||
ret = new ArrayList<>(1);
|
||||
ret.add(this.singleAABBRepresentation);
|
||||
} else {
|
||||
ret = new ArrayList<>();
|
||||
final double[] coordsX = this.rootCoordinatesX;
|
||||
final double[] coordsY = this.rootCoordinatesY;
|
||||
final double[] coordsZ = this.rootCoordinatesZ;
|
||||
@@ -718,6 +811,11 @@ abstract class VoxelShapeMixin implements CollisionVoxelShape {
|
||||
|
||||
final List<AABB> aabbs = this.toAabbs();
|
||||
|
||||
if (aabbs.isEmpty()) {
|
||||
// We are a SliceShape, which does not properly fill isEmpty for every case
|
||||
return Shapes.empty();
|
||||
}
|
||||
|
||||
if (aabbs.size() == 1) {
|
||||
final AABB singleAABB = aabbs.get(0);
|
||||
final VoxelShape ret = Shapes.create(singleAABB);
|
||||
|
||||
@@ -4,8 +4,6 @@ import net.minecraft.client.multiplayer.resolver.ServerAddressResolver;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
@@ -29,9 +27,10 @@ interface ServerAddressResolverMixin {
|
||||
private static InetAddress eliminateRDNS(final String name) throws UnknownHostException {
|
||||
final InetAddress ret = InetAddress.getByName(name);
|
||||
|
||||
if (ret instanceof Inet4Address || ret instanceof Inet6Address) {
|
||||
final byte[] address = ret.getAddress();
|
||||
if (address != null) {
|
||||
// pass name to prevent rDNS
|
||||
return InetAddress.getByAddress(name, ret.getAddress());
|
||||
return InetAddress.getByAddress(name, address);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -35,9 +35,11 @@ import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
||||
import net.minecraft.world.phys.shapes.EntityCollisionContext;
|
||||
import net.minecraft.world.phys.shapes.OffsetDoubleList;
|
||||
import net.minecraft.world.phys.shapes.Shapes;
|
||||
import net.minecraft.world.phys.shapes.SliceShape;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@@ -162,7 +164,8 @@ public final class CollisionUtil {
|
||||
|
||||
// startIndex and endIndex inclusive
|
||||
// assumes indices are in range of array
|
||||
private static int findFloor(final double[] values, final double value, int startIndex, int endIndex) {
|
||||
public static int findFloor(final double[] values, final double value, int startIndex, int endIndex) {
|
||||
Objects.checkFromToIndex(startIndex, endIndex + 1, values.length);
|
||||
do {
|
||||
final int middle = (startIndex + endIndex) >>> 1;
|
||||
final double middleVal = values[middle];
|
||||
@@ -177,6 +180,214 @@ public final class CollisionUtil {
|
||||
return startIndex - 1;
|
||||
}
|
||||
|
||||
private static VoxelShape sliceShapeVanilla(final VoxelShape src, final Direction.Axis axis,
|
||||
final int index) {
|
||||
return new SliceShape(src, axis, index);
|
||||
}
|
||||
|
||||
private static DoubleList offsetList(final double[] src, final double by) {
|
||||
final DoubleArrayList wrap = DoubleArrayList.wrap(src);
|
||||
if (by == 0.0) {
|
||||
return wrap;
|
||||
}
|
||||
return new OffsetDoubleList(wrap, by);
|
||||
}
|
||||
|
||||
private static VoxelShape sliceShapeOptimised(final VoxelShape src, final Direction.Axis axis,
|
||||
final int index) {
|
||||
// assume index in range
|
||||
final double off_x = ((CollisionVoxelShape)src).moonrise$offsetX();
|
||||
final double off_y = ((CollisionVoxelShape)src).moonrise$offsetY();
|
||||
final double off_z = ((CollisionVoxelShape)src).moonrise$offsetZ();
|
||||
|
||||
final double[] coords_x = ((CollisionVoxelShape)src).moonrise$rootCoordinatesX();
|
||||
final double[] coords_y = ((CollisionVoxelShape)src).moonrise$rootCoordinatesY();
|
||||
final double[] coords_z = ((CollisionVoxelShape)src).moonrise$rootCoordinatesZ();
|
||||
|
||||
final CachedShapeData cached_shape_data = ((CollisionVoxelShape)src).moonrise$getCachedVoxelData();
|
||||
|
||||
// note: size = coords.length - 1
|
||||
final int size_x = cached_shape_data.sizeX();
|
||||
final int size_y = cached_shape_data.sizeY();
|
||||
final int size_z = cached_shape_data.sizeZ();
|
||||
|
||||
final long[] bitset = cached_shape_data.voxelSet();
|
||||
|
||||
final DoubleList list_x;
|
||||
final DoubleList list_y;
|
||||
final DoubleList list_z;
|
||||
final int shape_sx;
|
||||
final int shape_ex;
|
||||
final int shape_sy;
|
||||
final int shape_ey;
|
||||
final int shape_sz;
|
||||
final int shape_ez;
|
||||
|
||||
switch (axis) {
|
||||
case X: {
|
||||
// validate index
|
||||
if (index < 0 || index >= size_x) {
|
||||
return Shapes.empty();
|
||||
}
|
||||
|
||||
// test if input is already "sliced"
|
||||
if (coords_x.length == 2 && (coords_x[0] + off_x) == 0.0 && (coords_x[1] + off_x) == 1.0) {
|
||||
return src;
|
||||
}
|
||||
|
||||
// test if result would be full box
|
||||
if (coords_y.length == 2 && coords_z.length == 2 &&
|
||||
(coords_y[0] + off_y) == 0.0 && (coords_y[1] + off_y) == 1.0 &&
|
||||
(coords_z[0] + off_z) == 0.0 && (coords_z[1] + off_z) == 1.0) {
|
||||
// note: size_y == size_z == 1
|
||||
final int bitIdx = 0 + 0*size_z + index*(size_z*size_y);
|
||||
return (bitset[bitIdx >>> 6] & (1L << bitIdx)) == 0L ? Shapes.empty() : Shapes.block();
|
||||
}
|
||||
|
||||
list_x = ZERO_ONE;
|
||||
list_y = offsetList(coords_y, off_y);
|
||||
list_z = offsetList(coords_z, off_z);
|
||||
shape_sx = index;
|
||||
shape_ex = index + 1;
|
||||
shape_sy = 0;
|
||||
shape_ey = size_y;
|
||||
shape_sz = 0;
|
||||
shape_ez = size_z;
|
||||
|
||||
break;
|
||||
}
|
||||
case Y: {
|
||||
// validate index
|
||||
if (index < 0 || index >= size_y) {
|
||||
return Shapes.empty();
|
||||
}
|
||||
|
||||
// test if input is already "sliced"
|
||||
if (coords_y.length == 2 && (coords_y[0] + off_y) == 0.0 && (coords_y[1] + off_y) == 1.0) {
|
||||
return src;
|
||||
}
|
||||
|
||||
// test if result would be full box
|
||||
if (coords_x.length == 2 && coords_z.length == 2 &&
|
||||
(coords_x[0] + off_x) == 0.0 && (coords_x[1] + off_x) == 1.0 &&
|
||||
(coords_z[0] + off_z) == 0.0 && (coords_z[1] + off_z) == 1.0) {
|
||||
// note: size_x == size_z == 1
|
||||
final int bitIdx = 0 + index*size_z + 0*(size_z*size_y);
|
||||
return (bitset[bitIdx >>> 6] & (1L << bitIdx)) == 0L ? Shapes.empty() : Shapes.block();
|
||||
}
|
||||
|
||||
list_x = offsetList(coords_x, off_x);
|
||||
list_y = ZERO_ONE;
|
||||
list_z = offsetList(coords_z, off_z);
|
||||
shape_sx = 0;
|
||||
shape_ex = size_x;
|
||||
shape_sy = index;
|
||||
shape_ey = index + 1;
|
||||
shape_sz = 0;
|
||||
shape_ez = size_z;
|
||||
|
||||
break;
|
||||
}
|
||||
case Z: {
|
||||
// validate index
|
||||
if (index < 0 || index >= size_z) {
|
||||
return Shapes.empty();
|
||||
}
|
||||
|
||||
// test if input is already "sliced"
|
||||
if (coords_z.length == 2 && (coords_z[0] + off_z) == 0.0 && (coords_z[1] + off_z) == 1.0) {
|
||||
return src;
|
||||
}
|
||||
|
||||
// test if result would be full box
|
||||
if (coords_x.length == 2 && coords_y.length == 2 &&
|
||||
(coords_x[0] + off_x) == 0.0 && (coords_x[1] + off_x) == 1.0 &&
|
||||
(coords_y[0] + off_y) == 0.0 && (coords_y[1] + off_y) == 1.0) {
|
||||
// note: size_x == size_y == 1
|
||||
final int bitIdx = index + 0*size_z + 0*(size_z*size_y);
|
||||
return (bitset[bitIdx >>> 6] & (1L << bitIdx)) == 0L ? Shapes.empty() : Shapes.block();
|
||||
}
|
||||
|
||||
list_x = offsetList(coords_x, off_x);
|
||||
list_y = offsetList(coords_y, off_y);
|
||||
list_z = ZERO_ONE;
|
||||
shape_sx = 0;
|
||||
shape_ex = size_x;
|
||||
shape_sy = 0;
|
||||
shape_ey = size_y;
|
||||
shape_sz = index;
|
||||
shape_ez = index + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalStateException("Unknown axis: " + axis);
|
||||
}
|
||||
}
|
||||
|
||||
final int local_len_x = shape_ex - shape_sx;
|
||||
final int local_len_y = shape_ey - shape_sy;
|
||||
final int local_len_z = shape_ez - shape_sz;
|
||||
|
||||
final BitSetDiscreteVoxelShape shape = new BitSetDiscreteVoxelShape(local_len_x, local_len_y, local_len_z);
|
||||
|
||||
final int idx_off = shape_sz + shape_sy*size_z + shape_sx*(size_z*size_y);
|
||||
for (int x = 0; x < local_len_x; ++x) {
|
||||
boolean setX = false;
|
||||
for (int y = 0; y < local_len_y; ++y) {
|
||||
boolean setY = false;
|
||||
for (int z = 0; z < local_len_z; ++z) {
|
||||
final int unslicedIdx = idx_off + z + y*size_z + x*(size_z*size_y);
|
||||
if ((bitset[unslicedIdx >>> 6] & (1L << unslicedIdx)) == 0L) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setY = true;
|
||||
setX = true;
|
||||
shape.zMin = Math.min(shape.zMin, z);
|
||||
shape.zMax = Math.max(shape.zMax, z + 1);
|
||||
|
||||
shape.storage.set(
|
||||
z + y*local_len_z + x*(local_len_y*local_len_z)
|
||||
);
|
||||
}
|
||||
|
||||
if (setY) {
|
||||
shape.yMin = Math.min(shape.yMin, y);
|
||||
shape.yMax = Math.max(shape.yMax, y + 1);
|
||||
}
|
||||
}
|
||||
if (setX) {
|
||||
shape.xMin = Math.min(shape.xMin, x);
|
||||
shape.xMax = Math.max(shape.xMax, x + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return shape.isEmpty() ? Shapes.empty() : new ArrayVoxelShape(
|
||||
shape, list_x, list_y, list_z
|
||||
);
|
||||
}
|
||||
|
||||
private static final boolean DEBUG_SLICE_SHAPE = false;
|
||||
|
||||
public static VoxelShape sliceShape(final VoxelShape src, final Direction.Axis axis,
|
||||
final int index) {
|
||||
final VoxelShape ret = sliceShapeOptimised(src, axis, index);
|
||||
if (DEBUG_SLICE_SHAPE) {
|
||||
final VoxelShape vanilla = sliceShapeVanilla(src, axis, index);
|
||||
if (!equals(ret, vanilla)) {
|
||||
// special case: SliceShape is not empty when it should be!
|
||||
if (areAnyFull(ret.shape) || areAnyFull(vanilla.shape)) {
|
||||
equals(ret, vanilla);
|
||||
sliceShapeOptimised(src, axis, index);
|
||||
throw new IllegalStateException("Slice shape mismatch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static boolean voxelShapeIntersectNoEmpty(final VoxelShape voxel, final AABB aabb) {
|
||||
if (voxel.isEmpty()) {
|
||||
return false;
|
||||
@@ -1306,7 +1517,7 @@ public final class CollisionUtil {
|
||||
return true;
|
||||
} else if (isEmpty1 ^ isEmpty2) {
|
||||
return false;
|
||||
}
|
||||
} // else: isEmpty1 = isEmpty2 = false
|
||||
|
||||
if (cachedShapeData1.hasSingleAABB() != cachedShapeData2.hasSingleAABB()) {
|
||||
return false;
|
||||
@@ -1327,6 +1538,12 @@ public final class CollisionUtil {
|
||||
|
||||
// useful only for testing
|
||||
public static boolean equals(final VoxelShape shape1, final VoxelShape shape2) {
|
||||
if (shape1.isEmpty() & shape2.isEmpty()) {
|
||||
return true;
|
||||
} else if (shape1.isEmpty() ^ shape2.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!equals(shape1.shape, shape2.shape)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1336,6 +1553,84 @@ public final class CollisionUtil {
|
||||
shape1.getCoords(Direction.Axis.Z).equals(shape2.getCoords(Direction.Axis.Z));
|
||||
}
|
||||
|
||||
public static boolean areAnyFull(final DiscreteVoxelShape shape) {
|
||||
if (shape.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int sizeX = shape.getXSize();
|
||||
final int sizeY = shape.getYSize();
|
||||
final int sizeZ = shape.getZSize();
|
||||
|
||||
for (int x = 0; x < sizeX; ++x) {
|
||||
for (int y = 0; y < sizeY; ++y) {
|
||||
for (int z = 0; z < sizeZ; ++z) {
|
||||
if (shape.isFull(x, y, z)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String shapeMismatch(final DiscreteVoxelShape shape1, final DiscreteVoxelShape shape2) {
|
||||
final CachedShapeData cachedShapeData1 = ((CollisionDiscreteVoxelShape)shape1).moonrise$getOrCreateCachedShapeData();
|
||||
final CachedShapeData cachedShapeData2 = ((CollisionDiscreteVoxelShape)shape2).moonrise$getOrCreateCachedShapeData();
|
||||
|
||||
final boolean isEmpty1 = cachedShapeData1.isEmpty();
|
||||
final boolean isEmpty2 = cachedShapeData2.isEmpty();
|
||||
|
||||
if (isEmpty1 & isEmpty2) {
|
||||
return null;
|
||||
} else if (isEmpty1 ^ isEmpty2) {
|
||||
return null;
|
||||
} // else: isEmpty1 = isEmpty2 = false
|
||||
|
||||
if (cachedShapeData1.sizeX() != cachedShapeData2.sizeX()) {
|
||||
return "size x: " + cachedShapeData1.sizeX() + " != " + cachedShapeData2.sizeX();
|
||||
}
|
||||
if (cachedShapeData1.sizeY() != cachedShapeData2.sizeY()) {
|
||||
return "size y: " + cachedShapeData1.sizeY() + " != " + cachedShapeData2.sizeY();
|
||||
}
|
||||
if (cachedShapeData1.sizeZ() != cachedShapeData2.sizeZ()) {
|
||||
return "size z: " + cachedShapeData1.sizeZ() + " != " + cachedShapeData2.sizeZ();
|
||||
}
|
||||
|
||||
final StringBuilder ret = new StringBuilder();
|
||||
|
||||
final int sizeX = cachedShapeData1.sizeX();;
|
||||
final int sizeY = cachedShapeData1.sizeY();
|
||||
final int sizeZ = cachedShapeData1.sizeZ();
|
||||
|
||||
boolean first = true;
|
||||
|
||||
for (int x = 0; x < sizeX; ++x) {
|
||||
for (int y = 0; y < sizeY; ++y) {
|
||||
for (int z = 0; z < sizeZ; ++z) {
|
||||
final boolean isFull1 = shape1.isFull(x, y, z);
|
||||
final boolean isFull2 = shape2.isFull(x, y, z);
|
||||
|
||||
if (isFull1 == isFull2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
ret.append(", ");
|
||||
}
|
||||
|
||||
ret.append("(").append(x).append(",").append(y).append(",").append(z)
|
||||
.append("): shape1: ").append(isFull1).append(", shape2: ").append(isFull2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret.isEmpty() ? null : ret.toString();
|
||||
}
|
||||
|
||||
public static AABB offsetX(final AABB box, final double dx) {
|
||||
return new AABB(box.minX + dx, box.minY, box.minZ, box.maxX + dx, box.maxY, box.maxZ);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user