Private
Public Access
1
0

Implemented SegmentableBlock as is in 1.21.5, including all needed misc classes etc.

This commit is contained in:
2026-06-09 20:54:53 +01:00
parent 5764fba7c9
commit 6ce207ad14
12 changed files with 324 additions and 0 deletions

View File

@ -0,0 +1,57 @@
package dev.micle.wildflowers_backport.block;
import dev.micle.wildflowers_backport.block.state.properties.ModBlockStateProperties;
import dev.micle.wildflowers_backport.util.ShapesUtils;
import dev.micle.wildflowers_backport.util.VoxelShapeUtils;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.Map;
import java.util.function.Function;
public interface SegmentableBlock {
int MIN_SEGMENT = 1;
int MAX_SEGMENT = 4;
IntegerProperty AMOUNT = ModBlockStateProperties.SEGMENT_AMOUNT;
default Function<BlockState, VoxelShape> getShapeCalculator(EnumProperty<Direction> dir, IntegerProperty property) {
Map<Direction, VoxelShape> map = ShapesUtils.rotateHorizontal(Block.box(0.0, 0.0, 0.0, 8.0, this.getShapeHeight(), 8.0));
return x -> {
VoxelShape voxelShape = Shapes.empty();
Direction direction = x.getValue(dir);
int i = x.getValue(property);
for (int j = 0; j < i; j++) {
voxelShape = Shapes.or(voxelShape, map.get(direction));
direction = direction.getCounterClockWise();
}
return VoxelShapeUtils.singleEncompassing(voxelShape);
};
}
default IntegerProperty getSegmentAmountProperty() {
return AMOUNT;
}
default double getShapeHeight() {
return 1.0;
}
default boolean canBeReplaced(BlockState blockState, BlockPlaceContext blockPlaceContext, IntegerProperty property) {
return !blockPlaceContext.isSecondaryUseActive() && blockPlaceContext.getItemInHand().is(blockState.getBlock().asItem()) && blockState.getValue(property) < 4;
}
default BlockState getStateForPlacement(BlockPlaceContext blockPlaceContext, Block block, IntegerProperty property, EnumProperty<Direction> dir) {
BlockState blockState = blockPlaceContext.getLevel().getBlockState(blockPlaceContext.getClickedPos());
return blockState.is(block)
? blockState.setValue(property, Math.min(4, blockState.getValue(property) + 1))
: block.defaultBlockState().setValue(dir, blockPlaceContext.getHorizontalDirection().getOpposite());
}
}

View File

@ -0,0 +1,7 @@
package dev.micle.wildflowers_backport.block.state.properties;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
public class ModBlockStateProperties {
public static final IntegerProperty SEGMENT_AMOUNT = IntegerProperty.create("segment_amount", 1, 4);
}

View File

@ -0,0 +1,34 @@
package dev.micle.wildflowers_backport.math;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import net.minecraft.util.Mth;
public enum Quadrant {
R0(0),
R90(1),
R180(2),
R270(3);
public static final Codec<Quadrant> CODEC = Codec.INT.comapFlatMap(x -> {
return switch (Mth.positiveModulo(x, 360)) {
case 0 -> DataResult.success(R0);
case 90 -> DataResult.success(R90);
case 180 -> DataResult.success(R180);
case 270 -> DataResult.success(R270);
default -> DataResult.error(() -> "Invalid rotation " + x + " found, only 0/90/180/270 allowed");
};
}, y -> {
return switch (y) {
case R0 -> 0;
case R90 -> 90;
case R180 -> 180;
case R270 -> 270;
};
});
public final int shift;
Quadrant(final int shift) {
this.shift = shift;
}
}

View File

@ -0,0 +1,15 @@
package dev.micle.wildflowers_backport.mixin;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import net.minecraft.world.phys.shapes.ArrayVoxelShape;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(ArrayVoxelShape.class)
public interface ArrayVoxelShapeAccessor {
@Invoker("<init>")
static ArrayVoxelShape newArrayVoxelShape(DiscreteVoxelShape pShape, DoubleList pXs, DoubleList pYs, DoubleList pZs) {
throw new AssertionError();
}
}

View File

@ -0,0 +1,14 @@
package dev.micle.wildflowers_backport.mixin;
import net.minecraft.world.phys.shapes.CubeVoxelShape;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(CubeVoxelShape.class)
public interface CubeVoxelShapeAccessor {
@Invoker("<init>")
static CubeVoxelShape newCubeVoxelShape(DiscreteVoxelShape pShape) {
throw new AssertionError();
}
}

View File

@ -0,0 +1,12 @@
package dev.micle.wildflowers_backport.mixin;
import com.mojang.math.OctahedralGroup;
import com.mojang.math.SymmetricGroup3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(OctahedralGroup.class)
public interface OctahedralGroupAccessor {
@Accessor("permutation")
SymmetricGroup3 wildflowers_backport$getPermutation();
}

View File

@ -0,0 +1,18 @@
package dev.micle.wildflowers_backport.mixin;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(VoxelShape.class)
public interface VoxelShapeAccessor {
@Accessor("shape")
DiscreteVoxelShape wildflowers_backport$getShape();
@Invoker("getCoords")
DoubleList wildflowers_backport$getCoords(Direction.Axis pAxis);
}

View File

@ -0,0 +1,13 @@
package dev.micle.wildflowers_backport.util;
import net.minecraft.core.Direction;
public class DirectionAxisUtils {
public static boolean choose(Direction.Axis axis, boolean x, boolean y, boolean z) {
return switch (axis) {
case X -> x;
case Y -> y;
case Z -> z;
};
}
}

View File

@ -0,0 +1,37 @@
package dev.micle.wildflowers_backport.util;
import com.mojang.math.OctahedralGroup;
import dev.micle.wildflowers_backport.math.Quadrant;
import dev.micle.wildflowers_backport.mixin.OctahedralGroupAccessor;
import net.minecraft.Util;
import net.minecraft.core.Direction;
public class OctahedralGroupUtils {
private static final Direction.Axis[] AXES = Direction.Axis.values();
private static final OctahedralGroup[][] XY_TABLE = Util.make(new OctahedralGroup[Quadrant.values().length][Quadrant.values().length], x -> {
for (Quadrant quadrant : Quadrant.values()) {
for (Quadrant quadrant1 : Quadrant.values()) {
OctahedralGroup octahedralGroup = OctahedralGroup.IDENTITY;
for (int i = 0; i < quadrant1.shift; i++) {
octahedralGroup = octahedralGroup.compose(OctahedralGroup.ROT_90_Y_NEG);
}
for (int j = 0; j < quadrant.shift; j++) {
octahedralGroup = octahedralGroup.compose(OctahedralGroup.ROT_90_X_NEG);
}
x[quadrant.ordinal()][quadrant1.ordinal()] = octahedralGroup;
}
}
});
public static Direction.Axis permute(OctahedralGroup octahedralGroup, Direction.Axis axis) {
return AXES[((OctahedralGroupAccessor) (Object) octahedralGroup).wildflowers_backport$getPermutation().permutation(axis.ordinal())];
}
public static OctahedralGroup fromXYAngles(Quadrant quadrant, Quadrant quadrant1) {
return XY_TABLE[quadrant.ordinal()][quadrant1.ordinal()];
}
}

View File

@ -0,0 +1,93 @@
package dev.micle.wildflowers_backport.util;
import com.google.common.collect.Maps;
import com.mojang.math.OctahedralGroup;
import dev.micle.wildflowers_backport.math.Quadrant;
import dev.micle.wildflowers_backport.mixin.ArrayVoxelShapeAccessor;
import dev.micle.wildflowers_backport.mixin.CubeVoxelShapeAccessor;
import dev.micle.wildflowers_backport.mixin.VoxelShapeAccessor;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CubeVoxelShape;
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.Map;
public class ShapesUtils {
private static final Vec3 BLOCK_CENTER = new Vec3(0.5, 0.5, 0.5);
public static VoxelShape rotate(VoxelShape voxelShape, OctahedralGroup octahedralGroup) {
return rotate(voxelShape, octahedralGroup, BLOCK_CENTER);
}
public static VoxelShape rotate(VoxelShape voxelShape, OctahedralGroup octahedralGroup, Vec3 vec3) {
if (octahedralGroup == OctahedralGroup.IDENTITY) {
return voxelShape;
} else {
DiscreteVoxelShape discreteVoxelShape = ((VoxelShapeAccessor) voxelShape).wildflowers_backport$getShape();
if (voxelShape instanceof CubeVoxelShape && BLOCK_CENTER.equals(vec3)) {
return CubeVoxelShapeAccessor.newCubeVoxelShape(discreteVoxelShape);
} else {
Direction.Axis direction$axis = OctahedralGroupUtils.permute(octahedralGroup, Direction.Axis.X);
Direction.Axis direction$axis1 = OctahedralGroupUtils.permute(octahedralGroup, Direction.Axis.Y);
Direction.Axis direction$axis2 = OctahedralGroupUtils.permute(octahedralGroup, Direction.Axis.Z);
DoubleList doubleList = ((VoxelShapeAccessor) voxelShape).wildflowers_backport$getCoords(direction$axis);
DoubleList doubleList1 = ((VoxelShapeAccessor) voxelShape).wildflowers_backport$getCoords(direction$axis1);
DoubleList doubleList2 = ((VoxelShapeAccessor) voxelShape).wildflowers_backport$getCoords(direction$axis2);
boolean flag = octahedralGroup.inverts(direction$axis);
boolean flag1 = octahedralGroup.inverts(direction$axis1);
boolean flag2 = octahedralGroup.inverts(direction$axis2);
boolean flag3 = DirectionAxisUtils.choose(direction$axis, flag, flag1, flag2);
boolean flag4 = DirectionAxisUtils.choose(direction$axis1, flag, flag1, flag2);
boolean flag5 = DirectionAxisUtils.choose(direction$axis2, flag, flag1, flag2);
return ArrayVoxelShapeAccessor.newArrayVoxelShape(
discreteVoxelShape,
makeAxis(doubleList, flag3, vec3.get(direction$axis), vec3.x),
makeAxis(doubleList1, flag4, vec3.get(direction$axis1), vec3.y),
makeAxis(doubleList2, flag5, vec3.get(direction$axis2), vec3.z)
);
}
}
}
public static Map<Direction, VoxelShape> rotateHorizontal(VoxelShape voxelShape) {
return rotateHorizontal(voxelShape, BLOCK_CENTER);
}
public static Map<Direction, VoxelShape> rotateHorizontal(VoxelShape voxelShape, Vec3 vec3) {
return Maps.newEnumMap(
Map.of(
Direction.NORTH,
voxelShape,
Direction.EAST,
rotate(voxelShape, OctahedralGroupUtils.fromXYAngles(Quadrant.R0, Quadrant.R90), vec3),
Direction.SOUTH,
rotate(voxelShape, OctahedralGroupUtils.fromXYAngles(Quadrant.R0, Quadrant.R180), vec3),
Direction.WEST,
rotate(voxelShape, OctahedralGroupUtils.fromXYAngles(Quadrant.R0, Quadrant.R270), vec3)
)
);
}
public static DoubleList makeAxis(DoubleList doubleList, boolean flag, double d1, double d2) {
if (!flag && d1 == d2) {
return doubleList;
} else {
int i = doubleList.size();
DoubleList doubleList1 = new DoubleArrayList(i);
int j = flag ? -1 : 1;
for (int k = flag ? i - 1 : 0; k >= 0 && k < i; k += j) {
doubleList1.add(d2 + j * (doubleList.getDouble(k) - d1));
}
return doubleList1;
}
}
}

View File

@ -0,0 +1,20 @@
package dev.micle.wildflowers_backport.util;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class VoxelShapeUtils {
public static VoxelShape singleEncompassing(VoxelShape voxelShape) {
return voxelShape.isEmpty()
? Shapes.empty()
: Shapes.box(
voxelShape.min(Direction.Axis.X),
voxelShape.min(Direction.Axis.Y),
voxelShape.min(Direction.Axis.Z),
voxelShape.min(Direction.Axis.X),
voxelShape.min(Direction.Axis.Y),
voxelShape.min(Direction.Axis.Z)
);
}
}

View File

@ -5,6 +5,10 @@
"compatibilityLevel": "JAVA_8",
"refmap": "firefly_bush_backport.refmap.json",
"mixins": [
"OctahedralGroupAccessor",
"VoxelShapeAccessor",
"ArrayVoxelShapeAccessor",
"CubeVoxelShapeAccessor"
],
"client": [
],