454 lines
20 KiB
Java
454 lines
20 KiB
Java
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<VoxelShape> 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<Direction.Axis, VoxelShape> rotateHorizontalAxis(VoxelShape p_393602_) {
|
|
return rotateHorizontalAxis(p_393602_, BLOCK_CENTER);
|
|
}
|
|
|
|
public static Map<Direction.Axis, VoxelShape> 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<Direction.Axis, VoxelShape> rotateAllAxis(VoxelShape p_393929_) {
|
|
return rotateAllAxis(p_393929_, BLOCK_CENTER);
|
|
}
|
|
|
|
public static Map<Direction.Axis, VoxelShape> 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<Direction, VoxelShape> rotateHorizontal(VoxelShape p_395124_) {
|
|
return rotateHorizontal(p_395124_, BLOCK_CENTER);
|
|
}
|
|
|
|
public static Map<Direction, VoxelShape> 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<Direction, VoxelShape> rotateAll(VoxelShape p_392088_) {
|
|
return rotateAll(p_392088_, BLOCK_CENTER);
|
|
}
|
|
|
|
public static Map<Direction, VoxelShape> 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<AttachFace, Map<Direction, VoxelShape>> 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_);
|
|
}
|
|
} |