package net.minecraft.world.phys.shapes; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps; import com.google.common.math.DoubleMath; import com.google.common.math.IntMath; import com.mojang.math.OctahedralGroup; import com.mojang.math.Quadrant; import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import it.unimi.dsi.fastutil.doubles.DoubleList; import java.util.Arrays; import java.util.Map; import java.util.Objects; import net.minecraft.Util; import net.minecraft.core.AxisCycle; import net.minecraft.core.Direction; import net.minecraft.world.level.block.state.properties.AttachFace; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; public final class Shapes { public static final double EPSILON = 1.0E-7; public static final double BIG_EPSILON = 1.0E-6; private static final VoxelShape BLOCK = Util.make(() -> { DiscreteVoxelShape discretevoxelshape = new BitSetDiscreteVoxelShape(1, 1, 1); discretevoxelshape.fill(0, 0, 0); return new CubeVoxelShape(discretevoxelshape); }); private static final Vec3 BLOCK_CENTER = new Vec3(0.5, 0.5, 0.5); public static final VoxelShape INFINITY = box( Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY ); private static final VoxelShape EMPTY = new ArrayVoxelShape( new BitSetDiscreteVoxelShape(0, 0, 0), new DoubleArrayList(new double[]{0.0}), new DoubleArrayList(new double[]{0.0}), new DoubleArrayList(new double[]{0.0}) ); public static VoxelShape empty() { return EMPTY; } public static VoxelShape block() { return BLOCK; } public static VoxelShape box(double p_83049_, double p_83050_, double p_83051_, double p_83052_, double p_83053_, double p_83054_) { if (!(p_83049_ > p_83052_) && !(p_83050_ > p_83053_) && !(p_83051_ > p_83054_)) { return create(p_83049_, p_83050_, p_83051_, p_83052_, p_83053_, p_83054_); } else { throw new IllegalArgumentException("The min values need to be smaller or equals to the max values"); } } public static VoxelShape create(double p_166050_, double p_166051_, double p_166052_, double p_166053_, double p_166054_, double p_166055_) { if (!(p_166053_ - p_166050_ < 1.0E-7) && !(p_166054_ - p_166051_ < 1.0E-7) && !(p_166055_ - p_166052_ < 1.0E-7)) { int i = findBits(p_166050_, p_166053_); int j = findBits(p_166051_, p_166054_); int k = findBits(p_166052_, p_166055_); if (i < 0 || j < 0 || k < 0) { return new ArrayVoxelShape( BLOCK.shape, DoubleArrayList.wrap(new double[]{p_166050_, p_166053_}), DoubleArrayList.wrap(new double[]{p_166051_, p_166054_}), DoubleArrayList.wrap(new double[]{p_166052_, p_166055_}) ); } else if (i == 0 && j == 0 && k == 0) { return block(); } else { int l = 1 << i; int i1 = 1 << j; int j1 = 1 << k; BitSetDiscreteVoxelShape bitsetdiscretevoxelshape = BitSetDiscreteVoxelShape.withFilledBounds( l, i1, j1, (int)Math.round(p_166050_ * l), (int)Math.round(p_166051_ * i1), (int)Math.round(p_166052_ * j1), (int)Math.round(p_166053_ * l), (int)Math.round(p_166054_ * i1), (int)Math.round(p_166055_ * j1) ); return new CubeVoxelShape(bitsetdiscretevoxelshape); } } else { return empty(); } } public static VoxelShape create(AABB p_83065_) { return create(p_83065_.minX, p_83065_.minY, p_83065_.minZ, p_83065_.maxX, p_83065_.maxY, p_83065_.maxZ); } @VisibleForTesting protected static int findBits(double p_83042_, double p_83043_) { if (!(p_83042_ < -1.0E-7) && !(p_83043_ > 1.0000001)) { for (int i = 0; i <= 3; i++) { int j = 1 << i; double d0 = p_83042_ * j; double d1 = p_83043_ * j; boolean flag = Math.abs(d0 - Math.round(d0)) < 1.0E-7 * j; boolean flag1 = Math.abs(d1 - Math.round(d1)) < 1.0E-7 * j; if (flag && flag1) { return i; } } return -1; } else { return -1; } } protected static long lcm(int p_83056_, int p_83057_) { return (long)p_83056_ * (p_83057_ / IntMath.gcd(p_83056_, p_83057_)); } public static VoxelShape or(VoxelShape p_83111_, VoxelShape p_83112_) { return join(p_83111_, p_83112_, BooleanOp.OR); } public static VoxelShape or(VoxelShape p_83125_, VoxelShape... p_83126_) { return Arrays.stream(p_83126_).reduce(p_83125_, Shapes::or); } public static VoxelShape join(VoxelShape p_83114_, VoxelShape p_83115_, BooleanOp p_83116_) { return joinUnoptimized(p_83114_, p_83115_, p_83116_).optimize(); } public static VoxelShape joinUnoptimized(VoxelShape p_83149_, VoxelShape p_83150_, BooleanOp p_83151_) { if (p_83151_.apply(false, false)) { throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); } else if (p_83149_ == p_83150_) { return p_83151_.apply(true, true) ? p_83149_ : empty(); } else { boolean flag = p_83151_.apply(true, false); boolean flag1 = p_83151_.apply(false, true); if (p_83149_.isEmpty()) { return flag1 ? p_83150_ : empty(); } else if (p_83150_.isEmpty()) { return flag ? p_83149_ : empty(); } else { IndexMerger indexmerger = createIndexMerger(1, p_83149_.getCoords(Direction.Axis.X), p_83150_.getCoords(Direction.Axis.X), flag, flag1); IndexMerger indexmerger1 = createIndexMerger(indexmerger.size() - 1, p_83149_.getCoords(Direction.Axis.Y), p_83150_.getCoords(Direction.Axis.Y), flag, flag1); IndexMerger indexmerger2 = createIndexMerger( (indexmerger.size() - 1) * (indexmerger1.size() - 1), p_83149_.getCoords(Direction.Axis.Z), p_83150_.getCoords(Direction.Axis.Z), flag, flag1 ); BitSetDiscreteVoxelShape bitsetdiscretevoxelshape = BitSetDiscreteVoxelShape.join( p_83149_.shape, p_83150_.shape, indexmerger, indexmerger1, indexmerger2, p_83151_ ); return (VoxelShape)(indexmerger instanceof DiscreteCubeMerger && indexmerger1 instanceof DiscreteCubeMerger && indexmerger2 instanceof DiscreteCubeMerger ? new CubeVoxelShape(bitsetdiscretevoxelshape) : new ArrayVoxelShape(bitsetdiscretevoxelshape, indexmerger.getList(), indexmerger1.getList(), indexmerger2.getList())); } } } public static boolean joinIsNotEmpty(VoxelShape p_83158_, VoxelShape p_83159_, BooleanOp p_83160_) { if (p_83160_.apply(false, false)) { throw (IllegalArgumentException)Util.pauseInIde(new IllegalArgumentException()); } else { boolean flag = p_83158_.isEmpty(); boolean flag1 = p_83159_.isEmpty(); if (!flag && !flag1) { if (p_83158_ == p_83159_) { return p_83160_.apply(true, true); } else { boolean flag2 = p_83160_.apply(true, false); boolean flag3 = p_83160_.apply(false, true); for (Direction.Axis direction$axis : AxisCycle.AXIS_VALUES) { if (p_83158_.max(direction$axis) < p_83159_.min(direction$axis) - 1.0E-7) { return flag2 || flag3; } if (p_83159_.max(direction$axis) < p_83158_.min(direction$axis) - 1.0E-7) { return flag2 || flag3; } } IndexMerger indexmerger = createIndexMerger(1, p_83158_.getCoords(Direction.Axis.X), p_83159_.getCoords(Direction.Axis.X), flag2, flag3); IndexMerger indexmerger1 = createIndexMerger( indexmerger.size() - 1, p_83158_.getCoords(Direction.Axis.Y), p_83159_.getCoords(Direction.Axis.Y), flag2, flag3 ); IndexMerger indexmerger2 = createIndexMerger( (indexmerger.size() - 1) * (indexmerger1.size() - 1), p_83158_.getCoords(Direction.Axis.Z), p_83159_.getCoords(Direction.Axis.Z), flag2, flag3 ); return joinIsNotEmpty(indexmerger, indexmerger1, indexmerger2, p_83158_.shape, p_83159_.shape, p_83160_); } } else { return p_83160_.apply(!flag, !flag1); } } } private static boolean joinIsNotEmpty( IndexMerger p_83104_, IndexMerger p_83105_, IndexMerger p_83106_, DiscreteVoxelShape p_83107_, DiscreteVoxelShape p_83108_, BooleanOp p_83109_ ) { return !p_83104_.forMergedIndexes( (p_83100_, p_83101_, p_83102_) -> p_83105_.forMergedIndexes( (p_166046_, p_166047_, p_166048_) -> p_83106_.forMergedIndexes( (p_166036_, p_166037_, p_166038_) -> !p_83109_.apply( p_83107_.isFullWide(p_83100_, p_166046_, p_166036_), p_83108_.isFullWide(p_83101_, p_166047_, p_166037_) ) ) ) ); } public static double collide(Direction.Axis p_193136_, AABB p_193137_, Iterable p_193138_, double p_193139_) { for (VoxelShape voxelshape : p_193138_) { if (Math.abs(p_193139_) < 1.0E-7) { return 0.0; } p_193139_ = voxelshape.collide(p_193136_, p_193137_, p_193139_); } return p_193139_; } public static boolean blockOccludes(VoxelShape p_83118_, VoxelShape p_83119_, Direction p_83120_) { if (p_83118_ == block() && p_83119_ == block()) { return true; } else if (p_83119_.isEmpty()) { return false; } else { Direction.Axis direction$axis = p_83120_.getAxis(); Direction.AxisDirection direction$axisdirection = p_83120_.getAxisDirection(); VoxelShape voxelshape = direction$axisdirection == Direction.AxisDirection.POSITIVE ? p_83118_ : p_83119_; VoxelShape voxelshape1 = direction$axisdirection == Direction.AxisDirection.POSITIVE ? p_83119_ : p_83118_; BooleanOp booleanop = direction$axisdirection == Direction.AxisDirection.POSITIVE ? BooleanOp.ONLY_FIRST : BooleanOp.ONLY_SECOND; return DoubleMath.fuzzyEquals(voxelshape.max(direction$axis), 1.0, 1.0E-7) && DoubleMath.fuzzyEquals(voxelshape1.min(direction$axis), 0.0, 1.0E-7) && !joinIsNotEmpty( new SliceShape(voxelshape, direction$axis, voxelshape.shape.getSize(direction$axis) - 1), new SliceShape(voxelshape1, direction$axis, 0), booleanop ); } } public static boolean mergedFaceOccludes(VoxelShape p_83153_, VoxelShape p_83154_, Direction p_83155_) { if (p_83153_ != block() && p_83154_ != block()) { Direction.Axis direction$axis = p_83155_.getAxis(); Direction.AxisDirection direction$axisdirection = p_83155_.getAxisDirection(); VoxelShape voxelshape = direction$axisdirection == Direction.AxisDirection.POSITIVE ? p_83153_ : p_83154_; VoxelShape voxelshape1 = direction$axisdirection == Direction.AxisDirection.POSITIVE ? p_83154_ : p_83153_; if (!DoubleMath.fuzzyEquals(voxelshape.max(direction$axis), 1.0, 1.0E-7)) { voxelshape = empty(); } if (!DoubleMath.fuzzyEquals(voxelshape1.min(direction$axis), 0.0, 1.0E-7)) { voxelshape1 = empty(); } return !joinIsNotEmpty( block(), joinUnoptimized( new SliceShape(voxelshape, direction$axis, voxelshape.shape.getSize(direction$axis) - 1), new SliceShape(voxelshape1, direction$axis, 0), BooleanOp.OR ), BooleanOp.ONLY_FIRST ); } else { return true; } } public static boolean faceShapeOccludes(VoxelShape p_83146_, VoxelShape p_83147_) { if (p_83146_ == block() || p_83147_ == block()) { return true; } else { return p_83146_.isEmpty() && p_83147_.isEmpty() ? false : !joinIsNotEmpty(block(), joinUnoptimized(p_83146_, p_83147_, BooleanOp.OR), BooleanOp.ONLY_FIRST); } } @VisibleForTesting protected static IndexMerger createIndexMerger(int p_83059_, DoubleList p_83060_, DoubleList p_83061_, boolean p_83062_, boolean p_83063_) { int i = p_83060_.size() - 1; int j = p_83061_.size() - 1; if (p_83060_ instanceof CubePointRange && p_83061_ instanceof CubePointRange) { long k = lcm(i, j); if (p_83059_ * k <= 256L) { return new DiscreteCubeMerger(i, j); } } if (p_83060_.getDouble(i) < p_83061_.getDouble(0) - 1.0E-7) { return new NonOverlappingMerger(p_83060_, p_83061_, false); } else if (p_83061_.getDouble(j) < p_83060_.getDouble(0) - 1.0E-7) { return new NonOverlappingMerger(p_83061_, p_83060_, true); } else { return (IndexMerger)(i == j && Objects.equals(p_83060_, p_83061_) ? new IdenticalMerger(p_83060_) : new IndirectMerger(p_83060_, p_83061_, p_83062_, p_83063_)); } } public static VoxelShape rotate(VoxelShape p_392699_, OctahedralGroup p_396102_) { return rotate(p_392699_, p_396102_, BLOCK_CENTER); } public static VoxelShape rotate(VoxelShape p_392970_, OctahedralGroup p_392838_, Vec3 p_394301_) { if (p_392838_ == OctahedralGroup.IDENTITY) { return p_392970_; } else { DiscreteVoxelShape discretevoxelshape = p_392970_.shape.rotate(p_392838_); if (p_392970_ instanceof CubeVoxelShape && BLOCK_CENTER.equals(p_394301_)) { return new CubeVoxelShape(discretevoxelshape); } else { Direction.Axis direction$axis = p_392838_.permute(Direction.Axis.X); Direction.Axis direction$axis1 = p_392838_.permute(Direction.Axis.Y); Direction.Axis direction$axis2 = p_392838_.permute(Direction.Axis.Z); DoubleList doublelist = p_392970_.getCoords(direction$axis); DoubleList doublelist1 = p_392970_.getCoords(direction$axis1); DoubleList doublelist2 = p_392970_.getCoords(direction$axis2); boolean flag = p_392838_.inverts(direction$axis); boolean flag1 = p_392838_.inverts(direction$axis1); boolean flag2 = p_392838_.inverts(direction$axis2); boolean flag3 = direction$axis.choose(flag, flag1, flag2); boolean flag4 = direction$axis1.choose(flag, flag1, flag2); boolean flag5 = direction$axis2.choose(flag, flag1, flag2); return new ArrayVoxelShape( discretevoxelshape, makeAxis(doublelist, flag3, p_394301_.get(direction$axis), p_394301_.x), makeAxis(doublelist1, flag4, p_394301_.get(direction$axis1), p_394301_.y), makeAxis(doublelist2, flag5, p_394301_.get(direction$axis2), p_394301_.z) ); } } } @VisibleForTesting static DoubleList makeAxis(DoubleList p_396173_, boolean p_392786_, double p_397154_, double p_392952_) { if (!p_392786_ && p_397154_ == p_392952_) { return p_396173_; } else { int i = p_396173_.size(); DoubleList doublelist = new DoubleArrayList(i); int j = p_392786_ ? -1 : 1; for (int k = p_392786_ ? i - 1 : 0; k >= 0 && k < i; k += j) { doublelist.add(p_392952_ + j * (p_396173_.getDouble(k) - p_397154_)); } return doublelist; } } public static boolean equal(VoxelShape p_397633_, VoxelShape p_393144_) { return !joinIsNotEmpty(p_397633_, p_393144_, BooleanOp.NOT_SAME); } public static Map rotateHorizontalAxis(VoxelShape p_393602_) { return rotateHorizontalAxis(p_393602_, BLOCK_CENTER); } public static Map rotateHorizontalAxis(VoxelShape p_392909_, Vec3 p_395722_) { return Maps.newEnumMap( Map.of(Direction.Axis.Z, p_392909_, Direction.Axis.X, rotate(p_392909_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R90), p_395722_)) ); } public static Map rotateAllAxis(VoxelShape p_393929_) { return rotateAllAxis(p_393929_, BLOCK_CENTER); } public static Map rotateAllAxis(VoxelShape p_395366_, Vec3 p_391541_) { return Maps.newEnumMap( Map.of( Direction.Axis.Z, p_395366_, Direction.Axis.X, rotate(p_395366_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R90), p_391541_), Direction.Axis.Y, rotate(p_395366_, OctahedralGroup.fromXYAngles(Quadrant.R90, Quadrant.R0), p_391541_) ) ); } public static Map rotateHorizontal(VoxelShape p_395124_) { return rotateHorizontal(p_395124_, BLOCK_CENTER); } public static Map rotateHorizontal(VoxelShape p_393025_, Vec3 p_394205_) { return Maps.newEnumMap( Map.of( Direction.NORTH, p_393025_, Direction.EAST, rotate(p_393025_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R90), p_394205_), Direction.SOUTH, rotate(p_393025_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R180), p_394205_), Direction.WEST, rotate(p_393025_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R270), p_394205_) ) ); } public static Map rotateAll(VoxelShape p_392088_) { return rotateAll(p_392088_, BLOCK_CENTER); } public static Map rotateAll(VoxelShape p_391678_, Vec3 p_391672_) { return Maps.newEnumMap( Map.of( Direction.NORTH, p_391678_, Direction.EAST, rotate(p_391678_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R90), p_391672_), Direction.SOUTH, rotate(p_391678_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R180), p_391672_), Direction.WEST, rotate(p_391678_, OctahedralGroup.fromXYAngles(Quadrant.R0, Quadrant.R270), p_391672_), Direction.UP, rotate(p_391678_, OctahedralGroup.fromXYAngles(Quadrant.R270, Quadrant.R0), p_391672_), Direction.DOWN, rotate(p_391678_, OctahedralGroup.fromXYAngles(Quadrant.R90, Quadrant.R0), p_391672_) ) ); } public static Map> rotateAttachFace(VoxelShape p_394424_) { return Map.of( AttachFace.WALL, rotateHorizontal(p_394424_), AttachFace.FLOOR, rotateHorizontal(rotate(p_394424_, OctahedralGroup.fromXYAngles(Quadrant.R270, Quadrant.R0))), AttachFace.CEILING, rotateHorizontal(rotate(p_394424_, OctahedralGroup.fromXYAngles(Quadrant.R90, Quadrant.R180))) ); } public interface DoubleLineConsumer { void consume(double p_83162_, double p_83163_, double p_83164_, double p_83165_, double p_83166_, double p_83167_); } }